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
This commit is contained in:
Nicholas Nethercote 2012-04-30 17:59:23 -07:00
Родитель 82bc5b339c
Коммит a8ce50418e
21 изменённых файлов: 734 добавлений и 606 удалений

Просмотреть файл

@ -164,6 +164,7 @@ CPPSRCS = \
Parser.cpp \
SemanticAnalysis.cpp \
TokenStream.cpp \
TreeContext.cpp \
TestingFunctions.cpp \
LifoAlloc.cpp \
MapObject.cpp \

Просмотреть файл

@ -49,6 +49,8 @@
#include "jsinferinlines.h"
#include "frontend/TreeContext-inl.h"
using namespace js;
using namespace js::frontend;

Просмотреть файл

@ -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.

Просмотреть файл

@ -53,103 +53,12 @@
#include "frontend/Parser.h"
#include "frontend/ParseMaps.h"
#include "frontend/TreeContext.h"
#include "vm/ScopeObject.h"
namespace js {
typedef HashSet<JSAtom *> 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<StaticBlockObject *> 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<StaticBlockObject *> 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<BindingKind> 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.
*/

Просмотреть файл

@ -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;

Просмотреть файл

@ -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 {

Просмотреть файл

@ -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;
}

Просмотреть файл

@ -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"

Просмотреть файл

@ -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"

Просмотреть файл

@ -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<BindingKind> 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__

Просмотреть файл

@ -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;
}

Просмотреть файл

@ -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<JSAtom *> 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<StaticBlockObject *> 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<StaticBlockObject *> 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__

Просмотреть файл

@ -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"

Просмотреть файл

@ -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"

Просмотреть файл

@ -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"

Просмотреть файл

@ -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"

Просмотреть файл

@ -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"

Просмотреть файл

@ -76,6 +76,7 @@
#include "jsobjinlines.h"
#include "jsscriptinlines.h"
#include "frontend/TreeContext-inl.h"
#include "vm/RegExpObject-inl.h"
using namespace js;

Просмотреть файл

@ -59,7 +59,6 @@
#include "jsopcodeinlines.h"
#include "builtin/RegExp.h"
#include "frontend/BytecodeEmitter.h"
#include "vm/RegExpStatics.h"
#include "vm/RegExpObject.h"

Просмотреть файл

@ -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"

Просмотреть файл

@ -42,6 +42,7 @@
#ifndef ScopeObject_h___
#define ScopeObject_h___
#include "jscntxt.h"
#include "jsobj.h"
namespace js {