зеркало из https://github.com/mozilla/gecko-dev.git
Bug 819509 - Reparse functions if we discover they are strict. r=njn
--HG-- extra : rebase_source : ab04928f5922e6785f32dcd802b2474525981e99
This commit is contained in:
Родитель
772cb84b6b
Коммит
a9e1ae4390
|
@ -168,17 +168,7 @@ frontend::CompileScript(JSContext *cx, HandleObject scopeChain, StackFrame *call
|
|||
#endif
|
||||
|
||||
TokenStream &tokenStream = parser.tokenStream;
|
||||
{
|
||||
ParseNode *stringsAtStart = ListNode::create(PNK_STATEMENTLIST, &parser);
|
||||
if (!stringsAtStart)
|
||||
return NULL;
|
||||
stringsAtStart->makeEmpty();
|
||||
bool ok = parser.processDirectives(stringsAtStart) && EmitTree(cx, &bce, stringsAtStart);
|
||||
parser.freeTree(stringsAtStart);
|
||||
if (!ok)
|
||||
return NULL;
|
||||
}
|
||||
JS_ASSERT(globalsc.strictModeState != StrictMode::UNKNOWN);
|
||||
bool canHaveDirectives = true;
|
||||
for (;;) {
|
||||
TokenKind tt = tokenStream.peekToken(TSF_OPERAND);
|
||||
if (tt <= TOK_EOF) {
|
||||
|
@ -192,6 +182,11 @@ frontend::CompileScript(JSContext *cx, HandleObject scopeChain, StackFrame *call
|
|||
if (!pn)
|
||||
return NULL;
|
||||
|
||||
if (canHaveDirectives) {
|
||||
if (!parser.maybeParseDirective(pn, &canHaveDirectives))
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (!FoldConstants(cx, pn, &parser))
|
||||
return NULL;
|
||||
if (!NameFunctions(cx, pn))
|
||||
|
@ -279,15 +274,8 @@ frontend::CompileFunctionBody(JSContext *cx, HandleFunction fun, CompileOptions
|
|||
|
||||
JS_ASSERT(fun);
|
||||
|
||||
StrictMode sms = StrictModeFromContext(cx);
|
||||
FunctionBox *funbox = parser.newFunctionBox(fun, /* outerpc = */ NULL, sms);
|
||||
fun->setArgCount(formals.length());
|
||||
|
||||
unsigned staticLevel = 0;
|
||||
ParseContext funpc(&parser, funbox, staticLevel, /* bodyid = */ 0);
|
||||
if (!funpc.init())
|
||||
return false;
|
||||
|
||||
/* FIXME: make Function format the source for a function definition. */
|
||||
ParseNode *fn = FunctionNode::create(PNK_NAME, &parser);
|
||||
if (!fn)
|
||||
|
@ -303,36 +291,33 @@ frontend::CompileFunctionBody(JSContext *cx, HandleFunction fun, CompileOptions
|
|||
argsbody->makeEmpty();
|
||||
fn->pn_body = argsbody;
|
||||
|
||||
for (unsigned i = 0; i < formals.length(); i++) {
|
||||
if (!DefineArg(&parser, fn, formals[i]))
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
* After we're done parsing, we must fold constants, analyze any nested
|
||||
* functions, and generate code for this function, including a stop opcode
|
||||
* at the end.
|
||||
*/
|
||||
ParseNode *pn = parser.functionBody(Parser::StatementListBody);
|
||||
if (!pn)
|
||||
return false;
|
||||
|
||||
if (!parser.tokenStream.matchToken(TOK_EOF)) {
|
||||
parser.reportError(NULL, JSMSG_SYNTAX_ERROR);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!FoldConstants(cx, pn, &parser))
|
||||
return false;
|
||||
|
||||
Rooted<JSScript*> script(cx, JSScript::Create(cx, NullPtr(), false, options,
|
||||
staticLevel, ss, 0, length));
|
||||
/* staticLevel = */ 0, ss,
|
||||
/* sourceStart = */ 0, length));
|
||||
if (!script)
|
||||
return false;
|
||||
|
||||
InternalHandle<Bindings*> bindings(script, &script->bindings);
|
||||
if (!funpc.generateFunctionBindings(cx, bindings))
|
||||
return false;
|
||||
// If the context is strict, immediately parse the body in strict
|
||||
// mode. Otherwise, we parse it normally. If we see a "use strict"
|
||||
// directive, we backup and reparse it as strict.
|
||||
TokenStream::Position start;
|
||||
parser.tokenStream.tell(&start);
|
||||
bool initiallyStrict = StrictModeFromContext(cx) == StrictMode::STRICT;
|
||||
bool becameStrict;
|
||||
FunctionBox *funbox;
|
||||
ParseNode *pn = parser.standaloneFunctionBody(fun, formals, script, fn, &funbox,
|
||||
initiallyStrict, &becameStrict);
|
||||
if (!pn) {
|
||||
if (initiallyStrict || !becameStrict || parser.tokenStream.hadError())
|
||||
return false;
|
||||
|
||||
// Reparse in strict mode.
|
||||
parser.tokenStream.seek(start);
|
||||
pn = parser.standaloneFunctionBody(fun, formals, script, fn, &funbox,
|
||||
/* strict = */ true);
|
||||
if (!pn)
|
||||
return false;
|
||||
}
|
||||
|
||||
BytecodeEmitter funbce(/* parent = */ NULL, &parser, funbox, script, /* callerFrame = */ NULL,
|
||||
/* hasGlobalScope = */ false, options.lineno);
|
||||
|
|
|
@ -4850,12 +4850,8 @@ EmitFunc(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn)
|
|||
SharedContext *outersc = bce->sc;
|
||||
FunctionBox *funbox = pn->pn_funbox;
|
||||
|
||||
// If funbox's strictModeState is still unknown (as can happen for
|
||||
// functions defined in defaults), inherit it from the parent.
|
||||
if (funbox->strictModeState == StrictMode::UNKNOWN) {
|
||||
JS_ASSERT(outersc->strictModeState != StrictMode::UNKNOWN);
|
||||
funbox->strictModeState = outersc->strictModeState;
|
||||
}
|
||||
JS_ASSERT(outersc->strictModeState != StrictMode::UNKNOWN);
|
||||
|
||||
if (outersc->isFunction && outersc->asFunbox()->mightAliasLocals())
|
||||
funbox->setMightAliasLocals(); // inherit mightAliasLocals from parent
|
||||
JS_ASSERT_IF(outersc->inStrictMode(), funbox->inStrictMode());
|
||||
|
|
|
@ -775,6 +775,48 @@ struct ParseNode {
|
|||
return !(isOp(JSOP_LAMBDA) || isOp(JSOP_DEFFUN));
|
||||
}
|
||||
|
||||
/*
|
||||
* True if this statement node could be a member of a Directive Prologue: an
|
||||
* expression statement consisting of a single string literal.
|
||||
*
|
||||
* This considers only the node and its children, not its context. After
|
||||
* parsing, check the node's pn_prologue flag to see if it is indeed part of
|
||||
* a directive prologue.
|
||||
*
|
||||
* Note that a Directive Prologue can contain statements that cannot
|
||||
* themselves be directives (string literals that include escape sequences
|
||||
* or escaped newlines, say). This member function returns true for such
|
||||
* nodes; we use it to determine the extent of the prologue.
|
||||
* isEscapeFreeStringLiteral, below, checks whether the node itself could be
|
||||
* a directive.
|
||||
*/
|
||||
bool isStringExprStatement() const {
|
||||
if (getKind() == PNK_SEMI) {
|
||||
JS_ASSERT(pn_arity == PN_UNARY);
|
||||
ParseNode *kid = pn_kid;
|
||||
return kid && kid->getKind() == PNK_STRING && !kid->pn_parens;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
* Return true if this node, known to be an unparenthesized string literal,
|
||||
* could be the string of a directive in a Directive Prologue. Directive
|
||||
* strings never contain escape sequences or line continuations.
|
||||
*/
|
||||
bool isEscapeFreeStringLiteral() const {
|
||||
JS_ASSERT(isKind(PNK_STRING) && !pn_parens);
|
||||
|
||||
/*
|
||||
* If the string's length in the source code is its length as a value,
|
||||
* accounting for the quotes, then it must not contain any escape
|
||||
* sequences or line continuations.
|
||||
*/
|
||||
JSString *str = pn_atom;
|
||||
return (pn_pos.begin.lineno == pn_pos.end.lineno &&
|
||||
pn_pos.begin.index + str->length() + 2 == pn_pos.end.index);
|
||||
}
|
||||
|
||||
inline bool test(unsigned flag) const;
|
||||
|
||||
bool isLet() const { return test(PND_LET); }
|
||||
|
|
|
@ -50,7 +50,8 @@ ParseContext::ParseContext(Parser *prs, SharedContext *sc, unsigned staticLevel,
|
|||
funHasReturnVoid(false),
|
||||
parsingForInit(false),
|
||||
parsingWith(prs->pc ? prs->pc->parsingWith : false), // inherit from parent context
|
||||
inDeclDestructuring(false)
|
||||
inDeclDestructuring(false),
|
||||
funBecameStrict(false)
|
||||
{
|
||||
prs->pc = this;
|
||||
}
|
||||
|
|
|
@ -706,6 +706,48 @@ CheckStrictBinding(JSContext *cx, Parser *parser, HandlePropertyName name, Parse
|
|||
return true;
|
||||
}
|
||||
|
||||
ParseNode *
|
||||
Parser::standaloneFunctionBody(HandleFunction fun, const AutoNameVector &formals, HandleScript script,
|
||||
ParseNode *fn, FunctionBox **funbox, bool strict, bool *becameStrict)
|
||||
{
|
||||
if (becameStrict)
|
||||
*becameStrict = false;
|
||||
|
||||
*funbox = newFunctionBox(fun, /* outerpc = */ NULL, strict ? StrictMode::STRICT : StrictMode::NOTSTRICT);
|
||||
if (!funbox)
|
||||
return NULL;
|
||||
|
||||
ParseContext funpc(this, *funbox, 0, /* staticLevel = */ 0);
|
||||
if (!funpc.init())
|
||||
return NULL;
|
||||
|
||||
for (unsigned i = 0; i < formals.length(); i++) {
|
||||
if (!DefineArg(this, fn, formals[i]))
|
||||
return NULL;
|
||||
}
|
||||
|
||||
ParseNode *pn = functionBody(StatementListBody);
|
||||
if (!pn) {
|
||||
if (becameStrict && pc->funBecameStrict)
|
||||
*becameStrict = true;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (!tokenStream.matchToken(TOK_EOF)) {
|
||||
reportError(NULL, JSMSG_SYNTAX_ERROR);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (!FoldConstants(context, pn, this))
|
||||
return NULL;
|
||||
|
||||
InternalHandle<Bindings*> bindings(script, &script->bindings);
|
||||
if (!funpc.generateFunctionBindings(context, bindings))
|
||||
return NULL;
|
||||
|
||||
return pn;
|
||||
}
|
||||
|
||||
ParseNode *
|
||||
Parser::functionBody(FunctionBodyType type)
|
||||
{
|
||||
|
@ -719,10 +761,6 @@ Parser::functionBody(FunctionBodyType type)
|
|||
JS_ASSERT(type == ExpressionBody);
|
||||
JS_ASSERT(JS_HAS_EXPR_CLOSURES);
|
||||
|
||||
// There are no directives to parse, so indicate we're done finding
|
||||
// strict mode directives.
|
||||
if (!setStrictMode(false))
|
||||
return NULL;
|
||||
pn = UnaryNode::create(PNK_RETURN, this);
|
||||
if (pn) {
|
||||
pn->pn_kid = assignExpr();
|
||||
|
@ -1475,7 +1513,8 @@ Parser::functionArguments(ParseNode **listp, ParseNode* funcpn, bool &hasRest)
|
|||
}
|
||||
|
||||
ParseNode *
|
||||
Parser::functionDef(HandlePropertyName funName, FunctionType type, FunctionSyntaxKind kind)
|
||||
Parser::functionDef(HandlePropertyName funName, const TokenStream::Position &start,
|
||||
FunctionType type, FunctionSyntaxKind kind)
|
||||
{
|
||||
JS_ASSERT_IF(kind == Statement, funName);
|
||||
|
||||
|
@ -1484,6 +1523,7 @@ Parser::functionDef(HandlePropertyName funName, FunctionType type, FunctionSynta
|
|||
if (!pn)
|
||||
return NULL;
|
||||
pn->pn_body = NULL;
|
||||
pn->pn_funbox = NULL;
|
||||
pn->pn_cookie.makeFree();
|
||||
pn->pn_dflags = 0;
|
||||
|
||||
|
@ -1589,31 +1629,55 @@ Parser::functionDef(HandlePropertyName funName, FunctionType type, FunctionSynta
|
|||
pn->setOp(JSOP_LAMBDA);
|
||||
}
|
||||
|
||||
ParseContext *outerpc = pc;
|
||||
|
||||
RootedFunction fun(context, newFunction(outerpc, funName, kind));
|
||||
RootedFunction fun(context, newFunction(pc, funName, kind));
|
||||
if (!fun)
|
||||
return NULL;
|
||||
|
||||
// Inherit strictness if neeeded.
|
||||
StrictMode sms = (outerpc->sc->strictModeState == StrictMode::STRICT) ?
|
||||
StrictMode::STRICT : StrictMode::UNKNOWN;
|
||||
// If the outer scope is strict, immediately parse the function in strict
|
||||
// mode. Otherwise, we parse it normally. If we see a "use strict"
|
||||
// directive, we backup and reparse it as strict.
|
||||
pn->pn_body = NULL;
|
||||
bool initiallyStrict = pc->sc->strictModeState == StrictMode::STRICT;
|
||||
bool becameStrict;
|
||||
if (!functionArgsAndBody(pn, fun, funName, type, kind, initiallyStrict, &becameStrict)) {
|
||||
if (initiallyStrict || !becameStrict || tokenStream.hadError())
|
||||
return NULL;
|
||||
|
||||
// Reparse the function in strict mode.
|
||||
tokenStream.seek(start);
|
||||
if (funName && tokenStream.getToken() == TOK_ERROR)
|
||||
return NULL;
|
||||
pn->pn_body = NULL;
|
||||
if (!functionArgsAndBody(pn, fun, funName, type, kind, true))
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return pn;
|
||||
}
|
||||
|
||||
bool
|
||||
Parser::functionArgsAndBody(ParseNode *pn, HandleFunction fun, HandlePropertyName funName, FunctionType type,
|
||||
FunctionSyntaxKind kind, bool strict, bool *becameStrict)
|
||||
{
|
||||
if (becameStrict)
|
||||
*becameStrict = false;
|
||||
ParseContext *outerpc = pc;
|
||||
|
||||
// Create box for fun->object early to protect against last-ditch GC.
|
||||
FunctionBox *funbox = newFunctionBox(fun, outerpc, sms);
|
||||
FunctionBox *funbox = newFunctionBox(fun, pc, strict ? StrictMode::STRICT : StrictMode::NOTSTRICT);
|
||||
if (!funbox)
|
||||
return NULL;
|
||||
return false;
|
||||
|
||||
/* Initialize early for possible flags mutation via destructuringExpr. */
|
||||
ParseContext funpc(this, funbox, outerpc->staticLevel + 1, outerpc->blockidGen);
|
||||
if (!funpc.init())
|
||||
return NULL;
|
||||
return false;
|
||||
|
||||
/* Now parse formal argument list and compute fun->nargs. */
|
||||
ParseNode *prelude = NULL;
|
||||
bool hasRest;
|
||||
if (!functionArguments(&prelude, pn, hasRest))
|
||||
return NULL;
|
||||
return false;
|
||||
|
||||
fun->setArgCount(funpc.numArgs());
|
||||
if (funbox->ndefaults)
|
||||
|
@ -1623,44 +1687,54 @@ Parser::functionDef(HandlePropertyName funName, FunctionType type, FunctionSynta
|
|||
|
||||
if (type == Getter && fun->nargs > 0) {
|
||||
reportError(NULL, JSMSG_ACCESSOR_WRONG_ARGS, "getter", "no", "s");
|
||||
return NULL;
|
||||
return false;
|
||||
}
|
||||
if (type == Setter && fun->nargs != 1) {
|
||||
reportError(NULL, JSMSG_ACCESSOR_WRONG_ARGS, "setter", "one", "");
|
||||
return NULL;
|
||||
return false;
|
||||
}
|
||||
|
||||
FunctionBodyType bodyType = StatementListBody;
|
||||
#if JS_HAS_EXPR_CLOSURES
|
||||
if (tokenStream.getToken(TSF_OPERAND) != TOK_LC) {
|
||||
tokenStream.ungetToken();
|
||||
fun->setIsExprClosure();
|
||||
bodyType = ExpressionBody;
|
||||
fun->setIsExprClosure();
|
||||
}
|
||||
#else
|
||||
MUST_MATCH_TOKEN(TOK_LC, JSMSG_CURLY_BEFORE_BODY);
|
||||
if (!tokenStream.matchToken(TOK_LC)) {
|
||||
reportError(NULL, JSMSG_CURLY_BEFORE_BODY);
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
|
||||
ParseNode *body = functionBody(bodyType);
|
||||
if (!body)
|
||||
return NULL;
|
||||
if (!body) {
|
||||
// Notify the caller if this function was discovered to be strict.
|
||||
if (becameStrict && pc->funBecameStrict)
|
||||
*becameStrict = true;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (funName && !CheckStrictBinding(context, this, funName, pn))
|
||||
return NULL;
|
||||
return false;
|
||||
|
||||
#if JS_HAS_EXPR_CLOSURES
|
||||
if (bodyType == StatementListBody) {
|
||||
#endif
|
||||
MUST_MATCH_TOKEN(TOK_RC, JSMSG_CURLY_AFTER_BODY);
|
||||
if (!tokenStream.matchToken(TOK_RC)) {
|
||||
reportError(NULL, JSMSG_CURLY_AFTER_BODY);
|
||||
return false;
|
||||
}
|
||||
funbox->bufEnd = tokenStream.offsetOfToken(tokenStream.currentToken()) + 1;
|
||||
#if JS_HAS_EXPR_CLOSURES
|
||||
} else {
|
||||
// We shouldn't call endOffset if the tokenizer got an error.
|
||||
if (tokenStream.hadError())
|
||||
return NULL;
|
||||
return false;
|
||||
funbox->bufEnd = tokenStream.endOffset(tokenStream.currentToken());
|
||||
if (kind == Statement && !MatchOrInsertSemicolon(context, &tokenStream))
|
||||
return NULL;
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
pn->pn_pos.end = tokenStream.currentToken().pos.end;
|
||||
|
@ -1687,7 +1761,7 @@ Parser::functionDef(HandlePropertyName funName, FunctionType type, FunctionSynta
|
|||
|
||||
block = ListNode::create(PNK_SEQ, this);
|
||||
if (!block)
|
||||
return NULL;
|
||||
return false;
|
||||
block->pn_pos = body->pn_pos;
|
||||
block->initList(body);
|
||||
|
||||
|
@ -1696,7 +1770,7 @@ Parser::functionDef(HandlePropertyName funName, FunctionType type, FunctionSynta
|
|||
|
||||
ParseNode *item = UnaryNode::create(PNK_SEMI, this);
|
||||
if (!item)
|
||||
return NULL;
|
||||
return false;
|
||||
|
||||
item->pn_pos.begin = item->pn_pos.end = body->pn_pos.begin;
|
||||
item->pn_kid = prelude;
|
||||
|
@ -1723,9 +1797,9 @@ Parser::functionDef(HandlePropertyName funName, FunctionType type, FunctionSynta
|
|||
pn->pn_blockid = outerpc->blockid();
|
||||
|
||||
if (!LeaveFunction(pn, this, funName, kind))
|
||||
return NULL;
|
||||
return false;
|
||||
|
||||
return pn;
|
||||
return true;
|
||||
}
|
||||
|
||||
ParseNode *
|
||||
|
@ -1741,12 +1815,15 @@ Parser::functionStmt()
|
|||
return NULL;
|
||||
}
|
||||
|
||||
TokenStream::Position start;
|
||||
tokenStream.positionAfterLastFunctionKeyword(start);
|
||||
|
||||
/* We forbid function statements in strict mode code. */
|
||||
if (!pc->atBodyLevel() && pc->sc->needStrictChecks() &&
|
||||
!reportStrictModeError(NULL, JSMSG_STRICT_FUNCTION_STATEMENT))
|
||||
return NULL;
|
||||
|
||||
return functionDef(name, Normal, Statement);
|
||||
return functionDef(name, start, Normal, Statement);
|
||||
}
|
||||
|
||||
ParseNode *
|
||||
|
@ -1754,78 +1831,13 @@ Parser::functionExpr()
|
|||
{
|
||||
RootedPropertyName name(context);
|
||||
JS_ASSERT(tokenStream.currentToken().type == TOK_FUNCTION);
|
||||
TokenStream::Position start;
|
||||
tokenStream.positionAfterLastFunctionKeyword(start);
|
||||
if (tokenStream.getToken(TSF_KEYWORD_IS_NAME) == TOK_NAME)
|
||||
name = tokenStream.currentToken().name();
|
||||
else
|
||||
tokenStream.ungetToken();
|
||||
return functionDef(name, Normal, Expression);
|
||||
}
|
||||
|
||||
/*
|
||||
* Indicate that the current scope can't switch to strict mode with a body-level
|
||||
* "use strict" directive anymore. Return false on error.
|
||||
*/
|
||||
bool
|
||||
Parser::setStrictMode(bool strictMode)
|
||||
{
|
||||
if (pc->sc->strictModeState != StrictMode::UNKNOWN) {
|
||||
// Strict mode was inherited.
|
||||
JS_ASSERT(pc->sc->strictModeState == StrictMode::STRICT);
|
||||
if (pc->sc->isFunction) {
|
||||
JS_ASSERT_IF(pc->parent, pc->parent->sc->strictModeState == StrictMode::STRICT);
|
||||
} else {
|
||||
JS_ASSERT_IF(pc->staticLevel == 0,
|
||||
StrictModeFromContext(context) == StrictMode::STRICT);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
if (strictMode) {
|
||||
if (pc->queuedStrictModeError) {
|
||||
// There was a strict mode error in this scope before we knew it was
|
||||
// strict. Throw it.
|
||||
JS_ASSERT(!(pc->queuedStrictModeError->report.flags & JSREPORT_WARNING));
|
||||
pc->queuedStrictModeError->throwError();
|
||||
return false;
|
||||
}
|
||||
pc->sc->strictModeState = StrictMode::STRICT;
|
||||
} else {
|
||||
if (!pc->parent || pc->parent->sc->strictModeState == StrictMode::NOTSTRICT) {
|
||||
// This scope lacks a strict directive, and its parent (if it has
|
||||
// one) definitely isn't strict, so it definitely won't be strict.
|
||||
pc->sc->strictModeState = StrictMode::NOTSTRICT;
|
||||
if (pc->queuedStrictModeError && context->hasStrictOption() &&
|
||||
pc->queuedStrictModeError->report.errorNumber != JSMSG_STRICT_CODE_WITH) {
|
||||
// Convert queued strict mode error to a warning.
|
||||
pc->queuedStrictModeError->report.flags |= JSREPORT_WARNING;
|
||||
pc->queuedStrictModeError->throwError();
|
||||
}
|
||||
} else {
|
||||
// This scope (which has a parent and so must be a function) lacks
|
||||
// a strict directive, but it's not yet clear if its parent is
|
||||
// strict. (This can only happen for functions in default
|
||||
// arguments.) Leave it in the UNKNOWN state for now.
|
||||
JS_ASSERT(pc->sc->isFunction);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
* Return true if this token, known to be an unparenthesized string literal,
|
||||
* could be the string of a directive in a Directive Prologue. Directive
|
||||
* strings never contain escape sequences or line continuations.
|
||||
*/
|
||||
static bool
|
||||
IsEscapeFreeStringLiteral(const Token &tok)
|
||||
{
|
||||
/*
|
||||
* If the string's length in the source code is its length as a value,
|
||||
* accounting for the quotes, then it must not contain any escape
|
||||
* sequences or line continuations.
|
||||
*/
|
||||
return (tok.pos.begin.lineno == tok.pos.end.lineno &&
|
||||
tok.pos.begin.index + tok.atom()->length() + 2 == tok.pos.end.index);
|
||||
return functionDef(name, start, Normal, Expression);
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -1848,52 +1860,49 @@ IsEscapeFreeStringLiteral(const Token &tok)
|
|||
* to the "use strict" statement, which is indeed a directive.
|
||||
*/
|
||||
bool
|
||||
Parser::processDirectives(ParseNode *stmts)
|
||||
Parser::maybeParseDirective(ParseNode *pn, bool *cont)
|
||||
{
|
||||
bool gotStrictMode = false;
|
||||
for (TokenKind tt = tokenStream.getToken(TSF_OPERAND); tt == TOK_STRING; tt = tokenStream.getToken(TSF_OPERAND)) {
|
||||
ParseNode *stringNode = atomNode(PNK_STRING, JSOP_STRING);
|
||||
if (!stringNode)
|
||||
return false;
|
||||
const Token directive = tokenStream.currentToken();
|
||||
bool isDirective = IsEscapeFreeStringLiteral(directive);
|
||||
JSAtom *atom = directive.atom();
|
||||
TokenKind next = tokenStream.peekTokenSameLine();
|
||||
*cont = pn->isStringExprStatement();
|
||||
if (!*cont)
|
||||
return true;
|
||||
|
||||
// We need to check whether the directive ends explicitly or implicitly
|
||||
// due to ASI. In the latter case, the expression must not continue on
|
||||
// the next line.
|
||||
if (next != TOK_EOF && next != TOK_SEMI && next != TOK_RC &&
|
||||
(next != TOK_EOL || TokenContinuesStringExpression(tokenStream.peekToken())))
|
||||
{
|
||||
freeTree(stringNode);
|
||||
if (next == TOK_ERROR)
|
||||
return false;
|
||||
break;
|
||||
}
|
||||
tokenStream.matchToken(TOK_SEMI);
|
||||
if (isDirective) {
|
||||
// It's a directive. Is it one we know?
|
||||
if (atom == context->names().useStrict && !gotStrictMode) {
|
||||
pc->sc->setExplicitUseStrict();
|
||||
if (!setStrictMode(true))
|
||||
ParseNode *string = pn->pn_kid;
|
||||
if (string->isEscapeFreeStringLiteral()) {
|
||||
// Mark this statement as being a possibly legitimate part of a
|
||||
// directive prologue, so the bytecode emitter won't warn about it being
|
||||
// useless code. (We mustn't just omit the statement entirely yet, as it
|
||||
// could be producing the value of an eval or JSScript execution.)
|
||||
//
|
||||
// Note that even if the string isn't one we recognize as a directive,
|
||||
// the emitter still shouldn't flag it as useless, as it could become a
|
||||
// directive in the future. We don't want to interfere with people
|
||||
// taking advantage of directive-prologue-enabled features that appear
|
||||
// in other browsers first.
|
||||
pn->pn_prologue = true;
|
||||
|
||||
JSAtom *directive = string->pn_atom;
|
||||
if (directive == context->runtime->atomState.useStrict) {
|
||||
// We're going to be in strict mode. Note that this scope explicitly
|
||||
// had "use strict";
|
||||
pc->sc->setExplicitUseStrict();
|
||||
if (pc->sc->strictModeState == StrictMode::NOTSTRICT) {
|
||||
if (pc->sc->isFunction) {
|
||||
// Request that this function be reparsed as strict.
|
||||
pc->funBecameStrict = true;
|
||||
return false;
|
||||
gotStrictMode = true;
|
||||
} else {
|
||||
// We don't reparse global scopes, so we keep track of the
|
||||
// one possible strict violation that could occur in the
|
||||
// directive prologue -- octal escapes -- and complain now.
|
||||
if (tokenStream.sawOctalEscape()) {
|
||||
reportError(NULL, JSMSG_DEPRECATED_OCTAL);
|
||||
return false;
|
||||
}
|
||||
pc->sc->strictModeState = StrictMode::STRICT;
|
||||
}
|
||||
}
|
||||
}
|
||||
ParseNode *stmt = UnaryNode::create(PNK_SEMI, this);
|
||||
if (!stmt) {
|
||||
freeTree(stringNode);
|
||||
return false;
|
||||
}
|
||||
stmt->pn_pos = stringNode->pn_pos;
|
||||
stmt->pn_kid = stringNode;
|
||||
stmt->pn_prologue = isDirective;
|
||||
stmts->append(stmt);
|
||||
}
|
||||
tokenStream.ungetToken();
|
||||
if (!gotStrictMode && !setStrictMode(false))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -1917,8 +1926,7 @@ Parser::statements(bool *hasFunctionStmt)
|
|||
ParseNode *saveBlock = pc->blockNode;
|
||||
pc->blockNode = pn;
|
||||
|
||||
if (pc->atBodyLevel() && !processDirectives(pn))
|
||||
return NULL;
|
||||
bool canHaveDirectives = pc->atBodyLevel();
|
||||
for (;;) {
|
||||
TokenKind tt = tokenStream.peekToken(TSF_OPERAND);
|
||||
if (tt <= TOK_EOF || tt == TOK_RC) {
|
||||
|
@ -1936,6 +1944,11 @@ Parser::statements(bool *hasFunctionStmt)
|
|||
return NULL;
|
||||
}
|
||||
|
||||
if (canHaveDirectives) {
|
||||
if (!maybeParseDirective(next, &canHaveDirectives))
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (next->isKind(PNK_FUNCTION)) {
|
||||
/*
|
||||
* PNX_FUNCDEFS notifies the emitter that the block contains body-
|
||||
|
@ -6711,7 +6724,10 @@ Parser::primaryExpr(TokenKind tt, bool afterDoubleDot)
|
|||
|
||||
/* NB: Getter function in { get x(){} } is unnamed. */
|
||||
Rooted<PropertyName*> funName(context, NULL);
|
||||
pn2 = functionDef(funName, op == JSOP_GETTER ? Getter : Setter, Expression);
|
||||
TokenStream::Position start;
|
||||
tokenStream.tell(&start);
|
||||
pn2 = functionDef(funName, start, op == JSOP_GETTER ? Getter : Setter,
|
||||
Expression);
|
||||
if (!pn2)
|
||||
return NULL;
|
||||
TokenPos pos = {begin, pn2->pn_pos.end};
|
||||
|
|
|
@ -199,6 +199,10 @@ struct ParseContext /* tree context for semantic checks */
|
|||
// they need to be treated differently.
|
||||
bool inDeclDestructuring:1;
|
||||
|
||||
// True if we are in a function, saw a "use strict" directive, and weren't
|
||||
// strict before.
|
||||
bool funBecameStrict:1;
|
||||
|
||||
inline ParseContext(Parser *prs, SharedContext *sc, unsigned staticLevel, uint32_t bodyid);
|
||||
inline ~ParseContext();
|
||||
|
||||
|
@ -350,7 +354,12 @@ struct Parser : private AutoGCRooter
|
|||
|
||||
/* Public entry points for parsing. */
|
||||
ParseNode *statement();
|
||||
bool processDirectives(ParseNode *stringsAtStart);
|
||||
bool maybeParseDirective(ParseNode *pn, bool *cont);
|
||||
|
||||
// Parse a function, given only its body. Used for the Function constructor.
|
||||
ParseNode *standaloneFunctionBody(HandleFunction fun, const AutoNameVector &formals, HandleScript script,
|
||||
ParseNode *fn, FunctionBox **funbox, bool strict,
|
||||
bool *becameStrict = NULL);
|
||||
|
||||
/*
|
||||
* Parse a function body. Pass StatementListBody if the body is a list of
|
||||
|
@ -424,7 +433,11 @@ struct Parser : private AutoGCRooter
|
|||
enum FunctionType { Getter, Setter, Normal };
|
||||
bool functionArguments(ParseNode **list, ParseNode *funcpn, bool &hasRest);
|
||||
|
||||
ParseNode *functionDef(HandlePropertyName name, FunctionType type, FunctionSyntaxKind kind);
|
||||
ParseNode *functionDef(HandlePropertyName name, const TokenStream::Position &start,
|
||||
FunctionType type, FunctionSyntaxKind kind);
|
||||
bool functionArgsAndBody(ParseNode *pn, HandleFunction fun, HandlePropertyName funName,
|
||||
FunctionType type, FunctionSyntaxKind kind, bool strict,
|
||||
bool *becameStrict = NULL);
|
||||
|
||||
ParseNode *unaryOpExpr(ParseNodeKind kind, JSOp op);
|
||||
|
||||
|
@ -473,7 +486,6 @@ struct Parser : private AutoGCRooter
|
|||
ParseNode *propertyQualifiedIdentifier();
|
||||
#endif /* JS_HAS_XML_SUPPORT */
|
||||
|
||||
bool setStrictMode(bool strictMode);
|
||||
bool setAssignmentLhsOps(ParseNode *pn, JSOp op);
|
||||
bool matchInOrOf(bool *isForOfp);
|
||||
};
|
||||
|
|
|
@ -33,7 +33,7 @@ SharedContext::inStrictMode()
|
|||
inline bool
|
||||
SharedContext::needStrictChecks()
|
||||
{
|
||||
return context->hasStrictOption() || strictModeState != StrictMode::NOTSTRICT;
|
||||
return context->hasStrictOption() || strictModeState == StrictMode::STRICT;
|
||||
}
|
||||
|
||||
inline GlobalSharedContext *
|
||||
|
|
|
@ -475,7 +475,7 @@ MOZ_END_ENUM_CLASS(StrictMode)
|
|||
inline StrictMode
|
||||
StrictModeFromContext(JSContext *cx)
|
||||
{
|
||||
return cx->hasRunOption(JSOPTION_STRICT_MODE) ? StrictMode::STRICT : StrictMode::UNKNOWN;
|
||||
return cx->hasRunOption(JSOPTION_STRICT_MODE) ? StrictMode::STRICT : StrictMode::NOTSTRICT;
|
||||
}
|
||||
|
||||
// Ideally, tokenizing would be entirely independent of context. But the
|
||||
|
|
Загрузка…
Ссылка в новой задаче