зеркало из https://github.com/mozilla/pjs.git
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:
Родитель
82bc5b339c
Коммит
a8ce50418e
|
@ -164,6 +164,7 @@ CPPSRCS = \
|
||||||
Parser.cpp \
|
Parser.cpp \
|
||||||
SemanticAnalysis.cpp \
|
SemanticAnalysis.cpp \
|
||||||
TokenStream.cpp \
|
TokenStream.cpp \
|
||||||
|
TreeContext.cpp \
|
||||||
TestingFunctions.cpp \
|
TestingFunctions.cpp \
|
||||||
LifoAlloc.cpp \
|
LifoAlloc.cpp \
|
||||||
MapObject.cpp \
|
MapObject.cpp \
|
||||||
|
|
|
@ -49,6 +49,8 @@
|
||||||
|
|
||||||
#include "jsinferinlines.h"
|
#include "jsinferinlines.h"
|
||||||
|
|
||||||
|
#include "frontend/TreeContext-inl.h"
|
||||||
|
|
||||||
using namespace js;
|
using namespace js;
|
||||||
using namespace js::frontend;
|
using namespace js::frontend;
|
||||||
|
|
||||||
|
|
|
@ -75,9 +75,9 @@
|
||||||
#include "jsscopeinlines.h"
|
#include "jsscopeinlines.h"
|
||||||
#include "jsscriptinlines.h"
|
#include "jsscriptinlines.h"
|
||||||
|
|
||||||
#include "frontend/BytecodeEmitter-inl.h"
|
|
||||||
#include "frontend/ParseMaps-inl.h"
|
#include "frontend/ParseMaps-inl.h"
|
||||||
#include "frontend/ParseNode-inl.h"
|
#include "frontend/ParseNode-inl.h"
|
||||||
|
#include "frontend/TreeContext-inl.h"
|
||||||
|
|
||||||
/* Allocation chunk counts, must be powers of two in general. */
|
/* Allocation chunk counts, must be powers of two in general. */
|
||||||
#define BYTECODE_CHUNK_LENGTH 1024 /* initial bytecode chunk length */
|
#define BYTECODE_CHUNK_LENGTH 1024 /* initial bytecode chunk length */
|
||||||
|
@ -98,12 +98,6 @@ NewTryNote(JSContext *cx, BytecodeEmitter *bce, JSTryNoteKind kind, unsigned sta
|
||||||
static JSBool
|
static JSBool
|
||||||
SetSrcNoteOffset(JSContext *cx, BytecodeEmitter *bce, unsigned index, unsigned which, ptrdiff_t offset);
|
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)
|
BytecodeEmitter::BytecodeEmitter(Parser *parser, unsigned lineno)
|
||||||
: TreeContext(parser),
|
: TreeContext(parser),
|
||||||
atomIndices(parser->context),
|
atomIndices(parser->context),
|
||||||
|
@ -363,65 +357,6 @@ ReportStatementTooLarge(JSContext *cx, BytecodeEmitter *bce)
|
||||||
StatementName(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,
|
* 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.
|
* 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;
|
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
|
JSBool
|
||||||
frontend::PopStatementBCE(JSContext *cx, BytecodeEmitter *bce)
|
frontend::PopStatementBCE(JSContext *cx, BytecodeEmitter *bce)
|
||||||
{
|
{
|
||||||
|
@ -751,35 +674,6 @@ frontend::DefineCompileTimeConstant(JSContext *cx, BytecodeEmitter *bce, JSAtom
|
||||||
return JS_TRUE;
|
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
|
* The function sets vp to NO_CONSTANT when the atom does not corresponds to a
|
||||||
* name defining a constant.
|
* name defining a constant.
|
||||||
|
|
|
@ -53,103 +53,12 @@
|
||||||
|
|
||||||
#include "frontend/Parser.h"
|
#include "frontend/Parser.h"
|
||||||
#include "frontend/ParseMaps.h"
|
#include "frontend/ParseMaps.h"
|
||||||
|
#include "frontend/TreeContext.h"
|
||||||
|
|
||||||
#include "vm/ScopeObject.h"
|
#include "vm/ScopeObject.h"
|
||||||
|
|
||||||
namespace js {
|
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
|
* To reuse space in StmtInfo, rename breaks and continues for use during
|
||||||
* try/catch/finally code generation and backpatching. To match most common
|
* try/catch/finally code generation and backpatching. To match most common
|
||||||
|
@ -162,351 +71,6 @@ struct StmtInfo {
|
||||||
#define GOSUBS(stmt) ((stmt).breaks)
|
#define GOSUBS(stmt) ((stmt).breaks)
|
||||||
#define GUARDJUMP(stmt) ((stmt).continues)
|
#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 {
|
struct TryNode {
|
||||||
JSTryNote note;
|
JSTryNote note;
|
||||||
TryNode *prev;
|
TryNode *prev;
|
||||||
|
@ -690,27 +254,6 @@ Emit3(JSContext *cx, BytecodeEmitter *bce, JSOp op, jsbytecode op1, jsbytecode o
|
||||||
ptrdiff_t
|
ptrdiff_t
|
||||||
EmitN(JSContext *cx, BytecodeEmitter *bce, JSOp op, size_t extra);
|
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
|
* Like PopStatementTC(bce), also patch breaks and continues unless the top
|
||||||
* statement info record represents a try-catch-finally suite. May fail if a
|
* statement info record represents a try-catch-finally suite. May fail if a
|
||||||
|
@ -734,23 +277,6 @@ PopStatementBCE(JSContext *cx, BytecodeEmitter *bce);
|
||||||
JSBool
|
JSBool
|
||||||
DefineCompileTimeConstant(JSContext *cx, BytecodeEmitter *bce, JSAtom *atom, ParseNode *pn);
|
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.
|
* Emit code into bce for the tree rooted at pn.
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -45,13 +45,15 @@
|
||||||
#include "jsxml.h"
|
#include "jsxml.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include "frontend/BytecodeEmitter.h"
|
|
||||||
#include "frontend/FoldConstants.h"
|
#include "frontend/FoldConstants.h"
|
||||||
#include "frontend/ParseNode.h"
|
#include "frontend/ParseNode.h"
|
||||||
|
#include "frontend/Parser.h"
|
||||||
|
#include "frontend/TreeContext.h"
|
||||||
#include "vm/NumericConversions.h"
|
#include "vm/NumericConversions.h"
|
||||||
|
|
||||||
#include "jsatominlines.h"
|
#include "jsatominlines.h"
|
||||||
|
|
||||||
|
#include "frontend/TreeContext-inl.h"
|
||||||
#include "vm/String-inl.h"
|
#include "vm/String-inl.h"
|
||||||
|
|
||||||
using namespace js;
|
using namespace js;
|
||||||
|
|
|
@ -41,8 +41,10 @@
|
||||||
#define ParseNode_inl_h__
|
#define ParseNode_inl_h__
|
||||||
|
|
||||||
#include "frontend/ParseNode.h"
|
#include "frontend/ParseNode.h"
|
||||||
#include "frontend/BytecodeEmitter.h"
|
|
||||||
#include "frontend/TokenStream.h"
|
#include "frontend/TokenStream.h"
|
||||||
|
#include "frontend/TreeContext.h"
|
||||||
|
|
||||||
|
#include "frontend/TreeContext-inl.h"
|
||||||
|
|
||||||
namespace js {
|
namespace js {
|
||||||
|
|
||||||
|
|
|
@ -74,9 +74,9 @@
|
||||||
#include "jsscript.h"
|
#include "jsscript.h"
|
||||||
#include "jsstr.h"
|
#include "jsstr.h"
|
||||||
|
|
||||||
#include "frontend/BytecodeEmitter.h"
|
|
||||||
#include "frontend/FoldConstants.h"
|
#include "frontend/FoldConstants.h"
|
||||||
#include "frontend/ParseMaps.h"
|
#include "frontend/ParseMaps.h"
|
||||||
|
#include "frontend/Parser.h"
|
||||||
#include "frontend/TokenStream.h"
|
#include "frontend/TokenStream.h"
|
||||||
#include "gc/Marking.h"
|
#include "gc/Marking.h"
|
||||||
|
|
||||||
|
@ -87,9 +87,9 @@
|
||||||
#include "jsatominlines.h"
|
#include "jsatominlines.h"
|
||||||
#include "jsscriptinlines.h"
|
#include "jsscriptinlines.h"
|
||||||
|
|
||||||
#include "frontend/BytecodeEmitter-inl.h"
|
|
||||||
#include "frontend/ParseMaps-inl.h"
|
#include "frontend/ParseMaps-inl.h"
|
||||||
#include "frontend/ParseNode-inl.h"
|
#include "frontend/ParseNode-inl.h"
|
||||||
|
#include "frontend/TreeContext-inl.h"
|
||||||
#include "vm/RegExpObject-inl.h"
|
#include "vm/RegExpObject-inl.h"
|
||||||
|
|
||||||
using namespace js;
|
using namespace js;
|
||||||
|
@ -7101,3 +7101,4 @@ Parser::parenExpr(JSBool *genexp)
|
||||||
|
|
||||||
return pn;
|
return pn;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -42,8 +42,8 @@
|
||||||
|
|
||||||
#include "jsfun.h"
|
#include "jsfun.h"
|
||||||
|
|
||||||
#include "frontend/BytecodeEmitter.h"
|
|
||||||
#include "frontend/Parser.h"
|
#include "frontend/Parser.h"
|
||||||
|
#include "frontend/TreeContext.h"
|
||||||
|
|
||||||
#include "jsobjinlines.h"
|
#include "jsobjinlines.h"
|
||||||
#include "jsfuninlines.h"
|
#include "jsfuninlines.h"
|
||||||
|
|
|
@ -64,9 +64,9 @@
|
||||||
#include "jsopcode.h"
|
#include "jsopcode.h"
|
||||||
#include "jsscript.h"
|
#include "jsscript.h"
|
||||||
|
|
||||||
#include "frontend/BytecodeEmitter.h"
|
|
||||||
#include "frontend/Parser.h"
|
#include "frontend/Parser.h"
|
||||||
#include "frontend/TokenStream.h"
|
#include "frontend/TokenStream.h"
|
||||||
|
#include "frontend/TreeContext.h"
|
||||||
#include "vm/RegExpObject.h"
|
#include "vm/RegExpObject.h"
|
||||||
|
|
||||||
#include "jsscriptinlines.h"
|
#include "jsscriptinlines.h"
|
||||||
|
|
|
@ -38,11 +38,11 @@
|
||||||
*
|
*
|
||||||
* ***** END LICENSE BLOCK ***** */
|
* ***** END LICENSE BLOCK ***** */
|
||||||
|
|
||||||
#ifndef BytecodeEmitter_inl_h__
|
#ifndef TreeContext_inl_h__
|
||||||
#define BytecodeEmitter_inl_h__
|
#define TreeContext_inl_h__
|
||||||
|
|
||||||
#include "frontend/ParseNode.h"
|
#include "frontend/Parser.h"
|
||||||
#include "frontend/TokenStream.h"
|
#include "frontend/TreeContext.h"
|
||||||
|
|
||||||
namespace js {
|
namespace js {
|
||||||
|
|
||||||
|
@ -59,11 +59,41 @@ TreeContext::TreeContext(Parser *prs)
|
||||||
prs->tc = this;
|
prs->tc = this;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
inline unsigned
|
||||||
* For functions the tree context is constructed and destructed a second
|
TreeContext::blockid()
|
||||||
* time during code generation. To avoid a redundant stats update in such
|
{
|
||||||
* cases, we store UINT16_MAX in maxScopeDepth.
|
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
|
inline
|
||||||
TreeContext::~TreeContext()
|
TreeContext::~TreeContext()
|
||||||
{
|
{
|
||||||
|
@ -71,6 +101,6 @@ TreeContext::~TreeContext()
|
||||||
parser->context->delete_(funcStmts);
|
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/MapObject.h"
|
||||||
#include "builtin/RegExp.h"
|
#include "builtin/RegExp.h"
|
||||||
#include "frontend/BytecodeCompiler.h"
|
#include "frontend/BytecodeCompiler.h"
|
||||||
#include "frontend/BytecodeEmitter.h"
|
#include "frontend/TreeContext.h"
|
||||||
#include "gc/Marking.h"
|
#include "gc/Marking.h"
|
||||||
#include "gc/Memory.h"
|
#include "gc/Memory.h"
|
||||||
#include "js/MemoryMetrics.h"
|
#include "js/MemoryMetrics.h"
|
||||||
|
|
|
@ -67,7 +67,6 @@
|
||||||
#include "jsstr.h"
|
#include "jsstr.h"
|
||||||
|
|
||||||
#include "frontend/BytecodeCompiler.h"
|
#include "frontend/BytecodeCompiler.h"
|
||||||
#include "frontend/BytecodeEmitter.h"
|
|
||||||
#include "frontend/TokenStream.h"
|
#include "frontend/TokenStream.h"
|
||||||
#include "gc/Marking.h"
|
#include "gc/Marking.h"
|
||||||
#include "vm/Debugger.h"
|
#include "vm/Debugger.h"
|
||||||
|
|
|
@ -73,7 +73,6 @@
|
||||||
#include "jslibmath.h"
|
#include "jslibmath.h"
|
||||||
|
|
||||||
#include "gc/Marking.h"
|
#include "gc/Marking.h"
|
||||||
#include "frontend/BytecodeEmitter.h"
|
|
||||||
#ifdef JS_METHODJIT
|
#ifdef JS_METHODJIT
|
||||||
#include "methodjit/MethodJIT.h"
|
#include "methodjit/MethodJIT.h"
|
||||||
#include "methodjit/Logging.h"
|
#include "methodjit/Logging.h"
|
||||||
|
|
|
@ -78,8 +78,8 @@
|
||||||
|
|
||||||
#include "builtin/MapObject.h"
|
#include "builtin/MapObject.h"
|
||||||
#include "frontend/BytecodeCompiler.h"
|
#include "frontend/BytecodeCompiler.h"
|
||||||
#include "frontend/BytecodeEmitter.h"
|
|
||||||
#include "frontend/Parser.h"
|
#include "frontend/Parser.h"
|
||||||
|
#include "frontend/TreeContext.h"
|
||||||
#include "gc/Marking.h"
|
#include "gc/Marking.h"
|
||||||
#include "js/MemoryMetrics.h"
|
#include "js/MemoryMetrics.h"
|
||||||
#include "vm/StringBuffer.h"
|
#include "vm/StringBuffer.h"
|
||||||
|
|
|
@ -57,9 +57,9 @@
|
||||||
#include "jsarray.h"
|
#include "jsarray.h"
|
||||||
#include "jsnum.h"
|
#include "jsnum.h"
|
||||||
|
|
||||||
#include "frontend/BytecodeEmitter.h"
|
|
||||||
#include "frontend/Parser.h"
|
#include "frontend/Parser.h"
|
||||||
#include "frontend/TokenStream.h"
|
#include "frontend/TokenStream.h"
|
||||||
|
#include "frontend/TreeContext.h"
|
||||||
#include "vm/RegExpObject.h"
|
#include "vm/RegExpObject.h"
|
||||||
|
|
||||||
#include "jsscriptinlines.h"
|
#include "jsscriptinlines.h"
|
||||||
|
|
|
@ -76,6 +76,7 @@
|
||||||
#include "jsobjinlines.h"
|
#include "jsobjinlines.h"
|
||||||
#include "jsscriptinlines.h"
|
#include "jsscriptinlines.h"
|
||||||
|
|
||||||
|
#include "frontend/TreeContext-inl.h"
|
||||||
#include "vm/RegExpObject-inl.h"
|
#include "vm/RegExpObject-inl.h"
|
||||||
|
|
||||||
using namespace js;
|
using namespace js;
|
||||||
|
|
|
@ -59,7 +59,6 @@
|
||||||
#include "jsopcodeinlines.h"
|
#include "jsopcodeinlines.h"
|
||||||
|
|
||||||
#include "builtin/RegExp.h"
|
#include "builtin/RegExp.h"
|
||||||
#include "frontend/BytecodeEmitter.h"
|
|
||||||
#include "vm/RegExpStatics.h"
|
#include "vm/RegExpStatics.h"
|
||||||
#include "vm/RegExpObject.h"
|
#include "vm/RegExpObject.h"
|
||||||
|
|
||||||
|
|
|
@ -43,7 +43,6 @@
|
||||||
#include "jslibmath.h"
|
#include "jslibmath.h"
|
||||||
#include "jsscope.h"
|
#include "jsscope.h"
|
||||||
|
|
||||||
#include "frontend/BytecodeEmitter.h"
|
|
||||||
#include "methodjit/MethodJIT.h"
|
#include "methodjit/MethodJIT.h"
|
||||||
#include "methodjit/Compiler.h"
|
#include "methodjit/Compiler.h"
|
||||||
#include "methodjit/StubCalls.h"
|
#include "methodjit/StubCalls.h"
|
||||||
|
|
|
@ -42,6 +42,7 @@
|
||||||
#ifndef ScopeObject_h___
|
#ifndef ScopeObject_h___
|
||||||
#define ScopeObject_h___
|
#define ScopeObject_h___
|
||||||
|
|
||||||
|
#include "jscntxt.h"
|
||||||
#include "jsobj.h"
|
#include "jsobj.h"
|
||||||
|
|
||||||
namespace js {
|
namespace js {
|
||||||
|
|
Загрузка…
Ссылка в новой задаче