From a8ce50418e3f5637e63b950c9f4647260162ca6e Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Mon, 30 Apr 2012 17:59:23 -0700 Subject: [PATCH] Bug 750580 - Remove cyclic dependency between Parser and BytecodeEmitter modules by introducing a TreeContext module. r=jorendorff. --HG-- rename : js/src/frontend/BytecodeEmitter-inl.h => js/src/frontend/TreeContext-inl.h extra : rebase_source : 5f4bc6d40424f6c1e44b3c7344559e19ca067a1b --- js/src/Makefile.in | 1 + js/src/frontend/BytecodeCompiler.cpp | 2 + js/src/frontend/BytecodeEmitter.cpp | 108 +--- js/src/frontend/BytecodeEmitter.h | 476 +--------------- js/src/frontend/FoldConstants.cpp | 4 +- js/src/frontend/ParseNode-inl.h | 4 +- js/src/frontend/Parser.cpp | 5 +- js/src/frontend/SemanticAnalysis.cpp | 2 +- js/src/frontend/TokenStream.cpp | 2 +- ...ytecodeEmitter-inl.h => TreeContext-inl.h} | 52 +- js/src/frontend/TreeContext.cpp | 159 ++++++ js/src/frontend/TreeContext.h | 513 ++++++++++++++++++ js/src/jsapi.cpp | 2 +- js/src/jsfun.cpp | 1 - js/src/jsinterp.cpp | 1 - js/src/jsobj.cpp | 2 +- js/src/jsreflect.cpp | 2 +- js/src/jsscript.cpp | 1 + js/src/methodjit/Compiler.cpp | 1 - js/src/methodjit/FastOps.cpp | 1 - js/src/vm/ScopeObject.h | 1 + 21 files changed, 734 insertions(+), 606 deletions(-) rename js/src/frontend/{BytecodeEmitter-inl.h => TreeContext-inl.h} (70%) create mode 100644 js/src/frontend/TreeContext.cpp create mode 100644 js/src/frontend/TreeContext.h diff --git a/js/src/Makefile.in b/js/src/Makefile.in index 20cc75e2796..967570d2c9a 100644 --- a/js/src/Makefile.in +++ b/js/src/Makefile.in @@ -164,6 +164,7 @@ CPPSRCS = \ Parser.cpp \ SemanticAnalysis.cpp \ TokenStream.cpp \ + TreeContext.cpp \ TestingFunctions.cpp \ LifoAlloc.cpp \ MapObject.cpp \ diff --git a/js/src/frontend/BytecodeCompiler.cpp b/js/src/frontend/BytecodeCompiler.cpp index 41c2c988647..2c6bad04876 100644 --- a/js/src/frontend/BytecodeCompiler.cpp +++ b/js/src/frontend/BytecodeCompiler.cpp @@ -49,6 +49,8 @@ #include "jsinferinlines.h" +#include "frontend/TreeContext-inl.h" + using namespace js; using namespace js::frontend; diff --git a/js/src/frontend/BytecodeEmitter.cpp b/js/src/frontend/BytecodeEmitter.cpp index 3c9b0b80151..5c8c0dc58bc 100644 --- a/js/src/frontend/BytecodeEmitter.cpp +++ b/js/src/frontend/BytecodeEmitter.cpp @@ -75,9 +75,9 @@ #include "jsscopeinlines.h" #include "jsscriptinlines.h" -#include "frontend/BytecodeEmitter-inl.h" #include "frontend/ParseMaps-inl.h" #include "frontend/ParseNode-inl.h" +#include "frontend/TreeContext-inl.h" /* Allocation chunk counts, must be powers of two in general. */ #define BYTECODE_CHUNK_LENGTH 1024 /* initial bytecode chunk length */ @@ -98,12 +98,6 @@ NewTryNote(JSContext *cx, BytecodeEmitter *bce, JSTryNoteKind kind, unsigned sta static JSBool SetSrcNoteOffset(JSContext *cx, BytecodeEmitter *bce, unsigned index, unsigned which, ptrdiff_t offset); -void -TreeContext::trace(JSTracer *trc) -{ - bindings.trace(trc); -} - BytecodeEmitter::BytecodeEmitter(Parser *parser, unsigned lineno) : TreeContext(parser), atomIndices(parser->context), @@ -363,65 +357,6 @@ ReportStatementTooLarge(JSContext *cx, BytecodeEmitter *bce) StatementName(bce)); } -bool -frontend::SetStaticLevel(TreeContext *tc, unsigned staticLevel) -{ - /* - * This is a lot simpler than error-checking every UpvarCookie::set, and - * practically speaking it leaves more than enough room for upvars. - */ - if (UpvarCookie::isLevelReserved(staticLevel)) { - JS_ReportErrorNumber(tc->parser->context, js_GetErrorMessage, NULL, - JSMSG_TOO_DEEP, js_function_str); - return false; - } - tc->staticLevel = staticLevel; - return true; -} - -bool -frontend::GenerateBlockId(TreeContext *tc, uint32_t &blockid) -{ - if (tc->blockidGen == JS_BIT(20)) { - JS_ReportErrorNumber(tc->parser->context, js_GetErrorMessage, NULL, - JSMSG_NEED_DIET, "program"); - return false; - } - blockid = tc->blockidGen++; - return true; -} - -void -frontend::PushStatement(TreeContext *tc, StmtInfo *stmt, StmtType type, ptrdiff_t top) -{ - stmt->type = type; - stmt->flags = 0; - stmt->blockid = tc->blockid(); - SET_STATEMENT_TOP(stmt, top); - stmt->label = NULL; - stmt->blockObj = NULL; - stmt->down = tc->topStmt; - tc->topStmt = stmt; - if (STMT_LINKS_SCOPE(stmt)) { - stmt->downScope = tc->topScopeStmt; - tc->topScopeStmt = stmt; - } else { - stmt->downScope = NULL; - } -} - -void -frontend::PushBlockScope(TreeContext *tc, StmtInfo *stmt, StaticBlockObject &blockObj, ptrdiff_t top) -{ - PushStatement(tc, stmt, STMT_BLOCK, top); - stmt->flags |= SIF_SCOPE; - blockObj.setEnclosingBlock(tc->blockChain); - stmt->downScope = tc->topScopeStmt; - tc->topScopeStmt = stmt; - tc->blockChain = &blockObj; - stmt->blockObj = &blockObj; -} - /* * Emit a backpatch op with offset pointing to the previous jump of this type, * so that we can walk back up the chain fixing up the op and jump offset. @@ -714,18 +649,6 @@ BackPatch(JSContext *cx, BytecodeEmitter *bce, ptrdiff_t last, jsbytecode *targe return JS_TRUE; } -void -frontend::PopStatementTC(TreeContext *tc) -{ - StmtInfo *stmt = tc->topStmt; - tc->topStmt = stmt->down; - if (STMT_LINKS_SCOPE(stmt)) { - tc->topScopeStmt = stmt->downScope; - if (stmt->flags & SIF_SCOPE) - tc->blockChain = stmt->blockObj->enclosingBlock(); - } -} - JSBool frontend::PopStatementBCE(JSContext *cx, BytecodeEmitter *bce) { @@ -751,35 +674,6 @@ frontend::DefineCompileTimeConstant(JSContext *cx, BytecodeEmitter *bce, JSAtom return JS_TRUE; } -StmtInfo * -frontend::LexicalLookup(TreeContext *tc, JSAtom *atom, int *slotp, StmtInfo *stmt) -{ - if (!stmt) - stmt = tc->topScopeStmt; - for (; stmt; stmt = stmt->downScope) { - if (stmt->type == STMT_WITH) - break; - - /* Skip "maybe scope" statements that don't contain let bindings. */ - if (!(stmt->flags & SIF_SCOPE)) - continue; - - StaticBlockObject &blockObj = *stmt->blockObj; - const Shape *shape = blockObj.nativeLookup(tc->parser->context, AtomToId(atom)); - if (shape) { - JS_ASSERT(shape->hasShortID()); - - if (slotp) - *slotp = blockObj.stackDepth() + shape->shortid(); - return stmt; - } - } - - if (slotp) - *slotp = -1; - return stmt; -} - /* * The function sets vp to NO_CONSTANT when the atom does not corresponds to a * name defining a constant. diff --git a/js/src/frontend/BytecodeEmitter.h b/js/src/frontend/BytecodeEmitter.h index 4b2ac38388d..c100d3347d3 100644 --- a/js/src/frontend/BytecodeEmitter.h +++ b/js/src/frontend/BytecodeEmitter.h @@ -53,103 +53,12 @@ #include "frontend/Parser.h" #include "frontend/ParseMaps.h" +#include "frontend/TreeContext.h" #include "vm/ScopeObject.h" namespace js { -typedef HashSet FuncStmtSet; - -/* - * NB: If you add enumerators for scope statements, add them between STMT_WITH - * and STMT_CATCH, or you will break the STMT_TYPE_IS_SCOPE macro. If you add - * non-looping statement enumerators, add them before STMT_DO_LOOP or you will - * break the STMT_TYPE_IS_LOOP macro. - * - * Also remember to keep the statementName array in BytecodeEmitter.cpp in - * sync. - */ -enum StmtType { - STMT_LABEL, /* labeled statement: L: s */ - STMT_IF, /* if (then) statement */ - STMT_ELSE, /* else clause of if statement */ - STMT_SEQ, /* synthetic sequence of statements */ - STMT_BLOCK, /* compound statement: { s1[;... sN] } */ - STMT_SWITCH, /* switch statement */ - STMT_WITH, /* with statement */ - STMT_CATCH, /* catch block */ - STMT_TRY, /* try block */ - STMT_FINALLY, /* finally block */ - STMT_SUBROUTINE, /* gosub-target subroutine body */ - STMT_DO_LOOP, /* do/while loop statement */ - STMT_FOR_LOOP, /* for loop statement */ - STMT_FOR_IN_LOOP, /* for/in loop statement */ - STMT_WHILE_LOOP, /* while loop statement */ - STMT_LIMIT -}; - -inline bool -STMT_TYPE_IN_RANGE(uint16_t type, StmtType begin, StmtType end) -{ - return begin <= type && type <= end; -} - -/* - * A comment on the encoding of the js::StmtType enum and type-testing macros: - * - * STMT_TYPE_MAYBE_SCOPE tells whether a statement type is always, or may - * become, a lexical scope. It therefore includes block and switch (the two - * low-numbered "maybe" scope types) and excludes with (with has dynamic scope - * pending the "reformed with" in ES4/JS2). It includes all try-catch-finally - * types, which are high-numbered maybe-scope types. - * - * STMT_TYPE_LINKS_SCOPE tells whether a js::StmtInfo of the given type eagerly - * links to other scoping statement info records. It excludes the two early - * "maybe" types, block and switch, as well as the try and both finally types, - * since try and the other trailing maybe-scope types don't need block scope - * unless they contain let declarations. - * - * We treat WITH as a static scope because it prevents lexical binding from - * continuing further up the static scope chain. With the lost "reformed with" - * proposal for ES4, we would be able to model it statically, too. - */ -#define STMT_TYPE_MAYBE_SCOPE(type) \ - (type != STMT_WITH && \ - STMT_TYPE_IN_RANGE(type, STMT_BLOCK, STMT_SUBROUTINE)) - -#define STMT_TYPE_LINKS_SCOPE(type) \ - STMT_TYPE_IN_RANGE(type, STMT_WITH, STMT_CATCH) - -#define STMT_TYPE_IS_TRYING(type) \ - STMT_TYPE_IN_RANGE(type, STMT_TRY, STMT_SUBROUTINE) - -#define STMT_TYPE_IS_LOOP(type) ((type) >= STMT_DO_LOOP) - -#define STMT_MAYBE_SCOPE(stmt) STMT_TYPE_MAYBE_SCOPE((stmt)->type) -#define STMT_LINKS_SCOPE(stmt) (STMT_TYPE_LINKS_SCOPE((stmt)->type) || \ - ((stmt)->flags & SIF_SCOPE)) -#define STMT_IS_TRYING(stmt) STMT_TYPE_IS_TRYING((stmt)->type) -#define STMT_IS_LOOP(stmt) STMT_TYPE_IS_LOOP((stmt)->type) - -struct StmtInfo { - uint16_t type; /* statement type */ - uint16_t flags; /* flags, see below */ - uint32_t blockid; /* for simplified dominance computation */ - ptrdiff_t update; /* loop update offset (top if none) */ - ptrdiff_t breaks; /* offset of last break in loop */ - ptrdiff_t continues; /* offset of last continue in loop */ - RootedVarAtom label; /* name of LABEL */ - RootedVar blockObj; /* block scope object */ - StmtInfo *down; /* info for enclosing statement */ - StmtInfo *downScope; /* next enclosing lexical scope */ - - StmtInfo(JSContext *cx) : label(cx), blockObj(cx) {} -}; - -#define SIF_SCOPE 0x0001 /* statement has its own lexical scope */ -#define SIF_BODY_BLOCK 0x0002 /* STMT_BLOCK type is a function body */ -#define SIF_FOR_BLOCK 0x0004 /* for (let ...) induced block scope */ - /* * To reuse space in StmtInfo, rename breaks and continues for use during * try/catch/finally code generation and backpatching. To match most common @@ -162,351 +71,6 @@ struct StmtInfo { #define GOSUBS(stmt) ((stmt).breaks) #define GUARDJUMP(stmt) ((stmt).continues) -#define SET_STATEMENT_TOP(stmt, top) \ - ((stmt)->update = (top), (stmt)->breaks = (stmt)->continues = (-1)) - -JS_ENUM_HEADER(TreeContextFlags, uint32_t) -{ - /* parsing inside function body */ - TCF_IN_FUNCTION = 0x1, - - /* function has 'return expr;' */ - TCF_RETURN_EXPR = 0x2, - - /* function has 'return;' */ - TCF_RETURN_VOID = 0x4, - - /* parsing init expr of for; exclude 'in' */ - TCF_IN_FOR_INIT = 0x8, - - /* function needs Call object per call */ - TCF_FUN_HEAVYWEIGHT = 0x10, - - /* parsed yield statement in function */ - TCF_FUN_IS_GENERATOR = 0x20, - - /* block contains a function statement */ - TCF_HAS_FUNCTION_STMT = 0x40, - - /* flag lambda from generator expression */ - TCF_GENEXP_LAMBDA = 0x80, - - /* script can optimize name references based on scope chain */ - TCF_COMPILE_N_GO = 0x100, - - /* API caller does not want result value from global script */ - TCF_NO_SCRIPT_RVAL = 0x200, - - /* - * Set when parsing a declaration-like destructuring pattern. This flag - * causes PrimaryExpr to create PN_NAME parse nodes for variable references - * which are not hooked into any definition's use chain, added to any tree - * context's AtomList, etc. etc. CheckDestructuring will do that work - * later. - * - * The comments atop CheckDestructuring explain the distinction between - * assignment-like and declaration-like destructuring patterns, and why - * they need to be treated differently. - */ - TCF_DECL_DESTRUCTURING = 0x400, - - /* - * This function/global/eval code body contained a Use Strict Directive. - * Treat certain strict warnings as errors, and forbid the use of 'with'. - * See also TSF_STRICT_MODE_CODE, JSScript::strictModeCode, and - * JSREPORT_STRICT_ERROR. - */ - TCF_STRICT_MODE_CODE = 0x800, - - /* - * The (static) bindings of this script need to support dynamic name - * read/write access. Here, 'dynamic' means dynamic dictionary lookup on - * the scope chain for a dynamic set of keys. The primary examples are: - * - direct eval - * - function:: - * - with - * since both effectively allow any name to be accessed. Non-exmaples are: - * - upvars of nested functions - * - function statement - * since the set of assigned name is known dynamically. 'with' could be in - * the non-example category, provided the set of all free variables within - * the with block was noted. However, we do not optimize 'with' so, for - * simplicity, 'with' is treated like eval. - * - * Note: access through the arguments object is not considered dynamic - * binding access since it does not go through the normal name lookup - * mechanism. This is debatable and could be changed (although care must be - * taken not to turn off the whole 'arguments' optimization). To answer the - * more general "is this argument aliased" question, script->needsArgsObj - * should be tested (see JSScript::argIsAlised). - */ - TCF_BINDINGS_ACCESSED_DYNAMICALLY = 0x1000, - - /* Compiling an eval() script. */ - TCF_COMPILE_FOR_EVAL = 0x2000, - - /* - * The function or a function that encloses it may define new local names - * at runtime through means other than calling eval. - */ - TCF_FUN_MIGHT_ALIAS_LOCALS = 0x4000, - - /* The script contains singleton initialiser JSOP_OBJECT. */ - TCF_HAS_SINGLETONS = 0x8000, - - /* Some enclosing scope is a with-statement or E4X filter-expression. */ - TCF_IN_WITH = 0x10000, - - /* - * This function does something that can extend the set of bindings in its - * call objects --- it does a direct eval in non-strict code, or includes a - * function statement (as opposed to a function definition). - * - * This flag is *not* inherited by enclosed or enclosing functions; it - * applies only to the function in whose flags it appears. - */ - TCF_FUN_EXTENSIBLE_SCOPE = 0x20000, - - /* The caller is JS_Compile*Script*. */ - TCF_NEED_SCRIPT_GLOBAL = 0x40000, - - /* - * Technically, every function has a binding named 'arguments'. Internally, - * this binding is only added when 'arguments' is mentioned by the function - * body. This flag indicates whether 'arguments' has been bound either - * through implicit use: - * function f() { return arguments } - * or explicit redeclaration: - * function f() { var arguments; return arguments } - * - * Note 1: overwritten arguments (function() { arguments = 3 }) will cause - * this flag to be set but otherwise require no special handling: - * 'arguments' is just a local variable and uses of 'arguments' will just - * read the local's current slot which may have been assigned. The only - * special semantics is that the initial value of 'arguments' is the - * arguments object (not undefined, like normal locals). - * - * Note 2: if 'arguments' is bound as a formal parameter, there will be an - * 'arguments' in Bindings, but, as the "LOCAL" in the name indicates, this - * flag will not be set. This is because, as a formal, 'arguments' will - * have no special semantics: the initial value is unconditionally the - * actual argument (or undefined if nactual < nformal). - */ - TCF_ARGUMENTS_HAS_LOCAL_BINDING = 0x80000, - - /* - * In many cases where 'arguments' has a local binding (as described above) - * we do not need to actually create an arguments object in the function - * prologue: instead we can analyze how 'arguments' is used (using the - * simple dataflow analysis in analyzeSSA) to determine that uses of - * 'arguments' can just read from the stack frame directly. However, the - * dataflow analysis only looks at how JSOP_ARGUMENTS is used, so it will - * be unsound in several cases. The frontend filters out such cases by - * setting this flag which eagerly sets script->needsArgsObj to true. - */ - TCF_DEFINITELY_NEEDS_ARGS_OBJ = 0x100000 - -} JS_ENUM_FOOTER(TreeContextFlags); - -/* Flags to check for return; vs. return expr; in a function. */ -static const uint32_t TCF_RETURN_FLAGS = TCF_RETURN_EXPR | TCF_RETURN_VOID; - -/* - * Sticky deoptimization flags to propagate from FunctionBody. - */ -static const uint32_t TCF_FUN_FLAGS = TCF_FUN_HEAVYWEIGHT | - TCF_FUN_IS_GENERATOR | - TCF_BINDINGS_ACCESSED_DYNAMICALLY | - TCF_FUN_MIGHT_ALIAS_LOCALS | - TCF_STRICT_MODE_CODE | - TCF_FUN_EXTENSIBLE_SCOPE | - TCF_ARGUMENTS_HAS_LOCAL_BINDING | - TCF_DEFINITELY_NEEDS_ARGS_OBJ; - -struct TreeContext { /* tree context for semantic checks */ - uint32_t flags; /* statement state flags, see above */ - uint32_t bodyid; /* block number of program/function body */ - uint32_t blockidGen; /* preincremented block number generator */ - uint32_t parenDepth; /* nesting depth of parens that might turn out - to be generator expressions */ - uint32_t yieldCount; /* number of |yield| tokens encountered at - non-zero depth in current paren tree */ - StmtInfo *topStmt; /* top of statement info stack */ - StmtInfo *topScopeStmt; /* top lexical scope statement */ - RootedVar blockChain; - /* compile time block scope chain (NB: one - deeper than the topScopeStmt/downScope - chain when in head of let block/expr) */ - ParseNode *blockNode; /* parse node for a block with let declarations - (block with its own lexical scope) */ - AtomDecls decls; /* function, const, and var declarations */ - Parser *parser; /* ptr to common parsing and lexing data */ - ParseNode *yieldNode; /* parse node for a yield expression that might - be an error if we turn out to be inside a - generator expression */ - ParseNode *argumentsNode; /* parse node for an arguments variable that - might be an error if we turn out to be - inside a generator expression */ - - private: - RootedVarFunction fun_; /* function to store argument and variable - names when flags & TCF_IN_FUNCTION */ - RootedVarObject scopeChain_; /* scope chain object for the script */ - - public: - JSFunction *fun() const { - JS_ASSERT(inFunction()); - return fun_; - } - void setFunction(JSFunction *fun) { - JS_ASSERT(inFunction()); - fun_ = fun; - } - JSObject *scopeChain() const { - JS_ASSERT(!inFunction()); - return scopeChain_; - } - void setScopeChain(JSObject *scopeChain) { - JS_ASSERT(!inFunction()); - scopeChain_ = scopeChain; - } - - OwnedAtomDefnMapPtr lexdeps; /* unresolved lexical name dependencies */ - - TreeContext *parent; /* Enclosing function or global context. */ - - unsigned staticLevel; /* static compilation unit nesting level */ - - FunctionBox *funbox; /* null or box for function we're compiling - if (flags & TCF_IN_FUNCTION) and not in - js::frontend::CompileFunctionBody */ - FunctionBox *functionList; - - ParseNode *innermostWith; /* innermost WITH parse node */ - - Bindings bindings; /* bindings in this code, including - arguments if we're compiling a function */ - Bindings::StackRoot bindingsRoot; /* root for stack allocated bindings. */ - - FuncStmtSet *funcStmts; /* Set of (non-top-level) function statements - that will alias any top-level bindings with - the same name. */ - - void trace(JSTracer *trc); - - inline TreeContext(Parser *prs); - inline ~TreeContext(); - - /* - * js::BytecodeEmitter derives from js::TreeContext; however, only the - * top-level BytecodeEmitters are actually used as full-fledged tree contexts - * (to hold decls and lexdeps). We can avoid allocation overhead by making - * this distinction explicit. - */ - enum InitBehavior { - USED_AS_TREE_CONTEXT, - USED_AS_CODE_GENERATOR - }; - - bool init(JSContext *cx, InitBehavior ib = USED_AS_TREE_CONTEXT) { - if (cx->hasRunOption(JSOPTION_STRICT_MODE)) - flags |= TCF_STRICT_MODE_CODE; - if (ib == USED_AS_CODE_GENERATOR) - return true; - return decls.init() && lexdeps.ensureMap(cx); - } - - unsigned blockid() { return topStmt ? topStmt->blockid : bodyid; } - - /* - * True if we are at the topmost level of a entire script or function body. - * For example, while parsing this code we would encounter f1 and f2 at - * body level, but we would not encounter f3 or f4 at body level: - * - * function f1() { function f2() { } } - * if (cond) { function f3() { if (cond) { function f4() { } } } } - */ - bool atBodyLevel() { return !topStmt || (topStmt->flags & SIF_BODY_BLOCK); } - - bool inStrictMode() const { - return flags & TCF_STRICT_MODE_CODE; - } - - inline bool needStrictChecks(); - - bool compileAndGo() const { return flags & TCF_COMPILE_N_GO; } - bool inFunction() const { return flags & TCF_IN_FUNCTION; } - - void noteBindingsAccessedDynamically() { - flags |= TCF_BINDINGS_ACCESSED_DYNAMICALLY; - } - - bool bindingsAccessedDynamically() const { - return flags & TCF_BINDINGS_ACCESSED_DYNAMICALLY; - } - - void noteMightAliasLocals() { - flags |= TCF_FUN_MIGHT_ALIAS_LOCALS; - } - - bool mightAliasLocals() const { - return flags & TCF_FUN_MIGHT_ALIAS_LOCALS; - } - - void noteArgumentsHasLocalBinding() { - flags |= TCF_ARGUMENTS_HAS_LOCAL_BINDING; - } - - bool argumentsHasLocalBinding() const { - return flags & TCF_ARGUMENTS_HAS_LOCAL_BINDING; - } - - unsigned argumentsLocalSlot() const { - PropertyName *arguments = parser->context->runtime->atomState.argumentsAtom; - unsigned slot; - DebugOnly kind = bindings.lookup(parser->context, arguments, &slot); - JS_ASSERT(kind == VARIABLE || kind == CONSTANT); - return slot; - } - - void noteDefinitelyNeedsArgsObj() { - JS_ASSERT(argumentsHasLocalBinding()); - flags |= TCF_DEFINITELY_NEEDS_ARGS_OBJ; - } - - bool definitelyNeedsArgsObj() const { - return flags & TCF_DEFINITELY_NEEDS_ARGS_OBJ; - } - - void noteHasExtensibleScope() { - flags |= TCF_FUN_EXTENSIBLE_SCOPE; - } - - bool hasExtensibleScope() const { - return flags & TCF_FUN_EXTENSIBLE_SCOPE; - } - - ParseNode *freeTree(ParseNode *pn) { return parser->freeTree(pn); } -}; - -/* - * Return true if we need to check for conditions that elicit - * JSOPTION_STRICT warnings or strict mode errors. - */ -inline bool TreeContext::needStrictChecks() { - return parser->context->hasStrictOption() || inStrictMode(); -} - -namespace frontend { - -bool -SetStaticLevel(TreeContext *tc, unsigned staticLevel); - -bool -GenerateBlockId(TreeContext *tc, uint32_t &blockid); - -} /* namespace frontend */ - struct TryNode { JSTryNote note; TryNode *prev; @@ -690,27 +254,6 @@ Emit3(JSContext *cx, BytecodeEmitter *bce, JSOp op, jsbytecode op1, jsbytecode o ptrdiff_t EmitN(JSContext *cx, BytecodeEmitter *bce, JSOp op, size_t extra); -/* - * Push the C-stack-allocated struct at stmt onto the stmtInfo stack. - */ -void -PushStatement(TreeContext *tc, StmtInfo *stmt, StmtType type, ptrdiff_t top); - -/* - * Push a block scope statement and link blockObj into tc->blockChain. To pop - * this statement info record, use PopStatementTC as usual, or if appropriate - * (if generating code), PopStatementBCE. - */ -void -PushBlockScope(TreeContext *tc, StmtInfo *stmt, StaticBlockObject &blockObj, ptrdiff_t top); - -/* - * Pop tc->topStmt. If the top StmtInfo struct is not stack-allocated, it - * is up to the caller to free it. - */ -void -PopStatementTC(TreeContext *tc); - /* * Like PopStatementTC(bce), also patch breaks and continues unless the top * statement info record represents a try-catch-finally suite. May fail if a @@ -734,23 +277,6 @@ PopStatementBCE(JSContext *cx, BytecodeEmitter *bce); JSBool DefineCompileTimeConstant(JSContext *cx, BytecodeEmitter *bce, JSAtom *atom, ParseNode *pn); -/* - * Find a lexically scoped variable (one declared by let, catch, or an array - * comprehension) named by atom, looking in tc's compile-time scopes. - * - * If a WITH statement is reached along the scope stack, return its statement - * info record, so callers can tell that atom is ambiguous. If slotp is not - * null, then if atom is found, set *slotp to its stack slot, otherwise to -1. - * This means that if slotp is not null, all the block objects on the lexical - * scope chain must have had their depth slots computed by the code generator, - * so the caller must be under EmitTree. - * - * In any event, directly return the statement info record in which atom was - * found. Otherwise return null. - */ -StmtInfo * -LexicalLookup(TreeContext *tc, JSAtom *atom, int *slotp, StmtInfo *stmt = NULL); - /* * Emit code into bce for the tree rooted at pn. */ diff --git a/js/src/frontend/FoldConstants.cpp b/js/src/frontend/FoldConstants.cpp index f397cbdbbf5..4c87d8f952e 100644 --- a/js/src/frontend/FoldConstants.cpp +++ b/js/src/frontend/FoldConstants.cpp @@ -45,13 +45,15 @@ #include "jsxml.h" #endif -#include "frontend/BytecodeEmitter.h" #include "frontend/FoldConstants.h" #include "frontend/ParseNode.h" +#include "frontend/Parser.h" +#include "frontend/TreeContext.h" #include "vm/NumericConversions.h" #include "jsatominlines.h" +#include "frontend/TreeContext-inl.h" #include "vm/String-inl.h" using namespace js; diff --git a/js/src/frontend/ParseNode-inl.h b/js/src/frontend/ParseNode-inl.h index 3ace3cd6a6c..ced797a1ce4 100644 --- a/js/src/frontend/ParseNode-inl.h +++ b/js/src/frontend/ParseNode-inl.h @@ -41,8 +41,10 @@ #define ParseNode_inl_h__ #include "frontend/ParseNode.h" -#include "frontend/BytecodeEmitter.h" #include "frontend/TokenStream.h" +#include "frontend/TreeContext.h" + +#include "frontend/TreeContext-inl.h" namespace js { diff --git a/js/src/frontend/Parser.cpp b/js/src/frontend/Parser.cpp index 03fab2e4986..2e3ed293751 100644 --- a/js/src/frontend/Parser.cpp +++ b/js/src/frontend/Parser.cpp @@ -74,9 +74,9 @@ #include "jsscript.h" #include "jsstr.h" -#include "frontend/BytecodeEmitter.h" #include "frontend/FoldConstants.h" #include "frontend/ParseMaps.h" +#include "frontend/Parser.h" #include "frontend/TokenStream.h" #include "gc/Marking.h" @@ -87,9 +87,9 @@ #include "jsatominlines.h" #include "jsscriptinlines.h" -#include "frontend/BytecodeEmitter-inl.h" #include "frontend/ParseMaps-inl.h" #include "frontend/ParseNode-inl.h" +#include "frontend/TreeContext-inl.h" #include "vm/RegExpObject-inl.h" using namespace js; @@ -7101,3 +7101,4 @@ Parser::parenExpr(JSBool *genexp) return pn; } + diff --git a/js/src/frontend/SemanticAnalysis.cpp b/js/src/frontend/SemanticAnalysis.cpp index f8de9a1242d..7b4e30d75a5 100644 --- a/js/src/frontend/SemanticAnalysis.cpp +++ b/js/src/frontend/SemanticAnalysis.cpp @@ -42,8 +42,8 @@ #include "jsfun.h" -#include "frontend/BytecodeEmitter.h" #include "frontend/Parser.h" +#include "frontend/TreeContext.h" #include "jsobjinlines.h" #include "jsfuninlines.h" diff --git a/js/src/frontend/TokenStream.cpp b/js/src/frontend/TokenStream.cpp index d689761dd5f..66343d6ece3 100644 --- a/js/src/frontend/TokenStream.cpp +++ b/js/src/frontend/TokenStream.cpp @@ -64,9 +64,9 @@ #include "jsopcode.h" #include "jsscript.h" -#include "frontend/BytecodeEmitter.h" #include "frontend/Parser.h" #include "frontend/TokenStream.h" +#include "frontend/TreeContext.h" #include "vm/RegExpObject.h" #include "jsscriptinlines.h" diff --git a/js/src/frontend/BytecodeEmitter-inl.h b/js/src/frontend/TreeContext-inl.h similarity index 70% rename from js/src/frontend/BytecodeEmitter-inl.h rename to js/src/frontend/TreeContext-inl.h index 9fafca052ac..f656be19de6 100644 --- a/js/src/frontend/BytecodeEmitter-inl.h +++ b/js/src/frontend/TreeContext-inl.h @@ -38,11 +38,11 @@ * * ***** END LICENSE BLOCK ***** */ -#ifndef BytecodeEmitter_inl_h__ -#define BytecodeEmitter_inl_h__ +#ifndef TreeContext_inl_h__ +#define TreeContext_inl_h__ -#include "frontend/ParseNode.h" -#include "frontend/TokenStream.h" +#include "frontend/Parser.h" +#include "frontend/TreeContext.h" namespace js { @@ -59,11 +59,41 @@ TreeContext::TreeContext(Parser *prs) prs->tc = this; } -/* - * For functions the tree context is constructed and destructed a second - * time during code generation. To avoid a redundant stats update in such - * cases, we store UINT16_MAX in maxScopeDepth. - */ +inline unsigned +TreeContext::blockid() +{ + return topStmt ? topStmt->blockid : bodyid; +} + +inline bool +TreeContext::atBodyLevel() +{ + return !topStmt || (topStmt->flags & SIF_BODY_BLOCK); +} + +inline bool +TreeContext::needStrictChecks() { + return parser->context->hasStrictOption() || inStrictMode(); +} + +inline unsigned +TreeContext::argumentsLocalSlot() const { + PropertyName *arguments = parser->context->runtime->atomState.argumentsAtom; + unsigned slot; + DebugOnly kind = bindings.lookup(parser->context, arguments, &slot); + JS_ASSERT(kind == VARIABLE || kind == CONSTANT); + return slot; +} + +inline ParseNode * +TreeContext::freeTree(ParseNode *pn) +{ + return parser->freeTree(pn); +} + +// For functions the tree context is constructed and destructed a second +// time during code generation. To avoid a redundant stats update in such +// cases, we store UINT16_MAX in maxScopeDepth. inline TreeContext::~TreeContext() { @@ -71,6 +101,6 @@ TreeContext::~TreeContext() parser->context->delete_(funcStmts); } -} /* namespace js */ +} // namespace js -#endif /* BytecodeEmitter_inl_h__ */ +#endif // TreeContext_inl_h__ diff --git a/js/src/frontend/TreeContext.cpp b/js/src/frontend/TreeContext.cpp new file mode 100644 index 00000000000..4870e27435f --- /dev/null +++ b/js/src/frontend/TreeContext.cpp @@ -0,0 +1,159 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * vim: set ts=8 sw=4 et tw=99: + * + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla Communicator client code, released + * March 31, 1998. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1998 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either of the GNU General Public License Version 2 or later (the "GPL"), + * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#include "frontend/ParseNode.h" +#include "frontend/Parser.h" +#include "frontend/TreeContext.h" + +#include "jsatominlines.h" + +#include "frontend/TreeContext-inl.h" +#include "vm/ScopeObject-inl.h" +#include "vm/String-inl.h" + +using namespace js; +using namespace js::frontend; + +void +TreeContext::trace(JSTracer *trc) +{ + bindings.trace(trc); +} + +bool +frontend::SetStaticLevel(TreeContext *tc, unsigned staticLevel) +{ + /* + * This is a lot simpler than error-checking every UpvarCookie::set, and + * practically speaking it leaves more than enough room for upvars. + */ + if (UpvarCookie::isLevelReserved(staticLevel)) { + JS_ReportErrorNumber(tc->parser->context, js_GetErrorMessage, NULL, + JSMSG_TOO_DEEP, js_function_str); + return false; + } + tc->staticLevel = staticLevel; + return true; +} + +bool +frontend::GenerateBlockId(TreeContext *tc, uint32_t &blockid) +{ + if (tc->blockidGen == JS_BIT(20)) { + JS_ReportErrorNumber(tc->parser->context, js_GetErrorMessage, NULL, + JSMSG_NEED_DIET, "program"); + return false; + } + blockid = tc->blockidGen++; + return true; +} + +void +frontend::PushStatement(TreeContext *tc, StmtInfo *stmt, StmtType type, ptrdiff_t top) +{ + stmt->type = type; + stmt->flags = 0; + stmt->blockid = tc->blockid(); + SET_STATEMENT_TOP(stmt, top); + stmt->label = NULL; + stmt->blockObj = NULL; + stmt->down = tc->topStmt; + tc->topStmt = stmt; + if (STMT_LINKS_SCOPE(stmt)) { + stmt->downScope = tc->topScopeStmt; + tc->topScopeStmt = stmt; + } else { + stmt->downScope = NULL; + } +} + +void +frontend::PushBlockScope(TreeContext *tc, StmtInfo *stmt, StaticBlockObject &blockObj, ptrdiff_t top) +{ + PushStatement(tc, stmt, STMT_BLOCK, top); + stmt->flags |= SIF_SCOPE; + blockObj.setEnclosingBlock(tc->blockChain); + stmt->downScope = tc->topScopeStmt; + tc->topScopeStmt = stmt; + tc->blockChain = &blockObj; + stmt->blockObj = &blockObj; +} + +void +frontend::PopStatementTC(TreeContext *tc) +{ + StmtInfo *stmt = tc->topStmt; + tc->topStmt = stmt->down; + if (STMT_LINKS_SCOPE(stmt)) { + tc->topScopeStmt = stmt->downScope; + if (stmt->flags & SIF_SCOPE) + tc->blockChain = stmt->blockObj->enclosingBlock(); + } +} + +StmtInfo * +frontend::LexicalLookup(TreeContext *tc, JSAtom *atom, int *slotp, StmtInfo *stmt) +{ + if (!stmt) + stmt = tc->topScopeStmt; + for (; stmt; stmt = stmt->downScope) { + if (stmt->type == STMT_WITH) + break; + + /* Skip "maybe scope" statements that don't contain let bindings. */ + if (!(stmt->flags & SIF_SCOPE)) + continue; + + StaticBlockObject &blockObj = *stmt->blockObj; + const Shape *shape = blockObj.nativeLookup(tc->parser->context, AtomToId(atom)); + if (shape) { + JS_ASSERT(shape->hasShortID()); + + if (slotp) + *slotp = blockObj.stackDepth() + shape->shortid(); + return stmt; + } + } + + if (slotp) + *slotp = -1; + return stmt; +} + diff --git a/js/src/frontend/TreeContext.h b/js/src/frontend/TreeContext.h new file mode 100644 index 00000000000..7d3144817e3 --- /dev/null +++ b/js/src/frontend/TreeContext.h @@ -0,0 +1,513 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * vim: set ts=8 sw=4 et tw=79: + * + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla Communicator client code, released + * March 31, 1998. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1998 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either of the GNU General Public License Version 2 or later (the "GPL"), + * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#ifndef TreeContext_h__ +#define TreeContext_h__ + +#include "jstypes.h" +#include "jsatom.h" +#include "jsopcode.h" +#include "jsscript.h" +#include "jsprvtd.h" +#include "jspubtd.h" + +#include "frontend/ParseMaps.h" + +#include "vm/ScopeObject.h" + +typedef struct BindData BindData; + +namespace js { + +JS_ENUM_HEADER(TreeContextFlags, uint32_t) +{ + // parsing inside function body + TCF_IN_FUNCTION = 0x1, + + // function has 'return expr;' + TCF_RETURN_EXPR = 0x2, + + // function has 'return;' + TCF_RETURN_VOID = 0x4, + + // parsing init expr of for; exclude 'in' + TCF_IN_FOR_INIT = 0x8, + + // function needs Call object per call + TCF_FUN_HEAVYWEIGHT = 0x10, + + // parsed yield statement in function + TCF_FUN_IS_GENERATOR = 0x20, + + // block contains a function statement + TCF_HAS_FUNCTION_STMT = 0x40, + + // flag lambda from generator expression + TCF_GENEXP_LAMBDA = 0x80, + + // script can optimize name references based on scope chain + TCF_COMPILE_N_GO = 0x100, + + // API caller does not want result value from global script + TCF_NO_SCRIPT_RVAL = 0x200, + + // Set when parsing a declaration-like destructuring pattern. This flag + // causes PrimaryExpr to create PN_NAME parse nodes for variable references + // which are not hooked into any definition's use chain, added to any tree + // context's AtomList, etc. etc. CheckDestructuring will do that work + // later. + // + // The comments atop CheckDestructuring explain the distinction between + // assignment-like and declaration-like destructuring patterns, and why + // they need to be treated differently. + // + TCF_DECL_DESTRUCTURING = 0x400, + + // This function/global/eval code body contained a Use Strict Directive. + // Treat certain strict warnings as errors, and forbid the use of 'with'. + // See also TSF_STRICT_MODE_CODE, JSScript::strictModeCode, and + // JSREPORT_STRICT_ERROR. + // + TCF_STRICT_MODE_CODE = 0x800, + + // The (static) bindings of this script need to support dynamic name + // read/write access. Here, 'dynamic' means dynamic dictionary lookup on + // the scope chain for a dynamic set of keys. The primary examples are: + // - direct eval + // - function:: + // - with + // since both effectively allow any name to be accessed. Non-exmaples are: + // - upvars of nested functions + // - function statement + // since the set of assigned name is known dynamically. 'with' could be in + // the non-example category, provided the set of all free variables within + // the with block was noted. However, we do not optimize 'with' so, for + // simplicity, 'with' is treated like eval. + // + // Note: access through the arguments object is not considered dynamic + // binding access since it does not go through the normal name lookup + // mechanism. This is debatable and could be changed (although care must be + // taken not to turn off the whole 'arguments' optimization). To answer the + // more general "is this argument aliased" question, script->needsArgsObj + // should be tested (see JSScript::argIsAlised). + TCF_BINDINGS_ACCESSED_DYNAMICALLY = 0x1000, + + // Compiling an eval() script. + TCF_COMPILE_FOR_EVAL = 0x2000, + + // The function or a function that encloses it may define new local names + // at runtime through means other than calling eval. + TCF_FUN_MIGHT_ALIAS_LOCALS = 0x4000, + + // The script contains singleton initialiser JSOP_OBJECT. + TCF_HAS_SINGLETONS = 0x8000, + + // Some enclosing scope is a with-statement or E4X filter-expression. + TCF_IN_WITH = 0x10000, + + // This function does something that can extend the set of bindings in its + // call objects --- it does a direct eval in non-strict code, or includes a + // function statement (as opposed to a function definition). + // + // This flag is *not* inherited by enclosed or enclosing functions; it + // applies only to the function in whose flags it appears. + // + TCF_FUN_EXTENSIBLE_SCOPE = 0x20000, + + // The caller is JS_Compile*Script*. + TCF_NEED_SCRIPT_GLOBAL = 0x40000, + + // Technically, every function has a binding named 'arguments'. Internally, + // this binding is only added when 'arguments' is mentioned by the function + // body. This flag indicates whether 'arguments' has been bound either + // through implicit use: + // function f() { return arguments } + // or explicit redeclaration: + // function f() { var arguments; return arguments } + // + // Note 1: overwritten arguments (function() { arguments = 3 }) will cause + // this flag to be set but otherwise require no special handling: + // 'arguments' is just a local variable and uses of 'arguments' will just + // read the local's current slot which may have been assigned. The only + // special semantics is that the initial value of 'arguments' is the + // arguments object (not undefined, like normal locals). + // + // Note 2: if 'arguments' is bound as a formal parameter, there will be an + // 'arguments' in Bindings, but, as the "LOCAL" in the name indicates, this + // flag will not be set. This is because, as a formal, 'arguments' will + // have no special semantics: the initial value is unconditionally the + // actual argument (or undefined if nactual < nformal). + // + TCF_ARGUMENTS_HAS_LOCAL_BINDING = 0x80000, + + // In many cases where 'arguments' has a local binding (as described above) + // we do not need to actually create an arguments object in the function + // prologue: instead we can analyze how 'arguments' is used (using the + // simple dataflow analysis in analyzeSSA) to determine that uses of + // 'arguments' can just read from the stack frame directly. However, the + // dataflow analysis only looks at how JSOP_ARGUMENTS is used, so it will + // be unsound in several cases. The frontend filters out such cases by + // setting this flag which eagerly sets script->needsArgsObj to true. + // + TCF_DEFINITELY_NEEDS_ARGS_OBJ = 0x100000 + +} JS_ENUM_FOOTER(TreeContextFlags); + +// Flags to check for return; vs. return expr; in a function. +static const uint32_t TCF_RETURN_FLAGS = TCF_RETURN_EXPR | TCF_RETURN_VOID; + +// Sticky deoptimization flags to propagate from FunctionBody. +static const uint32_t TCF_FUN_FLAGS = TCF_FUN_HEAVYWEIGHT | + TCF_FUN_IS_GENERATOR | + TCF_BINDINGS_ACCESSED_DYNAMICALLY | + TCF_FUN_MIGHT_ALIAS_LOCALS | + TCF_STRICT_MODE_CODE | + TCF_FUN_EXTENSIBLE_SCOPE | + TCF_ARGUMENTS_HAS_LOCAL_BINDING | + TCF_DEFINITELY_NEEDS_ARGS_OBJ; + +typedef HashSet FuncStmtSet; + +struct Parser; +struct StmtInfo; + +struct TreeContext { /* tree context for semantic checks */ + uint32_t flags; /* statement state flags, see above */ + uint32_t bodyid; /* block number of program/function body */ + uint32_t blockidGen; /* preincremented block number generator */ + uint32_t parenDepth; /* nesting depth of parens that might turn out + to be generator expressions */ + uint32_t yieldCount; /* number of |yield| tokens encountered at + non-zero depth in current paren tree */ + StmtInfo *topStmt; /* top of statement info stack */ + StmtInfo *topScopeStmt; /* top lexical scope statement */ + RootedVar blockChain; + /* compile time block scope chain (NB: one + deeper than the topScopeStmt/downScope + chain when in head of let block/expr) */ + ParseNode *blockNode; /* parse node for a block with let declarations + (block with its own lexical scope) */ + AtomDecls decls; /* function, const, and var declarations */ + Parser *parser; /* ptr to common parsing and lexing data */ + ParseNode *yieldNode; /* parse node for a yield expression that might + be an error if we turn out to be inside a + generator expression */ + ParseNode *argumentsNode; /* parse node for an arguments variable that + might be an error if we turn out to be + inside a generator expression */ + + private: + RootedVarFunction fun_; /* function to store argument and variable + names when flags & TCF_IN_FUNCTION */ + RootedVarObject scopeChain_; /* scope chain object for the script */ + + public: + JSFunction *fun() const { + JS_ASSERT(inFunction()); + return fun_; + } + void setFunction(JSFunction *fun) { + JS_ASSERT(inFunction()); + fun_ = fun; + } + JSObject *scopeChain() const { + JS_ASSERT(!inFunction()); + return scopeChain_; + } + void setScopeChain(JSObject *scopeChain) { + JS_ASSERT(!inFunction()); + scopeChain_ = scopeChain; + } + + OwnedAtomDefnMapPtr lexdeps; /* unresolved lexical name dependencies */ + + TreeContext *parent; /* Enclosing function or global context. */ + + unsigned staticLevel; /* static compilation unit nesting level */ + + FunctionBox *funbox; /* null or box for function we're compiling + if (flags & TCF_IN_FUNCTION) and not in + js::frontend::CompileFunctionBody */ + FunctionBox *functionList; + + ParseNode *innermostWith; /* innermost WITH parse node */ + + Bindings bindings; /* bindings in this code, including + arguments if we're compiling a function */ + Bindings::StackRoot bindingsRoot; /* root for stack allocated bindings. */ + + FuncStmtSet *funcStmts; /* Set of (non-top-level) function statements + that will alias any top-level bindings with + the same name. */ + + void trace(JSTracer *trc); + + inline TreeContext(Parser *prs); + inline ~TreeContext(); + + // js::BytecodeEmitter derives from js::TreeContext; however, only the + // top-level BytecodeEmitters are actually used as full-fledged tree contexts + // (to hold decls and lexdeps). We can avoid allocation overhead by making + // this distinction explicit. + enum InitBehavior { + USED_AS_TREE_CONTEXT, + USED_AS_CODE_GENERATOR + }; + + bool init(JSContext *cx, InitBehavior ib = USED_AS_TREE_CONTEXT) { + if (cx->hasRunOption(JSOPTION_STRICT_MODE)) + flags |= TCF_STRICT_MODE_CODE; + if (ib == USED_AS_CODE_GENERATOR) + return true; + return decls.init() && lexdeps.ensureMap(cx); + } + + unsigned blockid(); + + // True if we are at the topmost level of a entire script or function body. + // For example, while parsing this code we would encounter f1 and f2 at + // body level, but we would not encounter f3 or f4 at body level: + // + // function f1() { function f2() { } } + // if (cond) { function f3() { if (cond) { function f4() { } } } } + // + bool atBodyLevel(); + + bool inStrictMode() const { + return flags & TCF_STRICT_MODE_CODE; + } + + // Return true if we need to check for conditions that elicit + // JSOPTION_STRICT warnings or strict mode errors. + inline bool needStrictChecks(); + + bool compileAndGo() const { return flags & TCF_COMPILE_N_GO; } + bool inFunction() const { return flags & TCF_IN_FUNCTION; } + + void noteBindingsAccessedDynamically() { + flags |= TCF_BINDINGS_ACCESSED_DYNAMICALLY; + } + + bool bindingsAccessedDynamically() const { + return flags & TCF_BINDINGS_ACCESSED_DYNAMICALLY; + } + + void noteMightAliasLocals() { + flags |= TCF_FUN_MIGHT_ALIAS_LOCALS; + } + + bool mightAliasLocals() const { + return flags & TCF_FUN_MIGHT_ALIAS_LOCALS; + } + + void noteArgumentsHasLocalBinding() { + flags |= TCF_ARGUMENTS_HAS_LOCAL_BINDING; + } + + bool argumentsHasLocalBinding() const { + return flags & TCF_ARGUMENTS_HAS_LOCAL_BINDING; + } + + unsigned argumentsLocalSlot() const; + + void noteDefinitelyNeedsArgsObj() { + JS_ASSERT(argumentsHasLocalBinding()); + flags |= TCF_DEFINITELY_NEEDS_ARGS_OBJ; + } + + bool definitelyNeedsArgsObj() const { + return flags & TCF_DEFINITELY_NEEDS_ARGS_OBJ; + } + + void noteHasExtensibleScope() { + flags |= TCF_FUN_EXTENSIBLE_SCOPE; + } + + bool hasExtensibleScope() const { + return flags & TCF_FUN_EXTENSIBLE_SCOPE; + } + + ParseNode *freeTree(ParseNode *pn); +}; + +/* + * NB: If you add enumerators for scope statements, add them between STMT_WITH + * and STMT_CATCH, or you will break the STMT_TYPE_IS_SCOPE macro. If you add + * non-looping statement enumerators, add them before STMT_DO_LOOP or you will + * break the STMT_TYPE_IS_LOOP macro. + * + * Also remember to keep the statementName array in BytecodeEmitter.cpp in + * sync. + */ +enum StmtType { + STMT_LABEL, /* labeled statement: L: s */ + STMT_IF, /* if (then) statement */ + STMT_ELSE, /* else clause of if statement */ + STMT_SEQ, /* synthetic sequence of statements */ + STMT_BLOCK, /* compound statement: { s1[;... sN] } */ + STMT_SWITCH, /* switch statement */ + STMT_WITH, /* with statement */ + STMT_CATCH, /* catch block */ + STMT_TRY, /* try block */ + STMT_FINALLY, /* finally block */ + STMT_SUBROUTINE, /* gosub-target subroutine body */ + STMT_DO_LOOP, /* do/while loop statement */ + STMT_FOR_LOOP, /* for loop statement */ + STMT_FOR_IN_LOOP, /* for/in loop statement */ + STMT_WHILE_LOOP, /* while loop statement */ + STMT_LIMIT +}; + +inline bool +STMT_TYPE_IN_RANGE(uint16_t type, StmtType begin, StmtType end) +{ + return begin <= type && type <= end; +} + +/* + * A comment on the encoding of the js::StmtType enum and type-testing macros: + * + * STMT_TYPE_MAYBE_SCOPE tells whether a statement type is always, or may + * become, a lexical scope. It therefore includes block and switch (the two + * low-numbered "maybe" scope types) and excludes with (with has dynamic scope + * pending the "reformed with" in ES4/JS2). It includes all try-catch-finally + * types, which are high-numbered maybe-scope types. + * + * STMT_TYPE_LINKS_SCOPE tells whether a js::StmtInfo of the given type eagerly + * links to other scoping statement info records. It excludes the two early + * "maybe" types, block and switch, as well as the try and both finally types, + * since try and the other trailing maybe-scope types don't need block scope + * unless they contain let declarations. + * + * We treat WITH as a static scope because it prevents lexical binding from + * continuing further up the static scope chain. With the lost "reformed with" + * proposal for ES4, we would be able to model it statically, too. + */ +#define STMT_TYPE_MAYBE_SCOPE(type) \ + (type != STMT_WITH && \ + STMT_TYPE_IN_RANGE(type, STMT_BLOCK, STMT_SUBROUTINE)) + +#define STMT_TYPE_LINKS_SCOPE(type) \ + STMT_TYPE_IN_RANGE(type, STMT_WITH, STMT_CATCH) + +#define STMT_TYPE_IS_TRYING(type) \ + STMT_TYPE_IN_RANGE(type, STMT_TRY, STMT_SUBROUTINE) + +#define STMT_TYPE_IS_LOOP(type) ((type) >= STMT_DO_LOOP) + +#define STMT_MAYBE_SCOPE(stmt) STMT_TYPE_MAYBE_SCOPE((stmt)->type) +#define STMT_LINKS_SCOPE(stmt) (STMT_TYPE_LINKS_SCOPE((stmt)->type) || \ + ((stmt)->flags & SIF_SCOPE)) +#define STMT_IS_TRYING(stmt) STMT_TYPE_IS_TRYING((stmt)->type) +#define STMT_IS_LOOP(stmt) STMT_TYPE_IS_LOOP((stmt)->type) + +struct StmtInfo { + uint16_t type; /* statement type */ + uint16_t flags; /* flags, see below */ + uint32_t blockid; /* for simplified dominance computation */ + ptrdiff_t update; /* loop update offset (top if none) */ + ptrdiff_t breaks; /* offset of last break in loop */ + ptrdiff_t continues; /* offset of last continue in loop */ + RootedVarAtom label; /* name of LABEL */ + RootedVar blockObj; /* block scope object */ + StmtInfo *down; /* info for enclosing statement */ + StmtInfo *downScope; /* next enclosing lexical scope */ + + StmtInfo(JSContext *cx) : label(cx), blockObj(cx) {} +}; + +#define SIF_SCOPE 0x0001 /* statement has its own lexical scope */ +#define SIF_BODY_BLOCK 0x0002 /* STMT_BLOCK type is a function body */ +#define SIF_FOR_BLOCK 0x0004 /* for (let ...) induced block scope */ + +#define SET_STATEMENT_TOP(stmt, top) \ + ((stmt)->update = (top), (stmt)->breaks = (stmt)->continues = (-1)) + +namespace frontend { + +bool +SetStaticLevel(TreeContext *tc, unsigned staticLevel); + +bool +GenerateBlockId(TreeContext *tc, uint32_t &blockid); + +/* + * Push the C-stack-allocated struct at stmt onto the stmtInfo stack. + */ +void +PushStatement(TreeContext *tc, StmtInfo *stmt, StmtType type, ptrdiff_t top); + +/* + * Push a block scope statement and link blockObj into tc->blockChain. To pop + * this statement info record, use PopStatementTC as usual, or if appropriate + * (if generating code), PopStatementBCE. + */ +void +PushBlockScope(TreeContext *tc, StmtInfo *stmt, StaticBlockObject &blockObj, ptrdiff_t top); + +/* + * Pop tc->topStmt. If the top StmtInfo struct is not stack-allocated, it + * is up to the caller to free it. + */ +void +PopStatementTC(TreeContext *tc); + +/* + * Find a lexically scoped variable (one declared by let, catch, or an array + * comprehension) named by atom, looking in tc's compile-time scopes. + * + * If a WITH statement is reached along the scope stack, return its statement + * info record, so callers can tell that atom is ambiguous. If slotp is not + * null, then if atom is found, set *slotp to its stack slot, otherwise to -1. + * This means that if slotp is not null, all the block objects on the lexical + * scope chain must have had their depth slots computed by the code generator, + * so the caller must be under EmitTree. + * + * In any event, directly return the statement info record in which atom was + * found. Otherwise return null. + */ +StmtInfo * +LexicalLookup(TreeContext *tc, JSAtom *atom, int *slotp, StmtInfo *stmt = NULL); + +} // namespace frontend + +} // namespace js + +#endif // TreeContext_h__ diff --git a/js/src/jsapi.cpp b/js/src/jsapi.cpp index b58bc49e5a0..d6f0873d262 100644 --- a/js/src/jsapi.cpp +++ b/js/src/jsapi.cpp @@ -89,7 +89,7 @@ #include "builtin/MapObject.h" #include "builtin/RegExp.h" #include "frontend/BytecodeCompiler.h" -#include "frontend/BytecodeEmitter.h" +#include "frontend/TreeContext.h" #include "gc/Marking.h" #include "gc/Memory.h" #include "js/MemoryMetrics.h" diff --git a/js/src/jsfun.cpp b/js/src/jsfun.cpp index 15be60172c1..d45889a934e 100644 --- a/js/src/jsfun.cpp +++ b/js/src/jsfun.cpp @@ -67,7 +67,6 @@ #include "jsstr.h" #include "frontend/BytecodeCompiler.h" -#include "frontend/BytecodeEmitter.h" #include "frontend/TokenStream.h" #include "gc/Marking.h" #include "vm/Debugger.h" diff --git a/js/src/jsinterp.cpp b/js/src/jsinterp.cpp index 6fd02000a13..dd0e43bc408 100644 --- a/js/src/jsinterp.cpp +++ b/js/src/jsinterp.cpp @@ -73,7 +73,6 @@ #include "jslibmath.h" #include "gc/Marking.h" -#include "frontend/BytecodeEmitter.h" #ifdef JS_METHODJIT #include "methodjit/MethodJIT.h" #include "methodjit/Logging.h" diff --git a/js/src/jsobj.cpp b/js/src/jsobj.cpp index 510e5863a65..bc8af1b348c 100644 --- a/js/src/jsobj.cpp +++ b/js/src/jsobj.cpp @@ -78,8 +78,8 @@ #include "builtin/MapObject.h" #include "frontend/BytecodeCompiler.h" -#include "frontend/BytecodeEmitter.h" #include "frontend/Parser.h" +#include "frontend/TreeContext.h" #include "gc/Marking.h" #include "js/MemoryMetrics.h" #include "vm/StringBuffer.h" diff --git a/js/src/jsreflect.cpp b/js/src/jsreflect.cpp index 3e35d3ee4aa..67666f3819d 100644 --- a/js/src/jsreflect.cpp +++ b/js/src/jsreflect.cpp @@ -57,9 +57,9 @@ #include "jsarray.h" #include "jsnum.h" -#include "frontend/BytecodeEmitter.h" #include "frontend/Parser.h" #include "frontend/TokenStream.h" +#include "frontend/TreeContext.h" #include "vm/RegExpObject.h" #include "jsscriptinlines.h" diff --git a/js/src/jsscript.cpp b/js/src/jsscript.cpp index 411ba8cd24e..dbfad7ae0ba 100644 --- a/js/src/jsscript.cpp +++ b/js/src/jsscript.cpp @@ -76,6 +76,7 @@ #include "jsobjinlines.h" #include "jsscriptinlines.h" +#include "frontend/TreeContext-inl.h" #include "vm/RegExpObject-inl.h" using namespace js; diff --git a/js/src/methodjit/Compiler.cpp b/js/src/methodjit/Compiler.cpp index 6dabfd126e5..63457a7be50 100644 --- a/js/src/methodjit/Compiler.cpp +++ b/js/src/methodjit/Compiler.cpp @@ -59,7 +59,6 @@ #include "jsopcodeinlines.h" #include "builtin/RegExp.h" -#include "frontend/BytecodeEmitter.h" #include "vm/RegExpStatics.h" #include "vm/RegExpObject.h" diff --git a/js/src/methodjit/FastOps.cpp b/js/src/methodjit/FastOps.cpp index 3dde42e5529..ccce4715206 100644 --- a/js/src/methodjit/FastOps.cpp +++ b/js/src/methodjit/FastOps.cpp @@ -43,7 +43,6 @@ #include "jslibmath.h" #include "jsscope.h" -#include "frontend/BytecodeEmitter.h" #include "methodjit/MethodJIT.h" #include "methodjit/Compiler.h" #include "methodjit/StubCalls.h" diff --git a/js/src/vm/ScopeObject.h b/js/src/vm/ScopeObject.h index 383d7980a9e..37e0967d1ff 100644 --- a/js/src/vm/ScopeObject.h +++ b/js/src/vm/ScopeObject.h @@ -42,6 +42,7 @@ #ifndef ScopeObject_h___ #define ScopeObject_h___ +#include "jscntxt.h" #include "jsobj.h" namespace js {