Bug 678037 - Add (disabled) ability to parse script bytecode lazily, r=luke.

This commit is contained in:
Brian Hackett 2013-05-30 06:29:56 -06:00
parent d3b1307dba
commit ddbb9d9519
50 changed files with 1587 additions and 378 deletions

View File

@ -152,9 +152,11 @@ struct ZoneStats
gcHeapUnusedGcThings(0),
gcHeapStringsNormal(0),
gcHeapStringsShort(0),
gcHeapLazyScripts(0),
gcHeapTypeObjects(0),
gcHeapIonCodes(0),
stringCharsNonHuge(0),
lazyScripts(0),
typeObjects(0),
typePool(0),
hugeStrings()
@ -166,9 +168,11 @@ struct ZoneStats
gcHeapUnusedGcThings(other.gcHeapUnusedGcThings),
gcHeapStringsNormal(other.gcHeapStringsNormal),
gcHeapStringsShort(other.gcHeapStringsShort),
gcHeapLazyScripts(other.gcHeapLazyScripts),
gcHeapTypeObjects(other.gcHeapTypeObjects),
gcHeapIonCodes(other.gcHeapIonCodes),
stringCharsNonHuge(other.stringCharsNonHuge),
lazyScripts(other.lazyScripts),
typeObjects(other.typeObjects),
typePool(other.typePool),
hugeStrings()
@ -185,10 +189,12 @@ struct ZoneStats
ADD(gcHeapStringsNormal);
ADD(gcHeapStringsShort);
ADD(gcHeapLazyScripts);
ADD(gcHeapTypeObjects);
ADD(gcHeapIonCodes);
ADD(stringCharsNonHuge);
ADD(lazyScripts);
ADD(typeObjects);
ADD(typePool);
@ -206,10 +212,12 @@ struct ZoneStats
size_t gcHeapStringsNormal;
size_t gcHeapStringsShort;
size_t gcHeapLazyScripts;
size_t gcHeapTypeObjects;
size_t gcHeapIonCodes;
size_t stringCharsNonHuge;
size_t lazyScripts;
size_t typeObjects;
size_t typePool;

View File

@ -191,6 +191,30 @@ TryEvalJSON(JSContext *cx, JSScript *callerScript,
return EvalJSON_NotJSON;
}
static void
MarkFunctionsWithinEvalScript(JSScript *script)
{
// Mark top level functions in an eval script as being within an eval and,
// if applicable, inside a with statement.
if (!script->hasObjects())
return;
ObjectArray *objects = script->objects();
size_t start = script->innerObjectsStart();
for (size_t i = start; i < objects->length; i++) {
JSObject *obj = objects->vector[i];
if (obj->isFunction()) {
JSFunction *fun = obj->toFunction();
if (fun->hasScript())
fun->nonLazyScript()->directlyInsideEval = true;
else if (fun->isInterpretedLazy())
fun->lazyScript()->setDirectlyInsideEval();
}
}
}
// Define subset of ExecuteType so that casting performs the injection.
enum EvalType { DIRECT_EVAL = EXECUTE_DIRECT_EVAL, INDIRECT_EVAL = EXECUTE_INDIRECT_EVAL };
@ -295,6 +319,8 @@ EvalKernel(JSContext *cx, const CallArgs &args, EvalType evalType, AbstractFrame
if (!compiled)
return false;
MarkFunctionsWithinEvalScript(compiled);
esg.setNewScript(compiled);
}
@ -357,6 +383,8 @@ js::DirectEvalFromIon(JSContext *cx,
if (!compiled)
return false;
MarkFunctionsWithinEvalScript(compiled);
esg.setNewScript(compiled);
}

View File

@ -23,6 +23,7 @@
using namespace js;
using namespace js::frontend;
using mozilla::Maybe;
static bool
CheckLength(JSContext *cx, size_t length)
@ -84,6 +85,7 @@ frontend::CompileScript(JSContext *cx, HandleObject scopeChain,
SourceCompressionToken *extraSct /* = NULL */)
{
RootedString source(cx, source_);
SkipRoot skip(cx, &chars);
/*
* The scripted callerFrame can only be given for compile-and-go scripts
@ -119,15 +121,28 @@ frontend::CompileScript(JSContext *cx, HandleObject scopeChain,
break;
}
Parser<FullParseHandler> parser(cx, options, chars, length, /* foldConstants = */ true);
Maybe<Parser<SyntaxParseHandler> > syntaxParser;
if (options.canLazilyParse) {
syntaxParser.construct(cx, options, chars, length, /* foldConstants = */ false,
(Parser<SyntaxParseHandler> *) NULL,
(LazyScript *) NULL);
}
Parser<FullParseHandler> parser(cx, options, chars, length, /* foldConstants = */ true,
options.canLazilyParse ? &syntaxParser.ref() : NULL, NULL);
if (!parser.init())
return NULL;
parser.sct = sct;
GlobalSharedContext globalsc(cx, scopeChain, StrictModeFromContext(cx));
ParseContext<FullParseHandler> pc(&parser, NULL, &globalsc, staticLevel, /* bodyid = */ 0);
if (!pc.init())
// Syntax parsing may cause us to restart processing of top level
// statements in the script. Use Maybe<> so that the parse context can be
// reset when this occurs.
Maybe<ParseContext<FullParseHandler> > pc;
pc.construct(&parser, (GenericParseContext *) NULL, &globalsc, staticLevel, /* bodyid = */ 0);
if (!pc.ref().init())
return NULL;
bool savedCallerFun =
@ -150,8 +165,10 @@ frontend::CompileScript(JSContext *cx, HandleObject scopeChain,
JS_ASSERT_IF(globalScope, globalScope->isNative());
JS_ASSERT_IF(globalScope, JSCLASS_HAS_GLOBAL_FLAG_AND_SLOTS(globalScope->getClass()));
BytecodeEmitter::EmitterMode emitterMode =
options.selfHostingMode ? BytecodeEmitter::SelfHosting : BytecodeEmitter::Normal;
BytecodeEmitter bce(/* parent = */ NULL, &parser, &globalsc, script, options.forEval, evalCaller,
!!globalScope, options.lineno, options.selfHostingMode);
!!globalScope, options.lineno, emitterMode);
if (!bce.init())
return NULL;
@ -178,7 +195,7 @@ frontend::CompileScript(JSContext *cx, HandleObject scopeChain,
* wishes to decompile it while it's running.
*/
JSFunction *fun = evalCaller->functionOrCallerFunction();
ObjectBox *funbox = parser.newFunctionBox(fun, &pc, fun->strict());
ObjectBox *funbox = parser.newFunctionBox(fun, pc.addr(), fun->strict());
if (!funbox)
return NULL;
bce.objectList.add(funbox);
@ -195,9 +212,32 @@ frontend::CompileScript(JSContext *cx, HandleObject scopeChain,
return NULL;
}
TokenStream::Position pos(parser.keepAtoms);
parser.tokenStream.tell(&pos);
ParseNode *pn = parser.statement();
if (!pn)
return NULL;
if (!pn) {
if (parser.hadAbortedSyntaxParse()) {
// Parsing inner functions lazily may lead the parser into an
// unrecoverable state and may require starting over on the top
// level statement. Restart the parse; syntax parsing has
// already been disabled for the parser and the result will not
// be ambiguous.
parser.clearAbortedSyntaxParse();
parser.tokenStream.seek(pos);
pc.destroy();
pc.construct(&parser, (GenericParseContext *) NULL, &globalsc,
staticLevel, /* bodyid = */ 0);
if (!pc.ref().init())
return NULL;
JS_ASSERT(parser.pc == pc.addr());
pn = parser.statement();
}
if (!pn) {
JS_ASSERT(!parser.hadAbortedSyntaxParse());
return NULL;
}
}
if (canHaveDirectives) {
if (!parser.maybeParseDirective(pn, &canHaveDirectives))
@ -223,13 +263,13 @@ frontend::CompileScript(JSContext *cx, HandleObject scopeChain,
// free variables and as variables redeclared with 'var'.
RootedFunction fun(cx, evalCaller->functionOrCallerFunction());
HandlePropertyName arguments = cx->names().arguments;
for (AtomDefnRange r = pc.lexdeps->all(); !r.empty(); r.popFront()) {
for (AtomDefnRange r = pc.ref().lexdeps->all(); !r.empty(); r.popFront()) {
if (r.front().key() == arguments) {
if (!CheckArgumentsWithinEval(cx, parser, fun))
return NULL;
}
}
for (AtomDefnListMap::Range r = pc.decls().all(); !r.empty(); r.popFront()) {
for (AtomDefnListMap::Range r = pc.ref().decls().all(); !r.empty(); r.popFront()) {
if (r.front().key() == arguments) {
if (!CheckArgumentsWithinEval(cx, parser, fun))
return NULL;
@ -239,7 +279,7 @@ frontend::CompileScript(JSContext *cx, HandleObject scopeChain,
// If the eval'ed script contains any debugger statement, force construction
// of arguments objects for the caller script and any other scripts it is
// transitively nested inside.
if (pc.sc->hasDebuggerStatement()) {
if (pc.ref().sc->hasDebuggerStatement()) {
RootedObject scope(cx, scopeChain);
while (scope->isScope() || scope->isDebugScope()) {
if (scope->isCall() && !scope->asCall().isForEval()) {
@ -273,43 +313,55 @@ frontend::CompileScript(JSContext *cx, HandleObject scopeChain,
}
bool
frontend::ParseScript(JSContext *cx, HandleObject scopeChain,
const CompileOptions &options, StableCharPtr chars, size_t length)
frontend::CompileLazyFunction(JSContext *cx, HandleFunction fun, LazyScript *lazy,
const jschar *chars, size_t length)
{
if (!CheckLength(cx, length))
CompileOptions options(cx);
options.setPrincipals(cx->compartment->principals)
.setOriginPrincipals(lazy->parent()->originPrincipals)
.setVersion(lazy->parent()->getVersion())
.setFileAndLine(lazy->parent()->filename(), lazy->lineno())
.setColumn(lazy->column())
.setCompileAndGo(lazy->parent()->compileAndGo)
.setNoScriptRval(false)
.setSelfHostingMode(false);
Parser<FullParseHandler> parser(cx, options, chars, length,
/* foldConstants = */ true, NULL, lazy);
if (!parser.init())
return false;
Parser<SyntaxParseHandler> parser(cx, options, chars.get(), length, /* foldConstants = */ false);
if (!parser.init()) {
cx->clearPendingException();
RootedObject enclosingScope(cx, lazy->parent()->function());
ParseNode *pn = parser.standaloneLazyFunction(fun, lazy->parent()->staticLevel + 1,
lazy->strict());
if (!pn)
return false;
}
GlobalSharedContext globalsc(cx, scopeChain, StrictModeFromContext(cx));
ParseContext<SyntaxParseHandler> pc(&parser, NULL, &globalsc, 0, /* bodyid = */ 0);
if (!pc.init()) {
cx->clearPendingException();
JS::RootedScriptSource sourceObject(cx, ScriptSourceObject::create(cx, lazy->source()));
if (!sourceObject)
return false;
}
for (;;) {
TokenKind tt = parser.tokenStream.peekToken(TSF_OPERAND);
if (tt <= TOK_EOF) {
if (tt == TOK_EOF)
break;
JS_ASSERT(tt == TOK_ERROR);
cx->clearPendingException();
return false;
}
Rooted<JSScript*> script(cx, JSScript::Create(cx, enclosingScope, false,
options, lazy->parent()->staticLevel + 1,
sourceObject, lazy->begin(), lazy->end()));
if (!script)
return false;
if (!parser.statement()) {
cx->clearPendingException();
return false;
}
}
script->bindings = pn->pn_funbox->bindings;
return true;
if (lazy->directlyInsideEval())
script->directlyInsideEval = true;
bool hasGlobalScope = lazy->parent()->compileAndGo;
BytecodeEmitter bce(/* parent = */ NULL, &parser, pn->pn_funbox, script, options.forEval,
/* evalCaller = */ NullPtr(), hasGlobalScope,
options.lineno, BytecodeEmitter::LazyFunction);
if (!bce.init())
return false;
return EmitFunctionScript(cx, &bce, pn->pn_body);
}
// Compile a JS function body, which might appear as the value of an event
@ -319,6 +371,8 @@ frontend::CompileFunctionBody(JSContext *cx, MutableHandleFunction fun, CompileO
const AutoNameVector &formals, const jschar *chars, size_t length,
bool isAsmJSRecompile)
{
SkipRoot skip(cx, &chars);
if (!CheckLength(cx, length))
return false;
ScriptSource *ss = cx->new_<ScriptSource>();
@ -336,9 +390,17 @@ frontend::CompileFunctionBody(JSContext *cx, MutableHandleFunction fun, CompileO
return false;
}
Maybe<Parser<SyntaxParseHandler> > syntaxParser;
if (options.canLazilyParse) {
syntaxParser.construct(cx, options, chars, length, /* foldConstants = */ false,
(Parser<SyntaxParseHandler> *) NULL,
(LazyScript *) NULL);
}
JS_ASSERT(!options.forEval);
Parser<FullParseHandler> parser(cx, options, chars, length, /* foldConstants = */ true);
Parser<FullParseHandler> parser(cx, options, chars, length, /* foldConstants = */ true,
options.canLazilyParse ? &syntaxParser.ref() : NULL, NULL);
if (!parser.init())
return false;
parser.sct = &sct;
@ -374,21 +436,29 @@ frontend::CompileFunctionBody(JSContext *cx, MutableHandleFunction fun, CompileO
// directive, we backup and reparse it as strict.
TokenStream::Position start(parser.keepAtoms);
parser.tokenStream.tell(&start);
bool initiallyStrict = StrictModeFromContext(cx);
bool strict = StrictModeFromContext(cx);
bool becameStrict;
FunctionBox *funbox;
ParseNode *pn = parser.standaloneFunctionBody(fun, formals, script, fn, &funbox,
initiallyStrict, &becameStrict);
if (!pn) {
if (initiallyStrict || !becameStrict || parser.tokenStream.hadError())
return false;
// Reparse in strict mode.
parser.tokenStream.seek(start);
ParseNode *pn;
while (true) {
pn = parser.standaloneFunctionBody(fun, formals, script, fn, &funbox,
/* strict = */ true);
if (!pn)
return false;
strict, &becameStrict);
if (pn)
break;
if (parser.hadAbortedSyntaxParse()) {
// Hit some unrecoverable ambiguity during an inner syntax parse.
// Syntax parsing has now been disabled in the parser, so retry
// the parse.
parser.clearAbortedSyntaxParse();
} else {
// If the function became strict, reparse in strict mode.
if (strict || !becameStrict || parser.tokenStream.hadError())
return false;
strict = true;
}
parser.tokenStream.seek(start);
}
if (!NameFunctions(cx, pn))

View File

@ -19,8 +19,8 @@ CompileScript(JSContext *cx, HandleObject scopeChain, HandleScript evalCaller,
SourceCompressionToken *extraSct = NULL);
bool
ParseScript(JSContext *cx, HandleObject scopeChain,
const CompileOptions &options, StableCharPtr chars, size_t length);
CompileLazyFunction(JSContext *cx, HandleFunction fun, LazyScript *lazy,
const jschar *chars, size_t length);
bool
CompileFunctionBody(JSContext *cx, MutableHandleFunction fun, CompileOptions options,

View File

@ -92,7 +92,7 @@ struct frontend::StmtInfoBCE : public StmtInfoBase
BytecodeEmitter::BytecodeEmitter(BytecodeEmitter *parent,
Parser<FullParseHandler> *parser, SharedContext *sc,
HandleScript script, bool insideEval, HandleScript evalCaller,
bool hasGlobalScope, uint32_t lineNum, bool selfHostingMode)
bool hasGlobalScope, uint32_t lineNum, EmitterMode emitterMode)
: sc(sc),
parent(parent),
script(sc->context, script),
@ -117,7 +117,7 @@ BytecodeEmitter::BytecodeEmitter(BytecodeEmitter *parent,
emittingRunOnceLambda(false),
insideEval(insideEval),
hasGlobalScope(hasGlobalScope),
selfHostingMode(selfHostingMode)
emitterMode(emitterMode)
{
JS_ASSERT_IF(evalCaller, insideEval);
}
@ -828,23 +828,24 @@ ClonedBlockDepth(BytecodeEmitter *bce)
return clonedBlockDepth;
}
static uint16_t
AliasedNameToSlot(HandleScript script, PropertyName *name)
static bool
LookupAliasedName(HandleScript script, PropertyName *name, uint16_t *pslot)
{
/*
* Beware: BindingIter may contain more than one Binding for a given name
* (in the case of |function f(x,x) {}|) but only one will be aliased.
*/
unsigned slot = CallObject::RESERVED_SLOTS;
for (BindingIter bi(script); ; bi++) {
for (BindingIter bi(script); !bi.done(); bi++) {
if (bi->aliased()) {
if (bi->name() == name)
return slot;
if (bi->name() == name) {
*pslot = slot;
return true;
}
slot++;
}
}
return 0;
return false;
}
static bool
@ -876,13 +877,13 @@ EmitAliasedVarOp(JSContext *cx, JSOp op, ParseNode *pn, BytecodeEmitter *bce)
ScopeCoordinate sc;
if (IsArgOp(pn->getOp())) {
sc.hops = skippedScopes + ClonedBlockDepth(bceOfDef);
sc.slot = AliasedNameToSlot(bceOfDef->script, pn->name());
JS_ALWAYS_TRUE(LookupAliasedName(bceOfDef->script, pn->name(), &sc.slot));
} else {
JS_ASSERT(IsLocalOp(pn->getOp()) || pn->isKind(PNK_FUNCTION));
unsigned local = pn->pn_cookie.slot();
if (local < bceOfDef->script->bindings.numVars()) {
sc.hops = skippedScopes + ClonedBlockDepth(bceOfDef);
sc.slot = AliasedNameToSlot(bceOfDef->script, pn->name());
JS_ALWAYS_TRUE(LookupAliasedName(bceOfDef->script, pn->name(), &sc.slot));
} else {
unsigned depth = local - bceOfDef->script->bindings.numVars();
StaticBlockObject *b = bceOfDef->blockChain;
@ -903,9 +904,17 @@ static bool
EmitVarOp(JSContext *cx, ParseNode *pn, JSOp op, BytecodeEmitter *bce)
{
JS_ASSERT(pn->isKind(PNK_FUNCTION) || pn->isKind(PNK_NAME));
JS_ASSERT_IF(pn->isKind(PNK_NAME), IsArgOp(op) || IsLocalOp(op));
JS_ASSERT(!pn->pn_cookie.isFree());
if (IsAliasedVarOp(op)) {
ScopeCoordinate sc;
sc.hops = pn->pn_cookie.level();
sc.slot = pn->pn_cookie.slot();
return EmitAliasedVarOp(cx, op, sc, bce);
}
JS_ASSERT_IF(pn->isKind(PNK_NAME), IsArgOp(op) || IsLocalOp(op));
if (!bce->isAliasedName(pn)) {
JS_ASSERT(pn->isUsed() || pn->isDefn());
JS_ASSERT_IF(pn->isUsed(), pn->pn_cookie.level() == 0);
@ -936,14 +945,24 @@ static bool
EmitVarIncDec(JSContext *cx, ParseNode *pn, BytecodeEmitter *bce)
{
JSOp op = pn->pn_kid->getOp();
JS_ASSERT(IsLocalOp(op) || IsArgOp(op));
JS_ASSERT(IsArgOp(op) || IsLocalOp(op) || IsAliasedVarOp(op));
JS_ASSERT(pn->pn_kid->isKind(PNK_NAME));
JS_ASSERT(!pn->pn_kid->pn_cookie.isFree());
bool post;
JSOp binop = GetIncDecInfo(pn->getKind(), &post);
JSOp getOp = IsLocalOp(op) ? JSOP_GETLOCAL : JSOP_GETARG;
JSOp setOp = IsLocalOp(op) ? JSOP_SETLOCAL : JSOP_SETARG;
JSOp getOp, setOp;
if (IsLocalOp(op)) {
getOp = JSOP_GETLOCAL;
setOp = JSOP_SETLOCAL;
} else if (IsArgOp(op)) {
getOp = JSOP_GETARG;
setOp = JSOP_SETARG;
} else {
getOp = JSOP_GETALIASEDVAR;
setOp = JSOP_SETALIASEDVAR;
}
if (!EmitVarOp(cx, pn->pn_kid, getOp, bce)) // V
return false;
@ -1077,42 +1096,101 @@ EmitEnterBlock(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn, JSOp op)
}
/*
* Try to convert a *NAME op to a *GNAME op, which optimizes access to
* globals. Return true if a conversion was made.
*
* Don't convert to *GNAME ops within strict-mode eval, since access
* to a "global" might merely be to a binding local to that eval:
*
* "use strict";
* var x = "global";
* eval('var x = "eval"; eval("x");'); // 'eval', not 'global'
*
* Outside eval code, access to an undeclared global is a strict mode error:
*
* "use strict";
* function foo()
* {
* undeclared = 17; // throws ReferenceError
* }
* foo();
*
* In self-hosting mode, JSOP_*NAME is unconditionally converted to
* JSOP_*INTRINSIC. This causes lookups to be redirected to the special
* intrinsics holder in the global object, into which any missing values are
* cloned lazily upon first access.
* Try to convert a *NAME op with a free name to a more specialized GNAME,
* INTRINSIC or ALIASEDVAR op, which optimize accesses on that name.
* Return true if a conversion was made.
*/
static bool
TryConvertToGname(BytecodeEmitter *bce, ParseNode *pn, JSOp *op)
TryConvertFreeName(BytecodeEmitter *bce, ParseNode *pn)
{
if (bce->selfHostingMode) {
switch (*op) {
case JSOP_NAME: *op = JSOP_GETINTRINSIC; break;
case JSOP_SETNAME: *op = JSOP_SETINTRINSIC; break;
/*
* In self-hosting mode, JSOP_*NAME is unconditionally converted to
* JSOP_*INTRINSIC. This causes lookups to be redirected to the special
* intrinsics holder in the global object, into which any missing values are
* cloned lazily upon first access.
*/
if (bce->emitterMode == BytecodeEmitter::SelfHosting) {
JSOp op;
switch (pn->getOp()) {
case JSOP_NAME: op = JSOP_GETINTRINSIC; break;
case JSOP_SETNAME: op = JSOP_SETINTRINSIC; break;
/* Other *NAME ops aren't (yet) supported in self-hosted code. */
default: JS_NOT_REACHED("intrinsic");
}
pn->setOp(op);
return true;
}
/*
* When parsing inner functions lazily, parse nodes for outer functions no
* longer exist and only the function's scope chain is available for
* resolving upvar accesses within the inner function.
*/
if (bce->emitterMode == BytecodeEmitter::LazyFunction) {
size_t hops = 0;
FunctionBox *funbox = bce->sc->asFunctionBox();
if (funbox->hasExtensibleScope())
return false;
if (funbox->function()->atom() == pn->pn_atom)
return false;
if (funbox->function()->isHeavyweight()) {
hops++;
if (funbox->function()->isNamedLambda())
hops++;
}
if (bce->script->directlyInsideEval)
return false;
RootedObject outerScope(bce->sc->context, bce->script->enclosingStaticScope());
for (StaticScopeIter ssi(bce->sc->context, outerScope); !ssi.done(); ssi++) {
if (ssi.type() != StaticScopeIter::FUNCTION) {
if (ssi.hasDynamicScopeObject())
hops++;
continue;
}
RootedScript script(bce->sc->context, ssi.funScript());
if (script->function()->atom() == pn->pn_atom)
return false;
if (ssi.hasDynamicScopeObject()) {
uint16_t slot;
if (LookupAliasedName(script, pn->pn_atom->asPropertyName(), &slot)) {
JSOp op;
switch (pn->getOp()) {
case JSOP_NAME: op = JSOP_GETALIASEDVAR; break;
case JSOP_SETNAME: op = JSOP_SETALIASEDVAR; break;
default: return false;
}
pn->setOp(op);
JS_ALWAYS_TRUE(pn->pn_cookie.set(bce->sc->context, hops, slot));
return true;
}
hops++;
}
if (script->funHasExtensibleScope || script->directlyInsideEval)
return false;
}
}
/*
* Try to convert free names in global scope to GNAME opcodes.
*
* This conversion is not made if we are in strict mode. In eval code nested
* within (strict mode) eval code, access to an undeclared "global" might
* merely be to a binding local to that outer eval:
*
* "use strict";
* var x = "global";
* eval('var x = "eval"; eval("x");'); // 'eval', not 'global'
*
* Outside eval code, access to an undeclared global is a strict mode error:
*
* "use strict";
* function foo()
* {
* undeclared = 17; // throws ReferenceError
* }
* foo();
*/
if (bce->script->compileAndGo &&
bce->hasGlobalScope &&
!(bce->sc->isFunctionBox() && bce->sc->asFunctionBox()->mightAliasLocals()) &&
@ -1121,16 +1199,19 @@ TryConvertToGname(BytecodeEmitter *bce, ParseNode *pn, JSOp *op)
{
// If you change anything here, you might also need to change
// js::ReportIfUndeclaredVarAssignment.
switch (*op) {
case JSOP_NAME: *op = JSOP_GETGNAME; break;
case JSOP_SETNAME: *op = JSOP_SETGNAME; break;
JSOp op;
switch (pn->getOp()) {
case JSOP_NAME: op = JSOP_GETGNAME; break;
case JSOP_SETNAME: op = JSOP_SETGNAME; break;
case JSOP_SETCONST:
/* Not supported. */
return false;
default: JS_NOT_REACHED("gname");
}
pn->setOp(op);
return true;
}
return false;
}
@ -1225,8 +1306,7 @@ BindNameToSlotHelper(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn)
* If this is an eval in the global scope, then unbound variables
* must be globals, so try to use GNAME ops.
*/
if (!caller->functionOrCallerFunction() && TryConvertToGname(bce, pn, &op)) {
pn->setOp(op);
if (!caller->functionOrCallerFunction() && TryConvertFreeName(bce, pn)) {
pn->pn_dflags |= PND_BOUND;
return true;
}
@ -1239,12 +1319,10 @@ BindNameToSlotHelper(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn)
}
/* Optimize accesses to undeclared globals. */
if (!TryConvertToGname(bce, pn, &op))
if (!TryConvertFreeName(bce, pn))
return true;
pn->setOp(op);
pn->pn_dflags |= PND_BOUND;
return true;
}
@ -1394,7 +1472,7 @@ BindNameToSlot(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn)
if (!BindNameToSlotHelper(cx, bce, pn))
return false;
if (bce->selfHostingMode && !pn->isBound()) {
if (bce->emitterMode == BytecodeEmitter::SelfHosting && !pn->isBound()) {
bce->reportError(pn, JSMSG_SELFHOSTED_UNBOUND_NAME);
return false;
}
@ -1721,6 +1799,9 @@ EmitNameOp(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn, bool callContext)
case JSOP_GETLOCAL:
op = JSOP_CALLLOCAL;
break;
case JSOP_GETALIASEDVAR:
op = JSOP_CALLALIASEDVAR;
break;
default:
JS_ASSERT(op == JSOP_CALLEE);
break;
@ -2459,7 +2540,7 @@ frontend::EmitFunctionScript(JSContext *cx, BytecodeEmitter *bce, ParseNode *bod
if (bce->script->varIsAliased(varIndex)) {
ScopeCoordinate sc;
sc.hops = 0;
sc.slot = AliasedNameToSlot(bce->script, cx->names().arguments);
JS_ALWAYS_TRUE(LookupAliasedName(bce->script, cx->names().arguments, &sc.slot));
if (!EmitAliasedVarOp(cx, JSOP_SETALIASEDVAR, sc, bce))
return false;
} else {
@ -2498,28 +2579,10 @@ frontend::EmitFunctionScript(JSContext *cx, BytecodeEmitter *bce, ParseNode *bod
if (bce->parent && bce->parent->emittingRunOnceLambda)
bce->script->treatAsRunOnce = true;
/*
* Mark as singletons any function which will only be executed once, or
* which is inner to a lambda we only expect to run once. In the latter
* case, if the lambda runs multiple times then CloneFunctionObject will
* make a deep clone of its contents.
*/
bool singleton =
cx->typeInferenceEnabled() &&
bce->script->compileAndGo &&
bce->parent &&
(bce->parent->checkSingletonContext() ||
(!bce->parent->isInLoop() &&
bce->parent->parent &&
bce->parent->parent->emittingRunOnceLambda));
/* Initialize fun->script() so that the debugger has a valid fun->script(). */
RootedFunction fun(cx, bce->script->function());
JS_ASSERT(fun->isInterpreted());
JS_ASSERT(!fun->hasScript());
fun->setScript(bce->script);
if (!JSFunction::setTypeForScriptedFunction(cx, fun, singleton))
return false;
bce->tellDebuggerAboutCompiledScript(cx);
@ -3256,7 +3319,13 @@ EmitAssignment(JSContext *cx, BytecodeEmitter *bce, ParseNode *lhs, JSOp op, Par
if (!EmitAtomOp(cx, lhs, JSOP_GETINTRINSIC, bce))
return false;
} else {
JSOp op = lhs->isOp(JSOP_SETARG) ? JSOP_GETARG : JSOP_GETLOCAL;
JSOp op;
switch (lhs->getOp()) {
case JSOP_SETARG: op = JSOP_GETARG; break;
case JSOP_SETLOCAL: op = JSOP_GETLOCAL; break;
case JSOP_SETALIASEDVAR: op = JSOP_GETALIASEDVAR; break;
default: JS_NOT_REACHED("Bad op");
}
if (!EmitVarOp(cx, lhs, op, bce))
return false;
}
@ -3320,7 +3389,7 @@ EmitAssignment(JSContext *cx, BytecodeEmitter *bce, ParseNode *lhs, JSOp op, Par
}
break;
}
if (lhs->isOp(JSOP_SETARG) || lhs->isOp(JSOP_SETLOCAL)) {
if (lhs->isOp(JSOP_SETARG) || lhs->isOp(JSOP_SETLOCAL) || lhs->isOp(JSOP_SETALIASEDVAR)) {
if (!EmitVarOp(cx, lhs, lhs->getOp(), bce))
return false;
} else {
@ -4333,20 +4402,49 @@ EmitFor(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn, ptrdiff_t top)
static JS_NEVER_INLINE bool
EmitFunc(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn)
{
RootedFunction fun(cx, pn->pn_funbox->function());
FunctionBox *funbox = pn->pn_funbox;
RootedFunction fun(cx, funbox->function());
if (fun->isNative()) {
JS_ASSERT(IsAsmJSModuleNative(fun->native()));
return true;
}
if (fun->hasScript()) {
JS_ASSERT_IF(fun->isInterpretedLazy(), fun->lazyScript());
/*
* Set the EMITTEDFUNCTION flag in function definitions once they have been
* emitted. Function definitions that need hoisting to the top of the
* function will be seen by EmitFunc in two places.
*/
if (pn->pn_dflags & PND_EMITTEDFUNCTION) {
JS_ASSERT_IF(fun->hasScript(), fun->nonLazyScript());
JS_ASSERT(pn->functionIsHoisted());
JS_ASSERT(bce->sc->isFunctionBox());
return true;
}
{
pn->pn_dflags |= PND_EMITTEDFUNCTION;
/*
* Mark as singletons any function which will only be executed once, or
* which is inner to a lambda we only expect to run once. In the latter
* case, if the lambda runs multiple times then CloneFunctionObject will
* make a deep clone of its contents.
*/
bool singleton =
cx->typeInferenceEnabled() &&
bce->script->compileAndGo &&
(bce->checkSingletonContext() ||
(!bce->isInLoop() &&
bce->parent &&
bce->parent->emittingRunOnceLambda));
if (!JSFunction::setTypeForScriptedFunction(cx, fun, singleton))
return false;
if (fun->isInterpretedLazy()) {
if (!fun->lazyScript()->parent())
fun->lazyScript()->initParent(bce->script);
} else {
SharedContext *outersc = bce->sc;
FunctionBox *funbox = pn->pn_funbox;
if (outersc->isFunctionBox() && outersc->asFunctionBox()->mightAliasLocals())
funbox->setMightAliasLocals(); // inherit mightAliasLocals from parent
@ -4402,7 +4500,7 @@ EmitFunc(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn)
uint32_t lineNum = bce->parser->tokenStream.srcCoords.lineNum(pn->pn_pos.begin);
BytecodeEmitter bce2(bce, bce->parser, funbox, script, bce->insideEval,
bce->evalCaller, bce->hasGlobalScope, lineNum,
bce->selfHostingMode);
bce->emitterMode);
if (!bce2.init())
return false;
@ -4841,7 +4939,8 @@ EmitCallOrNew(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn)
ParseNode *pn2 = pn->pn_head;
switch (pn2->getKind()) {
case PNK_NAME:
if (bce->selfHostingMode && pn2->name() == cx->names().callFunction)
if (bce->emitterMode == BytecodeEmitter::SelfHosting &&
pn2->name() == cx->names().callFunction)
{
/*
* Special-casing of callFunction to emit bytecode that directly
@ -5065,6 +5164,7 @@ EmitIncOrDec(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn)
switch (op) {
case JSOP_SETLOCAL:
case JSOP_SETARG:
case JSOP_SETALIASEDVAR:
case JSOP_SETNAME:
case JSOP_SETGNAME:
maySet = true;

View File

@ -130,10 +130,24 @@ struct BytecodeEmitter
const bool hasGlobalScope:1; /* frontend::CompileScript's scope chain is the
global object */
const bool selfHostingMode:1; /* Emit JSOP_CALLINTRINSIC instead of JSOP_NAME
and assert that JSOP_NAME and JSOP_*GNAME
don't ever get emitted. See the comment for
the field |selfHostingMode| in Parser.h for details. */
enum EmitterMode {
Normal,
/*
* Emit JSOP_CALLINTRINSIC instead of JSOP_NAME and assert that
* JSOP_NAME and JSOP_*GNAME don't ever get emitted. See the comment
* for the field |selfHostingMode| in Parser.h for details.
*/
SelfHosting,
/*
* Check the static scope chain of the root function for resolving free
* variable accesses in the script.
*/
LazyFunction
};
const EmitterMode emitterMode;
/*
* Note that BytecodeEmitters are magic: they own the arena "top-of-stack"
@ -143,7 +157,7 @@ struct BytecodeEmitter
*/
BytecodeEmitter(BytecodeEmitter *parent, Parser<FullParseHandler> *parser, SharedContext *sc,
HandleScript script, bool insideEval, HandleScript evalCaller,
bool hasGlobalScope, uint32_t lineNum, bool selfHostingMode = false);
bool hasGlobalScope, uint32_t lineNum, EmitterMode emitterMode = Normal);
bool init();
bool isAliasedName(ParseNode *pn);

View File

@ -272,9 +272,12 @@ FoldConstants<FullParseHandler>(JSContext *cx, ParseNode **pnp,
if (!FoldConstants(cx, &pn->pn_body, parser))
return false;
} else {
// Note: pn_body is NULL for functions which are being lazily parsed.
JS_ASSERT(pn->getKind() == PNK_FUNCTION);
if (!FoldConstants(cx, &pn->pn_body, parser, pn->pn_funbox->inGenexpLambda))
return false;
if (pn->pn_body) {
if (!FoldConstants(cx, &pn->pn_body, parser, pn->pn_funbox->inGenexpLambda))
return false;
}
}
break;

View File

@ -15,6 +15,13 @@
namespace js {
namespace frontend {
template <typename ParseHandler>
class Parser;
class SyntaxParseHandler;
// Parse handler used when generating a full parse tree for all code which the
// parser encounters.
class FullParseHandler
{
ParseNodeAllocator allocator;
@ -34,17 +41,38 @@ class FullParseHandler
return node;
}
/*
* If this is a full parse to construct the bytecode for a function that
* was previously lazily parsed, that lazy function and the current index
* into its inner functions. We do not want to reparse the inner functions.
*/
LazyScript * const lazyOuterFunction_;
size_t lazyInnerFunctionIndex;
public:
/*
* If non-NULL, points to a syntax parser which can be used for inner
* functions. Cleared if language features not handled by the syntax parser
* are encountered, in which case all future activity will use the full
* parser.
*/
Parser<SyntaxParseHandler> *syntaxParser;
/* new_ methods for creating parse nodes. These report OOM on context. */
JS_DECLARE_NEW_METHODS(new_, allocParseNode, inline)
typedef ParseNode *Node;
typedef Definition *DefinitionNode;
FullParseHandler(JSContext *cx, TokenStream &tokenStream, bool foldConstants)
FullParseHandler(JSContext *cx, TokenStream &tokenStream, bool foldConstants,
Parser<SyntaxParseHandler> *syntaxParser, LazyScript *lazyOuterFunction)
: allocator(cx),
tokenStream(tokenStream),
foldConstants(foldConstants)
foldConstants(foldConstants),
lazyOuterFunction_(lazyOuterFunction),
lazyInnerFunctionIndex(0),
syntaxParser(syntaxParser)
{}
static ParseNode *null() { return NULL; }
@ -61,8 +89,8 @@ class FullParseHandler
pn->setOp(JSOP_NAME);
return pn;
}
Definition *newPlaceholder(ParseNode *pn, ParseContext<FullParseHandler> *pc) {
Definition *dn = (Definition *) NameNode::create(PNK_NAME, pn->pn_atom, this, pc);
Definition *newPlaceholder(JSAtom *atom, ParseContext<FullParseHandler> *pc) {
Definition *dn = (Definition *) NameNode::create(PNK_NAME, atom, this, pc);
if (!dn)
return NULL;
@ -313,6 +341,17 @@ class FullParseHandler
static Definition *nullDefinition() {
return NULL;
}
void disableSyntaxParser() {
syntaxParser = NULL;
}
LazyScript *lazyOuterFunction() {
return lazyOuterFunction_;
}
JSFunction *nextLazyInnerFunction() {
JS_ASSERT(lazyInnerFunctionIndex < lazyOuterFunction()->numInnerFunctions());
return lazyOuterFunction()->innerFunctions()[lazyInnerFunctionIndex++];
}
};
inline bool

View File

@ -632,6 +632,7 @@ struct ParseNode {
'arguments' that has been converted
into a definition after the function
body has been parsed. */
#define PND_EMITTEDFUNCTION 0x400 /* hoisted function that was emitted */
/* Flags to propagate from uses to definition. */
#define PND_USE2DEF_FLAGS (PND_ASSIGNED | PND_CLOSED)

View File

@ -62,6 +62,7 @@ ParseContext<ParseHandler>::ParseContext(Parser<ParseHandler> *prs,
oldpc(prs->pc),
lexdeps(prs->context),
funcStmts(NULL),
innerFunctions(prs->context),
inDeclDestructuring(false),
funBecameStrict(false)
{

View File

@ -59,6 +59,7 @@
using namespace js;
using namespace js::gc;
using mozilla::Maybe;
namespace js {
namespace frontend {
@ -213,6 +214,10 @@ ParseContext<SyntaxParseHandler>::define(JSContext *cx, HandlePropertyName name,
if (lexdeps.lookupDefn<SyntaxParseHandler>(name))
lexdeps->remove(name);
// Keep track of the number of arguments in args_, for fun->nargs.
if (kind == Definition::ARG && !args_.append((Definition *) NULL))
return false;
return decls_.addUnique(name, kind);
}
@ -378,9 +383,27 @@ Parser<ParseHandler>::reportWithOffset(ParseReportKind kind, bool strict, uint32
return result;
}
template <>
bool
Parser<FullParseHandler>::abortIfSyntaxParser()
{
handler.disableSyntaxParser();
return true;
}
template <>
bool
Parser<SyntaxParseHandler>::abortIfSyntaxParser()
{
abortedSyntaxParse = true;
return false;
}
template <typename ParseHandler>
Parser<ParseHandler>::Parser(JSContext *cx, const CompileOptions &options,
const jschar *chars, size_t length, bool foldConstants)
const jschar *chars, size_t length, bool foldConstants,
Parser<SyntaxParseHandler> *syntaxParser,
LazyScript *lazyOuterFunction)
: AutoGCRooter(cx, PARSER),
context(cx),
tokenStream(cx, options, chars, length, thisForCtor(), keepAtoms),
@ -391,10 +414,19 @@ Parser<ParseHandler>::Parser(JSContext *cx, const CompileOptions &options,
foldConstants(foldConstants),
compileAndGo(options.compileAndGo),
selfHostingMode(options.selfHostingMode),
unknownResult(false),
handler(cx, tokenStream, foldConstants)
abortedSyntaxParse(false),
handler(cx, tokenStream, foldConstants, syntaxParser, lazyOuterFunction)
{
// XXX bug 678037 always disable syntax parsing for now.
handler.disableSyntaxParser();
cx->activeCompilations++;
// The Mozilla specific 'strict' option adds extra warnings which are not
// generated if functions are parsed lazily. Note that the standard
// "use strict" does not inhibit lazy parsing.
if (context->hasStrictOption())
handler.disableSyntaxParser();
}
template <typename ParseHandler>
@ -984,6 +1016,18 @@ template <>
bool
Parser<SyntaxParseHandler>::checkFunctionArguments()
{
if (pc->sc->asFunctionBox()->function()->hasRest()) {
if (pc->lexdeps->lookup(context->names().arguments)) {
report(ParseError, false, null(), JSMSG_ARGUMENTS_AND_REST);
return false;
}
DefinitionNode maybeArgDef = pc->decls().lookupFirst(context->names().arguments);
if (maybeArgDef && handler.getDefinitionKind(maybeArgDef) != Definition::ARG) {
report(ParseError, false, null(), JSMSG_ARGUMENTS_AND_REST);
return false;
}
}
return true;
}
@ -1223,6 +1267,49 @@ MatchOrInsertSemicolon(JSContext *cx, TokenStream *ts)
return true;
}
template <typename ParseHandler>
typename ParseHandler::DefinitionNode
Parser<ParseHandler>::getOrCreateLexicalDependency(ParseContext<ParseHandler> *pc, JSAtom *atom)
{
AtomDefnAddPtr p = pc->lexdeps->lookupForAdd(atom);
if (p)
return p.value().get<ParseHandler>();
DefinitionNode dn = handler.newPlaceholder(atom, pc);
if (!dn)
return ParseHandler::nullDefinition();
DefinitionSingle def = DefinitionSingle::new_<ParseHandler>(dn);
if (!pc->lexdeps->add(p, atom, def))
return ParseHandler::nullDefinition();
return dn;
}
static bool
ConvertDefinitionToNamedLambdaUse(JSContext *cx, ParseContext<FullParseHandler> *pc,
FunctionBox *funbox, Definition *dn)
{
dn->setOp(JSOP_CALLEE);
if (!dn->pn_cookie.set(cx, pc->staticLevel, UpvarCookie::CALLEE_SLOT))
return false;
dn->pn_dflags |= PND_BOUND;
JS_ASSERT(dn->kind() == Definition::NAMED_LAMBDA);
/*
* Since 'dn' is a placeholder, it has not been defined in the
* ParseContext and hence we must manually flag a closed-over
* callee name as needing a dynamic scope (this is done for all
* definitions in the ParseContext by generateFunctionBindings).
*
* If 'dn' has been assigned to, then we also flag the function
* scope has needing a dynamic scope so that dynamic scope
* setter can either ignore the set (in non-strict mode) or
* produce an error (in strict mode).
*/
if (dn->isClosed() || dn->isAssigned())
funbox->function()->setIsHeavyweight();
return true;
}
/*
* Beware: this function is called for functions nested in other functions or
* global scripts but not for functions compiled through the Function
@ -1251,26 +1338,8 @@ Parser<FullParseHandler>::leaveFunction(ParseNode *fn, HandlePropertyName funNam
JS_ASSERT(dn->isPlaceholder());
if (atom == funName && kind == Expression) {
dn->setOp(JSOP_CALLEE);
if (!dn->pn_cookie.set(context, pc->staticLevel,
UpvarCookie::CALLEE_SLOT))
if (!ConvertDefinitionToNamedLambdaUse(context, pc, funbox, dn))
return false;
dn->pn_dflags |= PND_BOUND;
JS_ASSERT(dn->kind() == Definition::NAMED_LAMBDA);
/*
* Since 'dn' is a placeholder, it has not been defined in the
* ParseContext and hence we must manually flag a closed-over
* callee name as needing a dynamic scope (this is done for all
* definitions in the ParseContext by generateFunctionBindings).
*
* If 'dn' has been assigned to, then we also flag the function
* scope has needing a dynamic scope so that dynamic scope
* setter can either ignore the set (in non-strict mode) or
* produce an error (in strict mode).
*/
if (dn->isClosed() || dn->isAssigned())
funbox->function()->setIsHeavyweight();
continue;
}
@ -1293,38 +1362,30 @@ Parser<FullParseHandler>::leaveFunction(ParseNode *fn, HandlePropertyName funNam
handler.deoptimizeUsesWithin(dn, fn->pn_pos);
if (!outer_dn) {
AtomDefnAddPtr p = outerpc->lexdeps->lookupForAdd(atom);
if (p) {
outer_dn = p.value().get<FullParseHandler>();
} else {
/*
* Create a new placeholder for our outer lexdep. We could
* simply re-use the inner placeholder, but that introduces
* subtleties in the case where we find a later definition
* that captures an existing lexdep. For example:
*
* function f() { function g() { x; } let x; }
*
* Here, g's TOK_UPVARS node lists the placeholder for x,
* which must be captured by the 'let' declaration later,
* since 'let's are hoisted. Taking g's placeholder as our
* own would work fine. But consider:
*
* function f() { x; { function g() { x; } let x; } }
*
* Here, the 'let' must not capture all the uses of f's
* lexdep entry for x, but it must capture the x node
* referred to from g's TOK_UPVARS node. Always turning
* inherited lexdeps into uses of a new outer definition
* allows us to handle both these cases in a natural way.
*/
outer_dn = handler.newPlaceholder(dn, outerpc);
if (!outer_dn)
return false;
DefinitionSingle def = DefinitionSingle::new_<FullParseHandler>(outer_dn);
if (!outerpc->lexdeps->add(p, atom, def))
return false;
}
/*
* Create a new placeholder for our outer lexdep. We could
* simply re-use the inner placeholder, but that introduces
* subtleties in the case where we find a later definition
* that captures an existing lexdep. For example:
*
* function f() { function g() { x; } let x; }
*
* Here, g's TOK_UPVARS node lists the placeholder for x,
* which must be captured by the 'let' declaration later,
* since 'let's are hoisted. Taking g's placeholder as our
* own would work fine. But consider:
*
* function f() { x; { function g() { x; } let x; } }
*
* Here, the 'let' must not capture all the uses of f's
* lexdep entry for x, but it must capture the x node
* referred to from g's TOK_UPVARS node. Always turning
* inherited lexdeps into uses of a new outer definition
* allows us to handle both these cases in a natural way.
*/
outer_dn = getOrCreateLexicalDependency(outerpc, atom);
if (!outer_dn)
return false;
}
/*
@ -1363,11 +1424,7 @@ Parser<FullParseHandler>::leaveFunction(ParseNode *fn, HandlePropertyName funNam
InternalHandle<Bindings*> bindings =
InternalHandle<Bindings*>::fromMarkedLocation(&funbox->bindings);
if (!pc->generateFunctionBindings(context, bindings))
return false;
pc->lexdeps.releaseMap(context);
return true;
return pc->generateFunctionBindings(context, bindings);
}
template <>
@ -1376,8 +1433,10 @@ Parser<SyntaxParseHandler>::leaveFunction(Node fn, HandlePropertyName funName,
ParseContext<SyntaxParseHandler> *outerpc,
FunctionSyntaxKind kind)
{
pc->lexdeps.releaseMap(context);
return true;
outerpc->blockidGen = pc->blockidGen;
FunctionBox *funbox = pc->sc->asFunctionBox();
return addFreeVariablesFromLazyFunction(funbox->function(), outerpc);
}
/*
@ -1486,7 +1545,7 @@ Parser<ParseHandler>::functionArguments(FunctionSyntaxKind kind, Node *listp, No
// Record the start of function source (for FunctionToString). If we
// are parenFreeArrow, we will set this below, after consuming the NAME.
funbox->bufStart = tokenStream.currentToken().pos.begin;
funbox->setStart(tokenStream);
}
hasRest = false;
@ -1587,7 +1646,7 @@ Parser<ParseHandler>::functionArguments(FunctionSyntaxKind kind, Node *listp, No
case TOK_NAME:
{
if (parenFreeArrow)
funbox->bufStart = tokenStream.currentToken().pos.begin;
funbox->setStart(tokenStream);
RootedPropertyName name(context, tokenStream.currentToken().name());
bool disallowDuplicateArgs = destructuringArg || hasDefaults;
@ -1643,9 +1702,11 @@ Parser<ParseHandler>::functionArguments(FunctionSyntaxKind kind, Node *listp, No
template <>
bool
Parser<FullParseHandler>::checkFunctionDefinition(HandlePropertyName funName,
ParseNode **pn_, FunctionSyntaxKind kind)
ParseNode **pn_, FunctionSyntaxKind kind,
bool *pbodyProcessed)
{
ParseNode *&pn = *pn_;
*pbodyProcessed = false;
/* Function statements add a binding to the enclosing scope. */
bool bodyLevel = pc->atBodyLevel();
@ -1712,7 +1773,6 @@ Parser<FullParseHandler>::checkFunctionDefinition(HandlePropertyName funName,
* statements (e.g., functions in an "if" or "while" block) are
* dynamically bound when control flow reaches the statement. The
* emitter normally emits functions in two passes (see PNK_ARGSBODY).
* To distinguish
*/
if (bodyLevel) {
JS_ASSERT(pn->functionIsHoisted());
@ -1759,14 +1819,86 @@ Parser<FullParseHandler>::checkFunctionDefinition(HandlePropertyName funName,
pn->setOp(JSOP_LAMBDA);
}
// When a lazily-parsed function is called, we only fully parse (and emit)
// that function, not any of its nested children. The initial syntax-only
// parse recorded the free variables of nested functions and their extents,
// so we can skip over them after accounting for their free variables.
if (LazyScript *lazyOuter = handler.lazyOuterFunction()) {
JSFunction *fun = handler.nextLazyInnerFunction();
FunctionBox *funbox = newFunctionBox(fun, pc, /* strict = */ false);
if (!funbox)
return false;
handler.setFunctionBox(pn, funbox);
if (!addFreeVariablesFromLazyFunction(fun, pc))
return false;
// The position passed to tokenStream.advance() is relative to
// userbuf.base() while LazyScript::{begin,end} offsets are relative to
// the outermost script source. N.B: userbuf.base() is initialized
// (in TokenStream()) to begin() - column() so that column numbers in
// the lazily parsed script are correct.
uint32_t userbufBase = lazyOuter->begin() - lazyOuter->column();
tokenStream.advance(fun->lazyScript()->end() - userbufBase);
*pbodyProcessed = true;
return true;
}
return true;
}
template <class T, class U>
static inline void
PropagateTransitiveParseFlags(const T *inner, U *outer)
{
if (inner->bindingsAccessedDynamically())
outer->setBindingsAccessedDynamically();
if (inner->hasDebuggerStatement())
outer->setHasDebuggerStatement();
}
template <typename ParseHandler>
bool
Parser<ParseHandler>::addFreeVariablesFromLazyFunction(JSFunction *fun,
ParseContext<ParseHandler> *pc)
{
// Update any definition nodes in this context according to free variables
// in a lazily parsed inner function.
LazyScript *lazy = fun->lazyScript();
HeapPtrAtom *freeVariables = lazy->freeVariables();
for (size_t i = 0; i < lazy->numFreeVariables(); i++) {
JSAtom *atom = freeVariables[i];
// 'arguments' will be implicitly bound within the inner function.
if (atom == context->names().arguments)
continue;
DefinitionNode dn = pc->decls().lookupFirst(atom);
if (!dn) {
dn = getOrCreateLexicalDependency(pc, atom);
if (!dn)
return false;
}
/* Mark the outer dn as escaping. */
handler.setFlag(handler.getDefinitionNode(dn), PND_CLOSED);
}
PropagateTransitiveParseFlags(lazy, pc->sc);
return true;
}
template <>
bool
Parser<SyntaxParseHandler>::checkFunctionDefinition(HandlePropertyName funName,
Node *pn, FunctionSyntaxKind kind)
Node *pn, FunctionSyntaxKind kind,
bool *pbodyProcessed)
{
*pbodyProcessed = false;
/* Function statements add a binding to the enclosing scope. */
bool bodyLevel = pc->atBodyLevel();
@ -1797,6 +1929,11 @@ Parser<SyntaxParseHandler>::checkFunctionDefinition(HandlePropertyName funName,
pc->sc->setBindingsAccessedDynamically();
}
if (kind == Arrow) {
/* Arrow functions cannot yet be parsed lazily. */
return abortIfSyntaxParser();
}
return true;
}
@ -1812,9 +1949,13 @@ Parser<ParseHandler>::functionDef(HandlePropertyName funName, const TokenStream:
if (!pn)
return null();
if (!checkFunctionDefinition(funName, &pn, kind))
bool bodyProcessed;
if (!checkFunctionDefinition(funName, &pn, kind, &bodyProcessed))
return null();
if (bodyProcessed)
return pn;
RootedFunction fun(context, newFunction(pc, funName, kind));
if (!fun)
return null();
@ -1825,8 +1966,8 @@ Parser<ParseHandler>::functionDef(HandlePropertyName funName, const TokenStream:
handler.setFunctionBody(pn, null());
bool initiallyStrict = kind == Arrow || pc->sc->strict;
bool becameStrict;
if (!functionArgsAndBody(pn, fun, funName, startOffset, type, kind, initiallyStrict,
&becameStrict))
if (!functionArgsAndBody(pn, fun, funName, startOffset,
type, kind, initiallyStrict, &becameStrict))
{
if (initiallyStrict || !becameStrict || tokenStream.hadError())
return null();
@ -1846,22 +1987,10 @@ Parser<ParseHandler>::functionDef(HandlePropertyName funName, const TokenStream:
template <>
bool
Parser<FullParseHandler>::finishFunctionDefinition(ParseNode *pn, FunctionBox *funbox,
ParseNode *prelude, ParseNode *body,
ParseContext<FullParseHandler> *outerpc)
ParseNode *prelude, ParseNode *body)
{
pn->pn_pos.end = tokenStream.currentToken().pos.end;
/*
* Fruit of the poisonous tree: if a closure contains a dynamic name access
* (eval, with, etc), we consider the parent to do the same. The reason is
* that the deoptimizing effects of dynamic name access apply equally to
* parents: any local can be read at runtime.
*/
if (funbox->bindingsAccessedDynamically())
outerpc->sc->setBindingsAccessedDynamically();
if (funbox->hasDebuggerStatement())
outerpc->sc->setHasDebuggerStatement();
#if JS_HAS_DESTRUCTURING
/*
* If there were destructuring formal parameters, prepend the initializing
@ -1900,7 +2029,6 @@ Parser<FullParseHandler>::finishFunctionDefinition(ParseNode *pn, FunctionBox *f
pn->pn_funbox = funbox;
pn->pn_body->append(body);
pn->pn_body->pn_pos = body->pn_pos;
pn->pn_blockid = outerpc->blockid();
return true;
}
@ -1908,21 +2036,144 @@ Parser<FullParseHandler>::finishFunctionDefinition(ParseNode *pn, FunctionBox *f
template <>
bool
Parser<SyntaxParseHandler>::finishFunctionDefinition(Node pn, FunctionBox *funbox,
Node prelude, Node body,
ParseContext<SyntaxParseHandler> *outerpc)
Node prelude, Node body)
{
// The LazyScript for a lazily parsed function needs to be constructed
// while its ParseContext and associated lexdeps and inner functions are
// still available.
if (funbox->inWith)
return abortIfSyntaxParser();
size_t numFreeVariables = pc->lexdeps->count();
size_t numInnerFunctions = pc->innerFunctions.length();
LazyScript *lazy = LazyScript::Create(context, numFreeVariables, numInnerFunctions,
funbox->bufStart, funbox->bufEnd,
funbox->startLine, funbox->startColumn);
if (!lazy)
return false;
HeapPtrAtom *freeVariables = lazy->freeVariables();
size_t i = 0;
for (AtomDefnRange r = pc->lexdeps->all(); !r.empty(); r.popFront())
freeVariables[i++].init(r.front().key());
JS_ASSERT(i == numFreeVariables);
HeapPtrFunction *innerFunctions = lazy->innerFunctions();
for (size_t i = 0; i < numInnerFunctions; i++)
innerFunctions[i].init(pc->innerFunctions[i]);
if (pc->sc->strict)
lazy->setStrict();
PropagateTransitiveParseFlags(funbox, lazy);
funbox->object->toFunction()->initLazyScript(lazy);
return true;
}
template <typename ParseHandler>
template <>
bool
Parser<ParseHandler>::functionArgsAndBody(Node pn, HandleFunction fun, HandlePropertyName funName,
size_t startOffset, FunctionType type,
FunctionSyntaxKind kind, bool strict, bool *becameStrict)
Parser<FullParseHandler>::functionArgsAndBody(ParseNode *pn, HandleFunction fun,
HandlePropertyName funName,
size_t startOffset, FunctionType type,
FunctionSyntaxKind kind,
bool strict, bool *becameStrict)
{
if (becameStrict)
*becameStrict = false;
ParseContext<ParseHandler> *outerpc = pc;
ParseContext<FullParseHandler> *outerpc = pc;
// Create box for fun->object early to protect against last-ditch GC.
FunctionBox *funbox = newFunctionBox(fun, pc, strict);
if (!funbox)
return false;
// Disable lazy parsing if any functions are defined within a scope
// statement. Free names in the inner functions will be bound incorrectly.
if (pc->topScopeStmt)
handler.disableSyntaxParser();
// Try a syntax parse for this inner function.
do {
Parser<SyntaxParseHandler> *parser = handler.syntaxParser;
if (!parser)
break;
{
// Move the syntax parser to the current position in the stream.
TokenStream::Position position(keepAtoms);
tokenStream.tell(&position);
parser->tokenStream.seek(position, tokenStream);
ParseContext<SyntaxParseHandler> funpc(parser, outerpc, funbox,
outerpc->staticLevel + 1, outerpc->blockidGen);
if (!funpc.init())
return false;
if (!parser->functionArgsAndBodyGeneric(SyntaxParseHandler::NodeGeneric,
fun, funName, type, kind, strict, becameStrict))
{
if (parser->hadAbortedSyntaxParse()) {
// Try again with a full parse.
parser->clearAbortedSyntaxParse();
break;
}
return false;
}
outerpc->blockidGen = funpc.blockidGen;
// Advance this parser over tokens processed by the syntax parser.
parser->tokenStream.tell(&position);
tokenStream.seek(position, parser->tokenStream);
}
pn->pn_funbox = funbox;
if (!addFreeVariablesFromLazyFunction(fun, pc))
return false;
pn->pn_blockid = outerpc->blockid();
PropagateTransitiveParseFlags(funbox, outerpc->sc);
return true;
} while (false);
// Continue doing a full parse for this inner function.
ParseContext<FullParseHandler> funpc(this, pc, funbox,
outerpc->staticLevel + 1, outerpc->blockidGen);
if (!funpc.init())
return false;
if (!functionArgsAndBodyGeneric(pn, fun, funName, type, kind, strict, becameStrict))
return false;
if (!leaveFunction(pn, funName, outerpc, kind))
return false;
pn->pn_blockid = outerpc->blockid();
/*
* Fruit of the poisonous tree: if a closure contains a dynamic name access
* (eval, with, etc), we consider the parent to do the same. The reason is
* that the deoptimizing effects of dynamic name access apply equally to
* parents: any local can be read at runtime.
*/
PropagateTransitiveParseFlags(funbox, outerpc->sc);
return true;
}
template <>
bool
Parser<SyntaxParseHandler>::functionArgsAndBody(Node pn, HandleFunction fun,
HandlePropertyName funName,
size_t startOffset, FunctionType type,
FunctionSyntaxKind kind,
bool strict, bool *becameStrict)
{
if (becameStrict)
*becameStrict = false;
ParseContext<SyntaxParseHandler> *outerpc = pc;
// Create box for fun->object early to protect against last-ditch GC.
FunctionBox *funbox = newFunctionBox(fun, pc, strict);
@ -1930,17 +2181,82 @@ Parser<ParseHandler>::functionArgsAndBody(Node pn, HandleFunction fun, HandlePro
return false;
// Initialize early for possible flags mutation via destructuringExpr.
ParseContext<ParseHandler> funpc(this, pc, funbox, outerpc->staticLevel + 1, outerpc->blockidGen);
ParseContext<SyntaxParseHandler> funpc(this, pc, funbox,
outerpc->staticLevel + 1, outerpc->blockidGen);
if (!funpc.init())
return false;
// Now parse formal argument list and compute fun->nargs.
if (!functionArgsAndBodyGeneric(pn, fun, funName, type, kind, strict, becameStrict))
return false;
if (!leaveFunction(pn, funName, outerpc, kind))
return false;
// This is a lazy function inner to another lazy function. Remember the
// inner function so that if the outer function is eventually parsed we do
// not need any further parsing or processing of the inner function.
JS_ASSERT(fun->lazyScript());
return outerpc->innerFunctions.append(fun);
}
template <>
ParseNode *
Parser<FullParseHandler>::standaloneLazyFunction(HandleFunction fun, unsigned staticLevel,
bool strict)
{
Node pn = handler.newFunctionDefinition();
if (!pn)
return null();
FunctionBox *funbox = newFunctionBox(fun, /* outerpc = */ NULL, strict);
if (!funbox)
return null();
handler.setFunctionBox(pn, funbox);
ParseContext<FullParseHandler> funpc(this, NULL, funbox, staticLevel, 0);
if (!funpc.init())
return null();
RootedPropertyName funName(context, fun->atom() ? fun->atom()->asPropertyName() : NULL);
if (!functionArgsAndBodyGeneric(pn, fun, funName, Normal, Statement, strict, NULL))
return null();
if (fun->isNamedLambda()) {
if (AtomDefnPtr p = pc->lexdeps->lookup(funName)) {
Definition *dn = p.value().get<FullParseHandler>();
if (!ConvertDefinitionToNamedLambdaUse(context, pc, funbox, dn))
return NULL;
}
}
InternalHandle<Bindings*> bindings =
InternalHandle<Bindings*>::fromMarkedLocation(&funbox->bindings);
if (!pc->generateFunctionBindings(context, bindings))
return null();
return pn;
}
template <typename ParseHandler>
bool
Parser<ParseHandler>::functionArgsAndBodyGeneric(Node pn, HandleFunction fun,
HandlePropertyName funName, FunctionType type,
FunctionSyntaxKind kind,
bool strict, bool *becameStrict)
{
// Given a properly initialized parse context, try to parse an actual
// function without concern for conversion to strict mode, use of lazy
// parsing and such.
Node prelude = null();
bool hasRest;
if (!functionArguments(kind, &prelude, pn, hasRest))
return false;
fun->setArgCount(funpc.numArgs());
FunctionBox *funbox = pc->sc->asFunctionBox();
fun->setArgCount(pc->numArgs());
if (funbox->ndefaults)
fun->setHasDefaults();
if (hasRest)
@ -1961,7 +2277,7 @@ Parser<ParseHandler>::functionArgsAndBody(Node pn, HandleFunction fun, HandlePro
}
// Parse the function body.
mozilla::Maybe<GenexpGuard<ParseHandler> > yieldGuard;
Maybe<GenexpGuard<ParseHandler> > yieldGuard;
if (kind == Arrow)
yieldGuard.construct(this);
@ -2004,10 +2320,7 @@ Parser<ParseHandler>::functionArgsAndBody(Node pn, HandleFunction fun, HandlePro
}
#endif
if (!finishFunctionDefinition(pn, funbox, prelude, body, outerpc))
return false;
return leaveFunction(pn, funName, outerpc, kind);
return finishFunctionDefinition(pn, funbox, prelude, body);
}
template <>
@ -2050,7 +2363,7 @@ template <>
SyntaxParseHandler::Node
Parser<SyntaxParseHandler>::moduleDecl()
{
setUnknownResult();
JS_ALWAYS_FALSE(abortIfSyntaxParser());
return SyntaxParseHandler::NodeFailure;
}
@ -2179,6 +2492,8 @@ Parser<ParseHandler>::maybeParseDirective(Node pn, bool *cont)
if (pc->sc->isFunctionBox()) {
pc->sc->asFunctionBox()->useAsm = true;
pc->sc->asFunctionBox()->asmStart = handler.getPosition(pn).begin;
if (!abortIfSyntaxParser())
return false;
} else {
if (!report(ParseWarning, false, pn, JSMSG_USE_ASM_DIRECTIVE_FAIL))
return false;
@ -2586,24 +2901,17 @@ Parser<ParseHandler>::noteNameUse(HandlePropertyName name, Node pn)
if (!defs.empty()) {
dn = defs.front<ParseHandler>();
} else {
if (AtomDefnAddPtr p = pc->lexdeps->lookupForAdd(name)) {
dn = p.value().get<ParseHandler>();
} else {
/*
* No definition before this use in any lexical scope.
* Create a placeholder definition node to either:
* - Be adopted when we parse the real defining
* declaration, or
* - Be left as a free variable definition if we never
* see the real definition.
*/
dn = handler.newPlaceholder(pn, pc);
if (!dn)
return false;
DefinitionSingle def = DefinitionSingle::new_<ParseHandler>(dn);
if (!pc->lexdeps->add(p, name, def))
return false;
}
/*
* No definition before this use in any lexical scope.
* Create a placeholder definition node to either:
* - Be adopted when we parse the real defining
* declaration, or
* - Be left as a free variable definition if we never
* see the real definition.
*/
dn = getOrCreateLexicalDependency(pc, name);
if (!dn)
return false;
}
handler.linkUseToDef(pn, dn);
@ -2847,8 +3155,7 @@ bool
Parser<SyntaxParseHandler>::checkDestructuring(BindData<SyntaxParseHandler> *data,
Node left, bool toplevel)
{
setUnknownResult();
return false;
return abortIfSyntaxParser();
}
template <typename ParseHandler>
@ -2889,6 +3196,9 @@ Parser<ParseHandler>::returnOrYield(bool useAssignExpr)
#if JS_HAS_GENERATORS
if (tt == TOK_YIELD) {
if (!abortIfSyntaxParser())
return null();
/*
* If we're within parens, we won't know if this is a generator expression until we see
* a |for| token, so we have to delay flagging the current function.
@ -3023,7 +3333,7 @@ template <>
SyntaxParseHandler::Node
Parser<SyntaxParseHandler>::pushLetScope(HandleStaticBlockObject blockObj, StmtInfoPC *stmt)
{
setUnknownResult();
JS_ALWAYS_FALSE(abortIfSyntaxParser());
return SyntaxParseHandler::NodeFailure;
}
@ -3401,6 +3711,7 @@ Parser<FullParseHandler>::forStatement()
}
#if JS_HAS_BLOCK_SCOPE
else if (tt == TOK_LET) {
handler.disableSyntaxParser();
(void) tokenStream.getToken();
if (tokenStream.peekToken() == TOK_LP) {
pn1 = letBlock(LetExpresion);
@ -3695,6 +4006,12 @@ Parser<SyntaxParseHandler>::forStatement()
StmtInfoPC forStmt(context);
PushStatementPC(pc, &forStmt, STMT_FOR_LOOP);
/* Don't parse 'for each' loops. */
if (allowsForEachIn() && tokenStream.peekToken() == TOK_NAME) {
JS_ALWAYS_FALSE(abortIfSyntaxParser());
return null();
}
MUST_MATCH_TOKEN(TOK_LP, JSMSG_PAREN_AFTER_FOR);
/* True if we have 'for (var ...)'. */
@ -3718,7 +4035,7 @@ Parser<SyntaxParseHandler>::forStatement()
}
#if JS_HAS_BLOCK_SCOPE
else if (tt == TOK_CONST || tt == TOK_LET) {
setUnknownResult();
JS_ALWAYS_FALSE(abortIfSyntaxParser());
return null();
}
#endif
@ -3747,12 +4064,12 @@ Parser<SyntaxParseHandler>::forStatement()
lhsNode != SyntaxParseHandler::NodeName &&
lhsNode != SyntaxParseHandler::NodeLValue)
{
setUnknownResult();
JS_ALWAYS_FALSE(abortIfSyntaxParser());
return null();
}
if (!simpleForDecl) {
setUnknownResult();
JS_ALWAYS_FALSE(abortIfSyntaxParser());
return null();
}
@ -3961,6 +4278,9 @@ template <typename ParseHandler>
typename ParseHandler::Node
Parser<ParseHandler>::withStatement()
{
if (!abortIfSyntaxParser())
return null();
JS_ASSERT(tokenStream.isCurrentTokenType(TOK_WITH));
uint32_t begin = tokenStream.currentToken().pos.begin;
@ -4017,6 +4337,8 @@ template <>
ParseNode *
Parser<FullParseHandler>::letStatement()
{
handler.disableSyntaxParser();
ParseNode *pn;
do {
/* Check for a let statement or let expression. */
@ -4134,7 +4456,7 @@ template <>
SyntaxParseHandler::Node
Parser<SyntaxParseHandler>::letStatement()
{
setUnknownResult();
JS_ALWAYS_FALSE(abortIfSyntaxParser());
return SyntaxParseHandler::NodeFailure;
}
@ -4417,6 +4739,9 @@ Parser<ParseHandler>::statement()
break;
case TOK_CONST:
if (!abortIfSyntaxParser())
return null();
pn = variables(PNK_CONST);
if (!pn)
return null();
@ -4873,10 +5198,8 @@ bool
Parser<SyntaxParseHandler>::setAssignmentLhsOps(Node pn, JSOp op)
{
/* Full syntax checking of valid assignment LHS terms requires a parse tree. */
if (pn != SyntaxParseHandler::NodeName && pn != SyntaxParseHandler::NodeLValue) {
setUnknownResult();
return false;
}
if (pn != SyntaxParseHandler::NodeName && pn != SyntaxParseHandler::NodeLValue)
return abortIfSyntaxParser();
return checkStrictAssignment(pn);
}
@ -4919,6 +5242,8 @@ Parser<ParseHandler>::assignExpr()
case TOK_ARROW: {
tokenStream.seek(start);
if (!abortIfSyntaxParser())
return null();
if (tokenStream.getToken() == TOK_ERROR)
return null();
@ -5054,6 +5379,12 @@ Parser<SyntaxParseHandler>::checkDeleteExpression(Node *pn)
PropertyName *name = handler.isName(*pn);
if (name)
return report(ParseStrictError, pc->sc->strict, *pn, JSMSG_DEPRECATED_DELETE_OPERAND);
// Treat deletion of non-lvalues as ambiguous, so that any error associated
// with deleting a call expression is reported.
if (*pn != SyntaxParseHandler::NodeLValue && strictMode())
return abortIfSyntaxParser();
return true;
}
@ -5409,7 +5740,7 @@ CompExprTransplanter::transplant(ParseNode *pn)
* generator) a use of a new placeholder in the generator's
* lexdeps.
*/
Definition *dn2 = parser->handler.newPlaceholder(pn, parser->pc);
Definition *dn2 = parser->handler.newPlaceholder(atom, parser->pc);
if (!dn2)
return false;
dn2->pn_pos = root->pn_pos;
@ -5491,6 +5822,18 @@ Parser<FullParseHandler>::comprehensionTail(ParseNode *kid, unsigned blockid, bo
ParseContext<FullParseHandler> *outerpc,
ParseNodeKind kind, JSOp op)
{
/*
* If we saw any inner functions while processing the generator expression
* then they may have upvars referring to the let vars in this generator
* which were not correctly processed. Bail out and start over without
* allowing lazy parsing.
*/
if (handler.syntaxParser) {
handler.disableSyntaxParser();
abortedSyntaxParse = true;
return NULL;
}
unsigned adjust;
ParseNode *pn, *pn2, *pn3, **pnp;
StmtInfoPC stmtInfo(context);
@ -5746,8 +6089,7 @@ template <>
bool
Parser<SyntaxParseHandler>::arrayInitializerComprehensionTail(Node pn)
{
setUnknownResult();
return false;
return abortIfSyntaxParser();
}
#if JS_HAS_GENERATOR_EXPRS
@ -5853,7 +6195,7 @@ template <>
SyntaxParseHandler::Node
Parser<SyntaxParseHandler>::generatorExpr(Node kid)
{
setUnknownResult();
JS_ALWAYS_FALSE(abortIfSyntaxParser());
return SyntaxParseHandler::NodeFailure;
}

View File

@ -40,6 +40,7 @@ typedef HashSet<JSAtom *> FuncStmtSet;
class SharedContext;
typedef Vector<Definition *, 16> DeclVector;
typedef Vector<JSFunction *, 4> FunctionVector;
struct GenericParseContext
{
@ -203,6 +204,9 @@ struct ParseContext : public GenericParseContext
that will alias any top-level bindings with
the same name. */
// All inner functions in this context. Only filled in when parsing syntax.
FunctionVector innerFunctions;
// 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
@ -254,6 +258,7 @@ class GenexpGuard;
enum LetContext { LetExpresion, LetStatement };
enum VarContext { HoistVars, DontHoistVars };
enum FunctionType { Getter, Setter, Normal };
template <typename ParseHandler>
struct Parser : private AutoGCRooter, public StrictModeGetter
@ -301,7 +306,7 @@ struct Parser : private AutoGCRooter, public StrictModeGetter
* is not known whether the parse succeeds or fails, this bit is set and
* the parse will return false.
*/
bool unknownResult;
bool abortedSyntaxParse;
typedef typename ParseHandler::Node Node;
typedef typename ParseHandler::DefinitionNode DefinitionNode;
@ -319,7 +324,9 @@ struct Parser : private AutoGCRooter, public StrictModeGetter
...);
Parser(JSContext *cx, const CompileOptions &options,
const jschar *chars, size_t length, bool foldConstants);
const jschar *chars, size_t length, bool foldConstants,
Parser<SyntaxParseHandler> *syntaxParser,
LazyScript *lazyOuterFunction);
~Parser();
friend void AutoGCRooter::trace(JSTracer *trc);
@ -356,8 +363,11 @@ struct Parser : private AutoGCRooter, public StrictModeGetter
void trace(JSTracer *trc);
bool hadUnknownResult() {
return unknownResult;
bool hadAbortedSyntaxParse() {
return abortedSyntaxParse;
}
void clearAbortedSyntaxParse() {
abortedSyntaxParse = false;
}
private:
@ -365,13 +375,11 @@ struct Parser : private AutoGCRooter, public StrictModeGetter
/*
* Create a parse node with the given kind and op using the current token's
* atom.
*/
* atom.
*/
Node atomNode(ParseNodeKind kind, JSOp op);
void setUnknownResult() {
unknownResult = true;
}
inline bool abortIfSyntaxParser();
public:
@ -384,6 +392,10 @@ struct Parser : private AutoGCRooter, public StrictModeGetter
Node fn, FunctionBox **funbox, bool strict,
bool *becameStrict = NULL);
// Parse a function, given only its arguments and body. Used for lazily
// parsed functions.
Node standaloneLazyFunction(HandleFunction fun, unsigned staticLevel, bool strict);
/*
* Parse a function body. Pass StatementListBody if the body is a list of
* statements; pass ExpressionBody if the body is a single expression.
@ -391,6 +403,10 @@ struct Parser : private AutoGCRooter, public StrictModeGetter
enum FunctionBodyType { StatementListBody, ExpressionBody };
Node functionBody(FunctionSyntaxKind kind, FunctionBodyType type);
bool functionArgsAndBodyGeneric(Node pn, HandleFunction fun,
HandlePropertyName funName, FunctionType type,
FunctionSyntaxKind kind, bool strict, bool *becameStrict);
virtual bool strictMode() { return pc->sc->strict; }
private:
@ -439,7 +455,6 @@ struct Parser : private AutoGCRooter, public StrictModeGetter
/*
* Additional JS parsers.
*/
enum FunctionType { Getter, Setter, Normal };
bool functionArguments(FunctionSyntaxKind kind, Node *list, Node funcpn, bool &hasRest);
Node functionDef(HandlePropertyName name, const TokenStream::Position &start,
@ -478,10 +493,10 @@ struct Parser : private AutoGCRooter, public StrictModeGetter
void addStatementToList(Node pn, Node kid, bool *hasFunctionStmt);
bool checkFunctionArguments();
bool makeDefIntoUse(Definition *dn, Node pn, JSAtom *atom);
bool checkFunctionDefinition(HandlePropertyName funName, Node *pn, FunctionSyntaxKind kind);
bool finishFunctionDefinition(Node pn, FunctionBox *funbox,
Node prelude, Node body,
ParseContext<ParseHandler> *outerpc);
bool checkFunctionDefinition(HandlePropertyName funName, Node *pn, FunctionSyntaxKind kind,
bool *pbodyProcessed);
bool finishFunctionDefinition(Node pn, FunctionBox *funbox, Node prelude, Node body);
bool addFreeVariablesFromLazyFunction(JSFunction *fun, ParseContext<ParseHandler> *pc);
bool isValidForStatementLHS(Node pn1, JSVersion version,
bool forDecl, bool forEach, bool forOf);
@ -523,6 +538,7 @@ struct Parser : private AutoGCRooter, public StrictModeGetter
bool reportRedeclaration(Node pn, bool isConst, JSAtom *atom);
bool reportBadReturn(Node pn, ParseReportKind kind, unsigned errnum, unsigned anonerrnum);
bool checkFinalReturn(Node pn);
DefinitionNode getOrCreateLexicalDependency(ParseContext<ParseHandler> *pc, JSAtom *atom);
bool leaveFunction(Node fn, HandlePropertyName funName,
ParseContext<ParseHandler> *outerpc,

View File

@ -17,6 +17,7 @@
#include "builtin/Module.h"
#include "frontend/ParseMaps.h"
#include "frontend/ParseNode.h"
#include "frontend/TokenStream.h"
#include "vm/ScopeObject.h"
namespace js {
@ -200,6 +201,8 @@ class FunctionBox : public ObjectBox, public SharedContext
Bindings bindings; /* bindings for this function */
uint32_t bufStart;
uint32_t bufEnd;
uint32_t startLine;
uint32_t startColumn;
uint32_t asmStart; /* offset of the "use asm" directive, if present */
uint16_t ndefaults;
bool inWith:1; /* some enclosing scope is a with-statement */
@ -234,6 +237,12 @@ class FunctionBox : public ObjectBox, public SharedContext
bool useAsmOrInsideUseAsm() const {
return useAsm || insideUseAsm;
}
void setStart(const TokenStream &tokenStream) {
bufStart = tokenStream.currentToken().pos.begin;
startLine = tokenStream.getLineno();
startColumn = tokenStream.getColumn();
}
};
inline FunctionBox *

View File

@ -10,9 +10,19 @@
namespace js {
namespace frontend {
// Parse handler used when processing the syntax in a block of code, to generate
// the minimal information which is required to detect syntax errors and allow
// bytecode to be emitted for outer functions.
//
// When parsing, we start at the top level with a full parse, and when possible
// only check the syntax for inner functions, so that they can be lazily parsed
// into bytecode when/if they first run. Checking the syntax of a function is
// several times faster than doing a full parse/emit, and lazy parsing improves
// both performance and memory usage significantly when pages contain large
// amounts of code that never executes (which happens often).
class SyntaxParseHandler
{
/* Remember the last encountered name or string literal during syntax parses. */
// Remember the last encountered name or string literal during syntax parses.
JSAtom *lastAtom;
TokenPos lastStringPos;
TokenStream &tokenStream;
@ -28,7 +38,8 @@ class SyntaxParseHandler
};
typedef Definition::Kind DefinitionNode;
SyntaxParseHandler(JSContext *cx, TokenStream &tokenStream, bool foldConstants)
SyntaxParseHandler(JSContext *cx, TokenStream &tokenStream, bool foldConstants,
Parser<SyntaxParseHandler> *syntaxParser, LazyScript *lazyOuterFunction)
: lastAtom(NULL),
tokenStream(tokenStream)
{}
@ -42,7 +53,7 @@ class SyntaxParseHandler
lastAtom = name;
return NodeName;
}
DefinitionNode newPlaceholder(Node pn, ParseContext<SyntaxParseHandler> *pc) {
DefinitionNode newPlaceholder(JSAtom *atom, ParseContext<SyntaxParseHandler> *pc) {
return Definition::PLACEHOLDER;
}
Node newAtom(ParseNodeKind kind, JSAtom *atom, JSOp op = JSOP_NOP) {
@ -182,6 +193,8 @@ class SyntaxParseHandler
static DefinitionNode nullDefinition() {
return Definition::MISSING;
}
void disableSyntaxParser() {
}
};
} // namespace frontend

View File

@ -158,6 +158,22 @@ TokenStream::SourceCoords::add(uint32_t lineNum, uint32_t lineStartOffset)
}
}
JS_ALWAYS_INLINE void
TokenStream::SourceCoords::fill(const TokenStream::SourceCoords &other)
{
JS_ASSERT(lineStartOffsets_.back() == MAX_PTR);
JS_ASSERT(other.lineStartOffsets_.back() == MAX_PTR);
if (lineStartOffsets_.length() >= other.lineStartOffsets_.length())
return;
uint32_t sentinelIndex = lineStartOffsets_.length() - 1;
lineStartOffsets_[sentinelIndex] = other.lineStartOffsets_[sentinelIndex];
for (size_t i = sentinelIndex + 1; i < other.lineStartOffsets_.length(); i++)
(void)lineStartOffsets_.append(other.lineStartOffsets_[i]);
}
JS_ALWAYS_INLINE uint32_t
TokenStream::SourceCoords::lineIndexOf(uint32_t offset) const
{
@ -251,9 +267,9 @@ TokenStream::TokenStream(JSContext *cx, const CompileOptions &options,
lookahead(),
lineno(options.lineno),
flags(),
linebase(base),
linebase(base - options.column),
prevLinebase(NULL),
userbuf(cx, base, length),
userbuf(cx, base - options.column, length + options.column), // See comment below
filename(options.filename),
sourceMap(NULL),
listenerTSData(),
@ -268,6 +284,12 @@ TokenStream::TokenStream(JSContext *cx, const CompileOptions &options,
linebaseSkip(cx, &linebase),
prevLinebaseSkip(cx, &prevLinebase)
{
// Column numbers are computed as offsets from the current line's base, so the
// initial line's base must be included in the buffer. linebase and userbuf
// were adjusted above, and if we are starting tokenization part way through
// this line then adjust the next character.
userbuf.setAddressOfNextRawChar(base);
if (originPrincipals)
JS_HoldPrincipals(originPrincipals);
@ -501,6 +523,19 @@ TokenStream::TokenBuf::findEOLMax(const jschar *p, size_t max)
return p;
}
void
TokenStream::advance(size_t position)
{
const jschar *end = userbuf.base() + position;
while (userbuf.addressOfNextRawChar() < end)
getChar();
Token *cur = &tokens[cursor];
cur->pos.begin = userbuf.addressOfNextRawChar() - userbuf.base();
cur->type = TOK_ERROR;
lookahead = 0;
}
void
TokenStream::tell(Position *pos)
{
@ -530,6 +565,13 @@ TokenStream::seek(const Position &pos)
tokens[(cursor + 1 + i) & ntokensMask] = pos.lookaheadTokens[i];
}
void
TokenStream::seek(const Position &pos, const TokenStream &other)
{
srcCoords.fill(other.srcCoords);
seek(pos);
}
void
TokenStream::positionAfterLastFunctionKeyword(Position &pos)
{

View File

@ -472,6 +472,7 @@ class MOZ_STACK_CLASS TokenStream
const CharBuffer &getTokenbuf() const { return tokenbuf; }
const char *getFilename() const { return filename; }
unsigned getLineno() const { return lineno; }
unsigned getColumn() const { return userbuf.addressOfNextRawChar() - linebase - 1; }
JSVersion versionNumber() const { return VersionNumber(version); }
JSVersion versionWithFlags() const { return version; }
bool hadError() const { return !!(flags & TSF_HAD_ERROR); }
@ -660,8 +661,10 @@ class MOZ_STACK_CLASS TokenStream
Token lookaheadTokens[maxLookahead];
};
void advance(size_t position);
void tell(Position *);
void seek(const Position &pos);
void seek(const Position &pos, const TokenStream &other);
void positionAfterLastFunctionKeyword(Position &pos);
size_t positionToOffset(const Position &pos) const {
@ -752,6 +755,7 @@ class MOZ_STACK_CLASS TokenStream
SourceCoords(JSContext *cx, uint32_t ln);
void add(uint32_t lineNum, uint32_t lineStartOffset);
void fill(const SourceCoords &other);
bool isOnThisLine(uint32_t offset, uint32_t lineNum) const {
uint32_t lineIndex = lineNumToIndex(lineNum);
@ -828,7 +832,7 @@ class MOZ_STACK_CLASS TokenStream
ptr--;
}
const jschar *addressOfNextRawChar() {
const jschar *addressOfNextRawChar() const {
JS_ASSERT(ptr); /* make sure haven't been poisoned */
return ptr;
}

View File

@ -62,6 +62,7 @@ enum AllocKind {
FINALIZE_OBJECT16_BACKGROUND,
FINALIZE_OBJECT_LAST = FINALIZE_OBJECT16_BACKGROUND,
FINALIZE_SCRIPT,
FINALIZE_LAZY_SCRIPT,
FINALIZE_SHAPE,
FINALIZE_BASE_SHAPE,
FINALIZE_TYPE_OBJECT,

View File

@ -81,6 +81,7 @@ namespace gc {
static void MarkChildren(JSTracer *trc, JSString *str);
static void MarkChildren(JSTracer *trc, JSScript *script);
static void MarkChildren(JSTracer *trc, LazyScript *lazy);
static void MarkChildren(JSTracer *trc, Shape *shape);
static void MarkChildren(JSTracer *trc, BaseShape *base);
static void MarkChildren(JSTracer *trc, types::TypeObject *type);
@ -360,6 +361,7 @@ DeclMarkerImpl(Object, JSObject)
DeclMarkerImpl(Object, JSFunction)
DeclMarkerImpl(Object, ScopeObject)
DeclMarkerImpl(Script, JSScript)
DeclMarkerImpl(LazyScript, LazyScript)
DeclMarkerImpl(Shape, Shape)
DeclMarkerImpl(String, JSAtom)
DeclMarkerImpl(String, JSString)
@ -390,6 +392,9 @@ gc::MarkKind(JSTracer *trc, void **thingp, JSGCTraceKind kind)
case JSTRACE_SCRIPT:
MarkInternal(trc, reinterpret_cast<JSScript **>(thingp));
break;
case JSTRACE_LAZY_SCRIPT:
MarkInternal(trc, reinterpret_cast<LazyScript **>(thingp));
break;
case JSTRACE_SHAPE:
MarkInternal(trc, reinterpret_cast<Shape **>(thingp));
break;
@ -792,6 +797,20 @@ PushMarkStack(GCMarker *gcmarker, JSScript *thing)
MarkChildren(gcmarker, thing);
}
static void
PushMarkStack(GCMarker *gcmarker, LazyScript *thing)
{
JS_COMPARTMENT_ASSERT(gcmarker->runtime, thing);
JS_ASSERT(!IsInsideNursery(thing->runtime(), thing));
/*
* We mark lazy scripts directly rather than pushing on the stack as they
* only refer to normal scripts and to strings, and cannot recurse.
*/
if (thing->markIfUnmarked(gcmarker->getMarkColor()))
MarkChildren(gcmarker, thing);
}
static void
ScanShape(GCMarker *gcmarker, Shape *shape);
@ -997,6 +1016,12 @@ gc::MarkChildren(JSTracer *trc, JSScript *script)
script->markChildren(trc);
}
static void
gc::MarkChildren(JSTracer *trc, LazyScript *lazy)
{
lazy->markChildren(trc);
}
static void
gc::MarkChildren(JSTracer *trc, Shape *shape)
{
@ -1153,6 +1178,10 @@ gc::PushArena(GCMarker *gcmarker, ArenaHeader *aheader)
PushArenaTyped<JSScript>(gcmarker, aheader);
break;
case JSTRACE_LAZY_SCRIPT:
PushArenaTyped<LazyScript>(gcmarker, aheader);
break;
case JSTRACE_SHAPE:
PushArenaTyped<js::Shape>(gcmarker, aheader);
break;
@ -1501,6 +1530,10 @@ js::TraceChildren(JSTracer *trc, void *thing, JSGCTraceKind kind)
MarkChildren(trc, static_cast<JSScript *>(thing));
break;
case JSTRACE_LAZY_SCRIPT:
MarkChildren(trc, static_cast<LazyScript *>(thing));
break;
case JSTRACE_SHAPE:
MarkChildren(trc, static_cast<Shape *>(thing));
break;

View File

@ -101,6 +101,7 @@ DeclMarker(Object, JSObject)
DeclMarker(Object, JSFunction)
DeclMarker(Object, ScopeObject)
DeclMarker(Script, JSScript)
DeclMarker(LazyScript, LazyScript)
DeclMarker(Shape, Shape)
DeclMarker(String, JSAtom)
DeclMarker(String, JSString)
@ -396,6 +397,12 @@ TraceKind(JSScript *script)
return JSTRACE_SCRIPT;
}
inline JSGCTraceKind
TraceKind(LazyScript *lazy)
{
return JSTRACE_LAZY_SCRIPT;
}
} /* namespace gc */
void

View File

@ -1550,7 +1550,7 @@ CodeGenerator::visitCallGeneric(LCallGeneric *call)
masm.branchIfFunctionHasNoScript(calleereg, &uncompiled);
// Knowing that calleereg is a non-native function, load the JSScript.
masm.loadPtr(Address(calleereg, offsetof(JSFunction, u.i.script_)), objreg);
masm.loadPtr(Address(calleereg, JSFunction::offsetOfNativeOrScript()), objreg);
// Load script jitcode.
masm.loadBaselineOrIonRaw(objreg, objreg, executionMode, &uncompiled);
@ -1685,7 +1685,7 @@ CodeGenerator::visitCallKnown(LCallKnown *call)
}
// Knowing that calleereg is a non-native function, load the JSScript.
masm.loadPtr(Address(calleereg, offsetof(JSFunction, u.i.script_)), objreg);
masm.loadPtr(Address(calleereg, JSFunction::offsetOfNativeOrScript()), objreg);
// Load script jitcode.
if (call->mir()->needsArgCheck())
@ -1894,7 +1894,7 @@ CodeGenerator::visitApplyArgsGeneric(LApplyArgsGeneric *apply)
}
// Knowing that calleereg is a non-native function, load the JSScript.
masm.loadPtr(Address(calleereg, offsetof(JSFunction, u.i.script_)), objreg);
masm.loadPtr(Address(calleereg, JSFunction::offsetOfNativeOrScript()), objreg);
// Load script jitcode.
masm.loadBaselineOrIonRaw(objreg, objreg, executionMode, &invoke);

View File

@ -7965,6 +7965,9 @@ IonBuilder::jsop_object(JSObject *obj)
bool
IonBuilder::jsop_lambda(JSFunction *fun)
{
if (fun->isInterpreted() && !fun->getOrCreateScript(cx))
return false;
JS_ASSERT(script()->analysis()->usesScopeChain());
if (fun->isArrow())
return abort("bound arrow function");

View File

@ -416,7 +416,7 @@ IonRuntime::generateArgumentsRectifier(JSContext *cx, ExecutionMode mode, void *
// Call the target function.
// Note that this code assumes the function is JITted.
masm.ma_ldr(DTRAddr(r1, DtrOffImm(offsetof(JSFunction, u.i.script_))), r3);
masm.ma_ldr(DTRAddr(r1, DtrOffImm(JSFunction::offsetOfNativeOrScript())), r3);
masm.loadBaselineOrIonRaw(r3, r3, mode, NULL);
masm.ma_callIonHalfPush(r3);

View File

@ -388,7 +388,7 @@ IonRuntime::generateArgumentsRectifier(JSContext *cx, ExecutionMode mode, void *
// Call the target function.
// Note that this code assumes the function is JITted.
masm.movq(Operand(rax, offsetof(JSFunction, u.i.script_)), rax);
masm.movq(Operand(rax, JSFunction::offsetOfNativeOrScript()), rax);
masm.loadBaselineOrIonRaw(rax, rax, mode, NULL);
masm.call(rax);
uint32_t returnOffset = masm.currentOffset();

View File

@ -379,7 +379,7 @@ IonRuntime::generateArgumentsRectifier(JSContext *cx, ExecutionMode mode, void *
// Call the target function.
// Note that this assumes the function is JITted.
masm.movl(Operand(eax, offsetof(JSFunction, u.i.script_)), eax);
masm.movl(Operand(eax, JSFunction::offsetOfNativeOrScript()), eax);
masm.loadBaselineOrIonRaw(eax, eax, mode, NULL);
masm.call(eax);
uint32_t returnOffset = masm.currentOffset();

View File

@ -2597,6 +2597,10 @@ JS_GetTraceThingInfo(char *buf, size_t bufsize, JSTracer *trc, void *thing,
name = "script";
break;
case JSTRACE_LAZY_SCRIPT:
name = "lazyscript";
break;
case JSTRACE_IONCODE:
name = "ioncode";
break;
@ -2668,6 +2672,7 @@ JS_GetTraceThingInfo(char *buf, size_t bufsize, JSTracer *trc, void *thing,
break;
}
case JSTRACE_LAZY_SCRIPT:
case JSTRACE_IONCODE:
case JSTRACE_SHAPE:
case JSTRACE_BASE_SHAPE:
@ -4919,6 +4924,11 @@ JS_CloneFunctionObject(JSContext *cx, JSObject *funobjArg, JSObject *parentArg)
* script, we cannot clone it without breaking the compiler's assumptions.
*/
RootedFunction fun(cx, funobj->toFunction());
if (fun->isInterpretedLazy()) {
AutoCompartment ac(cx, funobj);
if (!fun->getOrCreateScript(cx))
return NULL;
}
if (fun->isInterpreted() && (fun->nonLazyScript()->enclosingStaticScope() ||
(fun->nonLazyScript()->compileAndGo && !parent->isGlobal())))
{
@ -5282,10 +5292,12 @@ JS::CompileOptions::CompileOptions(JSContext *cx)
utf8(false),
filename(NULL),
lineno(1),
column(0),
compileAndGo(cx->hasOption(JSOPTION_COMPILE_N_GO)),
forEval(false),
noScriptRval(cx->hasOption(JSOPTION_NO_SCRIPT_RVAL)),
selfHostingMode(false),
canLazilyParse(true),
sourcePolicy(SAVE_SOURCE)
{
}
@ -5424,7 +5436,7 @@ JS_BufferIsCompilableUnit(JSContext *cx, JSObject *objArg, const char *utf8, siz
CompileOptions options(cx);
options.setCompileAndGo(false);
Parser<frontend::FullParseHandler> parser(cx, options, chars, length,
/* foldConstants = */ true);
/* foldConstants = */ true, NULL, NULL);
if (parser.init()) {
older = JS_SetErrorReporter(cx, NULL);
if (!parser.parse(obj) &&

View File

@ -3894,10 +3894,12 @@ struct JS_PUBLIC_API(CompileOptions) {
bool utf8;
const char *filename;
unsigned lineno;
unsigned column;
bool compileAndGo;
bool forEval;
bool noScriptRval;
bool selfHostingMode;
bool canLazilyParse;
enum SourcePolicy {
NO_SOURCE,
LAZY_SOURCE,
@ -3912,10 +3914,12 @@ struct JS_PUBLIC_API(CompileOptions) {
CompileOptions &setFileAndLine(const char *f, unsigned l) {
filename = f; lineno = l; return *this;
}
CompileOptions &setColumn(unsigned c) { column = c; return *this; }
CompileOptions &setCompileAndGo(bool cng) { compileAndGo = cng; return *this; }
CompileOptions &setForEval(bool eval) { forEval = eval; return *this; }
CompileOptions &setNoScriptRval(bool nsr) { noScriptRval = nsr; return *this; }
CompileOptions &setSelfHostingMode(bool shm) { selfHostingMode = shm; return *this; }
CompileOptions &setCanLazilyParse(bool clp) { canLazilyParse = clp; return *this; }
CompileOptions &setSourcePolicy(SourcePolicy sp) { sourcePolicy = sp; return *this; }
};

View File

@ -921,6 +921,8 @@ JS::IncrementalReferenceBarrier(void *ptr, JSGCTraceKind kind)
JSString::writeBarrierPre(static_cast<JSString*>(cell));
else if (kind == JSTRACE_SCRIPT)
JSScript::writeBarrierPre(static_cast<JSScript*>(cell));
else if (kind == JSTRACE_LAZY_SCRIPT)
LazyScript::writeBarrierPre(static_cast<LazyScript*>(cell));
else if (kind == JSTRACE_SHAPE)
Shape::writeBarrierPre(static_cast<Shape*>(cell));
else if (kind == JSTRACE_BASE_SHAPE)

View File

@ -491,8 +491,13 @@ JSFunction::trace(JSTracer *trc)
MarkString(trc, &atom_, "atom");
if (isInterpreted()) {
if (hasScript())
MarkScriptUnbarriered(trc, &u.i.script_, "script");
// Functions can be be marked as interpreted despite having no script
// yet at some points when parsing, and can be lazy with no lazy script
// for self hosted code.
if (hasScript() && u.i.s.script_)
MarkScriptUnbarriered(trc, &u.i.s.script_, "script");
else if (isInterpretedLazy() && u.i.s.lazy_)
MarkLazyScriptUnbarriered(trc, &u.i.s.lazy_, "lazyScript");
if (u.i.env_)
MarkObjectUnbarriered(trc, &u.i.env_, "fun_callscope");
}
@ -584,8 +589,8 @@ FindBody(JSContext *cx, HandleFunction fun, StableCharPtr chars, size_t length,
JSString *
js::FunctionToString(JSContext *cx, HandleFunction fun, bool bodyOnly, bool lambdaParen)
{
StringBuffer out(cx);
RootedScript script(cx);
if (fun->isInterpretedLazy() && !fun->getOrCreateScript(cx))
return NULL;
// If the object is an automatically-bound arrow function, get the source
// of the pre-binding target.
@ -596,6 +601,9 @@ js::FunctionToString(JSContext *cx, HandleFunction fun, bool bodyOnly, bool lamb
return FunctionToString(cx, targetFun, bodyOnly, lambdaParen);
}
StringBuffer out(cx);
RootedScript script(cx);
if (fun->hasScript()) {
script = fun->nonLazyScript();
if (script->isGeneratorExp) {
@ -1061,17 +1069,65 @@ JSFunction::getBoundFunctionArgumentCount() const
return getSlot(JSSLOT_BOUND_FUNCTION_ARGS_COUNT).toPrivateUint32();
}
bool
JSFunction::initializeLazyScript(JSContext *cx)
/* static */ bool
JSFunction::createScriptForLazilyInterpretedFunction(JSContext *cx, HandleFunction fun)
{
JS_ASSERT(isInterpretedLazy());
const JSFunctionSpec *fs = static_cast<JSFunctionSpec *>(getExtendedSlot(0).toPrivate());
Rooted<JSFunction*> self(cx, this);
JS_ASSERT(fun->isInterpretedLazy());
if (LazyScript *lazy = fun->lazyScriptOrNull()) {
/* Trigger a pre barrier on the lazy script being overwritten. */
if (cx->zone()->needsBarrier())
LazyScript::writeBarrierPre(lazy);
if (JSScript *script = lazy->maybeScript()) {
fun->flags &= ~INTERPRETED_LAZY;
fun->flags |= INTERPRETED;
fun->initScript(script);
/*
* Set some bits that are normally filled in by the Parser after
* the full parse tree has been produced.
*/
if (script->function()->isHeavyweight())
fun->setIsHeavyweight();
fun->nargs = script->function()->nargs;
return true;
}
/* Lazily parsed script. */
const jschar *chars = lazy->source()->chars(cx);
if (!chars)
return false;
/*
* GC must be suppressed for the remainder of the lazy parse, as any
* GC activity may destroy the characters.
*/
AutoSuppressGC suppressGC(cx);
fun->flags &= ~INTERPRETED_LAZY;
fun->flags |= INTERPRETED;
fun->initScript(NULL);
const jschar *lazyStart = chars + lazy->begin();
size_t lazyLength = lazy->end() - lazy->begin();
if (!frontend::CompileLazyFunction(cx, fun, lazy, lazyStart, lazyLength)) {
fun->initLazyScript(lazy);
return false;
}
lazy->initScript(fun->nonLazyScript());
return true;
}
/* Lazily cloned self hosted script. */
JSFunctionSpec *fs = static_cast<JSFunctionSpec *>(fun->getExtendedSlot(0).toPrivate());
RootedAtom funAtom(cx, Atomize(cx, fs->selfHostedName, strlen(fs->selfHostedName)));
if (!funAtom)
return false;
Rooted<PropertyName *> funName(cx, funAtom->asPropertyName());
return cx->runtime->cloneSelfHostedFunctionScript(cx, funName, self);
return cx->runtime->cloneSelfHostedFunctionScript(cx, funName, fun);
}
/* ES5 15.3.4.5.1 and 15.3.4.5.2. */
@ -1504,6 +1560,10 @@ js::CloneFunctionObject(JSContext *cx, HandleFunction fun, HandleObject parent,
bool useSameScript = cx->compartment == fun->compartment() &&
!fun->hasSingletonType() &&
!types::UseNewTypeForClone(fun);
if (!useSameScript && fun->isInterpretedLazy() && !fun->getOrCreateScript(cx))
return NULL;
NewObjectKind newKind = useSameScript ? newKindArg : SingletonObject;
JSObject *cloneobj = NewObjectWithClassProto(cx, &FunctionClass, NULL, SkipScopeParent(parent),
allocKind, newKind);
@ -1513,16 +1573,13 @@ js::CloneFunctionObject(JSContext *cx, HandleFunction fun, HandleObject parent,
clone->nargs = fun->nargs;
clone->flags = fun->flags & ~JSFunction::EXTENDED;
if (fun->isInterpreted()) {
if (fun->isInterpretedLazy()) {
RootedFunction cloneRoot(cx, clone);
AutoCompartment ac(cx, fun);
if (!fun->getOrCreateScript(cx))
return NULL;
clone = cloneRoot;
}
if (fun->hasScript()) {
clone->initScript(fun->nonLazyScript());
clone->initEnvironment(parent);
} else if (fun->isInterpretedLazy()) {
LazyScript *lazy = fun->lazyScriptOrNull();
clone->initLazyScript(lazy);
clone->initEnvironment(parent);
} else {
clone->initNative(fun->native(), fun->jitInfo());
}

View File

@ -68,8 +68,11 @@ class JSFunction : public JSObject
use the accessor! */
} n;
struct Scripted {
JSScript *script_; /* interpreted bytecode descriptor or null;
use the accessor! */
union {
JSScript *script_; /* interpreted bytecode descriptor or null;
use the accessor! */
js::LazyScript *lazy_; /* lazily compiled script, or NULL */
} s;
JSObject *env_; /* environment for new activations;
use the accessor! */
} i;
@ -80,6 +83,12 @@ class JSFunction : public JSObject
public:
bool isHeavyweight() {
/* The heavyweight flag is not set until the script is parsed. */
JS_ASSERT(!isInterpretedLazy());
return flags & HEAVYWEIGHT;
}
/* A function can be classified as either native (C++) or interpreted (JS): */
bool isInterpreted() const { return flags & (INTERPRETED | INTERPRETED_LAZY); }
bool isNative() const { return !isInterpreted(); }
@ -88,10 +97,9 @@ class JSFunction : public JSObject
bool isNativeConstructor() const { return flags & NATIVE_CTOR; }
/* Possible attributes of an interpreted function: */
bool isHeavyweight() const { return flags & HEAVYWEIGHT; }
bool isFunctionPrototype() const { return flags & IS_FUN_PROTO; }
bool isInterpretedLazy() const { return flags & INTERPRETED_LAZY; }
bool hasScript() const { return isInterpreted() && u.i.script_; }
bool hasScript() const { return flags & INTERPRETED; }
bool isExprClosure() const { return flags & EXPR_CLOSURE; }
bool hasGuessedAtom() const { return flags & HAS_GUESSED_ATOM; }
bool isLambda() const { return flags & LAMBDA; }
@ -206,7 +214,7 @@ class JSFunction : public JSObject
static inline size_t offsetOfEnvironment() { return offsetof(JSFunction, u.i.env_); }
static inline size_t offsetOfAtom() { return offsetof(JSFunction, atom_); }
bool initializeLazyScript(JSContext *cx);
static bool createScriptForLazilyInterpretedFunction(JSContext *cx, js::HandleFunction fun);
JSScript *getOrCreateScript(JSContext *cx) {
JS_ASSERT(isInterpreted());
@ -214,12 +222,13 @@ class JSFunction : public JSObject
if (isInterpretedLazy()) {
JS::RootedFunction self(cx, this);
js::MaybeCheckStackRoots(cx);
if (!self->initializeLazyScript(cx))
if (!createScriptForLazilyInterpretedFunction(cx, self))
return NULL;
return self->u.i.script_;
JS_ASSERT(self->hasScript());
return self->u.i.s.script_;
}
JS_ASSERT(hasScript());
return u.i.script_;
return u.i.s.script_;
}
static bool maybeGetOrCreateScript(JSContext *cx, js::HandleFunction fun,
@ -235,20 +244,35 @@ class JSFunction : public JSObject
JSScript *nonLazyScript() const {
JS_ASSERT(hasScript());
return JS::HandleScript::fromMarkedLocation(&u.i.script_);
return JS::HandleScript::fromMarkedLocation(&u.i.s.script_);
}
JSScript *maybeNonLazyScript() const {
return isInterpreted() ? nonLazyScript() : NULL;
return hasScript() ? nonLazyScript() : NULL;
}
js::HeapPtrScript &mutableScript() {
JS_ASSERT(isInterpreted());
return *(js::HeapPtrScript *)&u.i.script_;
return *(js::HeapPtrScript *)&u.i.s.script_;
}
// A lazily interpreted function will have an associated LazyScript if the
// script has not yet been parsed. For functions whose scripts are lazily
// cloned from self hosted code, there is no LazyScript.
js::LazyScript *lazyScript() const {
JS_ASSERT(isInterpretedLazy() && u.i.s.lazy_);
return u.i.s.lazy_;
}
js::LazyScript *lazyScriptOrNull() const {
JS_ASSERT(isInterpretedLazy());
return u.i.s.lazy_;
}
inline void setScript(JSScript *script_);
inline void initScript(JSScript *script_);
inline void initLazyScript(js::LazyScript *script);
JSNative native() const {
JS_ASSERT(isNative());
@ -264,7 +288,7 @@ class JSFunction : public JSObject
inline void setJitInfo(const JSJitInfo *data);
static unsigned offsetOfNativeOrScript() {
JS_STATIC_ASSERT(offsetof(U, n.native) == offsetof(U, i.script_));
JS_STATIC_ASSERT(offsetof(U, n.native) == offsetof(U, i.s.script_));
JS_STATIC_ASSERT(offsetof(U, n.native) == offsetof(U, nativeOrScript));
return offsetof(JSFunction, u.nativeOrScript);
}

View File

@ -168,13 +168,32 @@ SkipScopeParent(JSObject *parent)
return parent;
}
inline bool
CanReuseFunctionForClone(JSContext *cx, HandleFunction fun)
{
if (!fun->hasSingletonType())
return false;
if (fun->isInterpretedLazy()) {
LazyScript *lazy = fun->lazyScript();
if (lazy->hasBeenCloned())
return false;
lazy->setHasBeenCloned();
} else {
JSScript *script = fun->nonLazyScript();
if (script->hasBeenCloned)
return false;
script->hasBeenCloned = true;
}
return true;
}
inline JSFunction *
CloneFunctionObjectIfNotSingleton(JSContext *cx, HandleFunction fun, HandleObject parent,
NewObjectKind newKind = GenericObject)
{
/*
* For attempts to clone functions at a function definition opcode,
* don't perform the clone if the function has singleton type. This
* try to avoid the the clone if the function has singleton type. This
* was called pessimistically, and we need to preserve the type's
* property that if it is singleton there is only a single object
* with its type in existence.
@ -184,16 +203,12 @@ CloneFunctionObjectIfNotSingleton(JSContext *cx, HandleFunction fun, HandleObjec
* cases, fall through to CloneFunctionObject, which will deep clone
* the function's script.
*/
if (fun->hasSingletonType()) {
RootedScript script(cx, fun->getOrCreateScript(cx));
if (!script->hasBeenCloned) {
script->hasBeenCloned = true;
Rooted<JSObject*> obj(cx, SkipScopeParent(parent));
if (!JSObject::setParent(cx, fun, obj))
return NULL;
fun->setEnvironment(parent);
return fun;
}
if (CanReuseFunctionForClone(cx, fun)) {
RootedObject obj(cx, SkipScopeParent(parent));
if (!JSObject::setParent(cx, fun, obj))
return NULL;
fun->setEnvironment(parent);
return fun;
}
// These intermediate variables are needed to avoid link errors on some
@ -222,6 +237,17 @@ JSFunction::initScript(JSScript *script_)
mutableScript().init(script_);
}
inline void
JSFunction::initLazyScript(js::LazyScript *lazy)
{
JS_ASSERT(isInterpreted());
flags &= ~INTERPRETED;
flags |= INTERPRETED_LAZY;
u.i.s.lazy_ = lazy;
}
inline JSObject *
JSFunction::getBoundFunctionTarget() const
{

View File

@ -121,6 +121,7 @@ const uint32_t Arena::ThingSizes[] = {
sizeof(JSObject_Slots16), /* FINALIZE_OBJECT16 */
sizeof(JSObject_Slots16), /* FINALIZE_OBJECT16_BACKGROUND */
sizeof(JSScript), /* FINALIZE_SCRIPT */
sizeof(LazyScript), /* FINALIZE_LAZY_SCRIPT */
sizeof(Shape), /* FINALIZE_SHAPE */
sizeof(BaseShape), /* FINALIZE_BASE_SHAPE */
sizeof(types::TypeObject), /* FINALIZE_TYPE_OBJECT */
@ -146,6 +147,7 @@ const uint32_t Arena::FirstThingOffsets[] = {
OFFSET(JSObject_Slots16), /* FINALIZE_OBJECT16 */
OFFSET(JSObject_Slots16), /* FINALIZE_OBJECT16_BACKGROUND */
OFFSET(JSScript), /* FINALIZE_SCRIPT */
OFFSET(LazyScript), /* FINALIZE_LAZY_SCRIPT */
OFFSET(Shape), /* FINALIZE_SHAPE */
OFFSET(BaseShape), /* FINALIZE_BASE_SHAPE */
OFFSET(types::TypeObject), /* FINALIZE_TYPE_OBJECT */
@ -211,6 +213,7 @@ static const AllocKind BackgroundPhaseStrings[] = {
};
static const AllocKind BackgroundPhaseShapes[] = {
FINALIZE_LAZY_SCRIPT,
FINALIZE_SHAPE,
FINALIZE_BASE_SHAPE,
FINALIZE_TYPE_OBJECT
@ -427,6 +430,8 @@ FinalizeArenas(FreeOp *fop,
return FinalizeTypedArenas<JSObject>(fop, src, dest, thingKind, budget);
case FINALIZE_SCRIPT:
return FinalizeTypedArenas<JSScript>(fop, src, dest, thingKind, budget);
case FINALIZE_LAZY_SCRIPT:
return FinalizeTypedArenas<LazyScript>(fop, src, dest, thingKind, budget);
case FINALIZE_SHAPE:
return FinalizeTypedArenas<Shape>(fop, src, dest, thingKind, budget);
case FINALIZE_BASE_SHAPE:
@ -1435,6 +1440,7 @@ ArenaLists::queueScriptsForSweep(FreeOp *fop)
{
gcstats::AutoPhase ap(fop->runtime()->gcStats, gcstats::PHASE_SWEEP_SCRIPT);
queueForForegroundSweep(fop, FINALIZE_SCRIPT);
queueForBackgroundSweep(fop, FINALIZE_LAZY_SCRIPT);
}
void

View File

@ -119,6 +119,7 @@ MapAllocToTraceKind(AllocKind kind)
JSTRACE_OBJECT, /* FINALIZE_OBJECT16 */
JSTRACE_OBJECT, /* FINALIZE_OBJECT16_BACKGROUND */
JSTRACE_SCRIPT, /* FINALIZE_SCRIPT */
JSTRACE_LAZY_SCRIPT,/* FINALIZE_LAZY_SCRIPT */
JSTRACE_SHAPE, /* FINALIZE_SHAPE */
JSTRACE_BASE_SHAPE, /* FINALIZE_BASE_SHAPE */
JSTRACE_TYPE_OBJECT,/* FINALIZE_TYPE_OBJECT */
@ -140,6 +141,7 @@ template <> struct MapTypeToTraceKind<DebugScopeObject> { const static JSGCTrace
template <> struct MapTypeToTraceKind<GlobalObject> { const static JSGCTraceKind kind = JSTRACE_OBJECT; };
template <> struct MapTypeToTraceKind<ScopeObject> { const static JSGCTraceKind kind = JSTRACE_OBJECT; };
template <> struct MapTypeToTraceKind<JSScript> { const static JSGCTraceKind kind = JSTRACE_SCRIPT; };
template <> struct MapTypeToTraceKind<LazyScript> { const static JSGCTraceKind kind = JSTRACE_LAZY_SCRIPT; };
template <> struct MapTypeToTraceKind<Shape> { const static JSGCTraceKind kind = JSTRACE_SHAPE; };
template <> struct MapTypeToTraceKind<BaseShape> { const static JSGCTraceKind kind = JSTRACE_BASE_SHAPE; };
template <> struct MapTypeToTraceKind<UnownedBaseShape> { const static JSGCTraceKind kind = JSTRACE_BASE_SHAPE; };
@ -170,6 +172,7 @@ IsNurseryAllocable(AllocKind kind)
false, /* FINALIZE_OBJECT16 */
true, /* FINALIZE_OBJECT16_BACKGROUND */
false, /* FINALIZE_SCRIPT */
false, /* FINALIZE_LAZY_SCRIPT */
false, /* FINALIZE_SHAPE */
false, /* FINALIZE_BASE_SHAPE */
false, /* FINALIZE_TYPE_OBJECT */
@ -201,6 +204,7 @@ IsBackgroundFinalized(AllocKind kind)
false, /* FINALIZE_OBJECT16 */
true, /* FINALIZE_OBJECT16_BACKGROUND */
false, /* FINALIZE_SCRIPT */
true, /* FINALIZE_LAZY_SCRIPT */
true, /* FINALIZE_SHAPE */
true, /* FINALIZE_BASE_SHAPE */
true, /* FINALIZE_TYPE_OBJECT */

View File

@ -591,6 +591,13 @@ js_NewGCScript(JSContext *cx)
sizeof(JSScript), js::gc::TenuredHeap);
}
inline js::LazyScript *
js_NewGCLazyScript(JSContext *cx)
{
return js::gc::NewGCThing<js::LazyScript, js::CanGC>(cx, js::gc::FINALIZE_LAZY_SCRIPT,
sizeof(js::LazyScript), js::gc::TenuredHeap);
}
inline js::Shape *
js_NewGCShape(JSContext *cx)
{

View File

@ -5949,20 +5949,12 @@ JSScript::makeAnalysis(JSContext *cx)
/* static */ bool
JSFunction::setTypeForScriptedFunction(JSContext *cx, HandleFunction fun, bool singleton /* = false */)
{
JS_ASSERT(fun->nonLazyScript());
JS_ASSERT(fun->nonLazyScript()->function() == fun);
if (!cx->typeInferenceEnabled())
return true;
if (singleton) {
if (!setSingletonType(cx, fun))
return false;
} else if (UseNewTypeForClone(fun)) {
/*
* Leave the default unknown-properties type for the function, it
* should not be used by scripts or appear in type sets.
*/
} else {
RootedObject funProto(cx, fun->getProto());
TypeObject *type = cx->compartment->types.newTypeObject(cx, &FunctionClass, funProto);

View File

@ -716,7 +716,7 @@ UseNewTypeForClone(JSFunction *fun)
if (!fun->isInterpreted())
return false;
if (fun->nonLazyScript()->shouldCloneAtCallsite)
if (fun->hasScript() && fun->nonLazyScript()->shouldCloneAtCallsite)
return true;
if (fun->isArrow())

View File

@ -57,6 +57,7 @@ ZoneStats::GCHeapThingsSize()
size_t n = 0;
n += gcHeapStringsNormal;
n += gcHeapStringsShort;
n += gcHeapLazyScripts;
n += gcHeapTypeObjects;
n += gcHeapIonCodes;
@ -271,6 +272,13 @@ StatsCellCallback(JSRuntime *rt, void *data, void *thing, JSGCTraceKind traceKin
break;
}
case JSTRACE_LAZY_SCRIPT: {
LazyScript *lazy = static_cast<LazyScript *>(thing);
zStats->gcHeapLazyScripts += thingSize;
zStats->lazyScripts += lazy->sizeOfExcludingThis(rtStats->mallocSizeOf_);
break;
}
case JSTRACE_IONCODE: {
#ifdef JS_ION
zStats->gcHeapIonCodes += thingSize;

View File

@ -487,6 +487,12 @@ IsLocalOp(JSOp op)
return JOF_OPTYPE(op) == JOF_LOCAL;
}
inline bool
IsAliasedVarOp(JSOp op)
{
return JOF_OPTYPE(op) == JOF_SCOPECOORD;
}
inline bool
IsGlobalOp(JSOp op)
{

View File

@ -83,6 +83,7 @@ class RegExpShared;
class RegExpStatics;
class MatchPairs;
class PropertyName;
class LazyScript;
enum RegExpFlag
{

View File

@ -150,9 +150,10 @@ typedef enum {
JSTRACE_SCRIPT,
/*
* Trace kinds internal to the engine. The embedding can only them if it
* implements JSTraceCallback.
* Trace kinds internal to the engine. The embedding can only see them if
* it implements JSTraceCallback.
*/
JSTRACE_LAZY_SCRIPT,
JSTRACE_IONCODE,
JSTRACE_SHAPE,
JSTRACE_BASE_SHAPE,

View File

@ -3069,7 +3069,9 @@ reflect_parse(JSContext *cx, uint32_t argc, jsval *vp)
size_t length = stable->length();
CompileOptions options(cx);
options.setFileAndLine(filename, lineno);
Parser<FullParseHandler> parser(cx, options, chars.get(), length, /* foldConstants = */ false);
options.setCanLazilyParse(false);
Parser<FullParseHandler> parser(cx, options, chars.get(), length,
/* foldConstants = */ false, NULL, NULL);
if (!parser.init())
return JS_FALSE;

View File

@ -1213,17 +1213,15 @@ SourceDataCache::purge()
map_ = NULL;
}
JSStableString *
ScriptSource::substring(JSContext *cx, uint32_t start, uint32_t stop)
const jschar *
ScriptSource::chars(JSContext *cx)
{
const jschar *chars;
#ifdef USE_ZLIB
Rooted<JSStableString *> cached(cx, NULL);
#endif
#ifdef JS_THREADSAFE
if (!ready()) {
chars = cx->runtime->sourceCompressorThread.currentChars();
} else
if (!ready())
return cx->runtime->sourceCompressorThread.currentChars();
#endif
#ifdef USE_ZLIB
if (compressed()) {
@ -1247,14 +1245,21 @@ ScriptSource::substring(JSContext *cx, uint32_t start, uint32_t stop)
}
cx->runtime->sourceDataCache.put(this, cached);
}
chars = cached->chars().get();
JS_ASSERT(chars);
} else {
chars = data.source;
return cached->chars().get();
}
return data.source;
#else
chars = data.source;
return data.source;
#endif
}
JSStableString *
ScriptSource::substring(JSContext *cx, uint32_t start, uint32_t stop)
{
JS_ASSERT(start <= stop);
const jschar *chars = this->chars(cx);
if (!chars)
return NULL;
JSFlatString *flatStr = js_NewStringCopyN<CanGC>(cx, chars + start, stop - start);
if (!flatStr)
return NULL;
@ -1667,6 +1672,8 @@ JSScript::Create(JSContext *cx, HandleObject enclosingScope, bool savedCallerFun
const CompileOptions &options, unsigned staticLevel,
JS::HandleScriptSource sourceObject, uint32_t bufStart, uint32_t bufEnd)
{
JS_ASSERT(bufStart <= bufEnd);
RootedScript script(cx, js_NewGCScript(cx));
if (!script)
return NULL;
@ -1958,7 +1965,7 @@ JSScript::enclosingScriptsCompiledSuccessfully() const
while (enclosing) {
if (enclosing->isFunction()) {
JSFunction *fun = enclosing->toFunction();
if (!fun->hasScript())
if (!fun->hasScript() || !fun->nonLazyScript())
return false;
enclosing = fun->nonLazyScript()->enclosingStaticScope();
} else {
@ -2294,6 +2301,8 @@ js::CloneScript(JSContext *cx, HandleObject enclosingScope, HandleFunction fun,
clone = CloneStaticBlockObject(cx, enclosingScope, innerBlock);
} else if (obj->isFunction()) {
RootedFunction innerFun(cx, obj->toFunction());
if (!innerFun->getOrCreateScript(cx))
return NULL;
RootedObject staticScope(cx, innerFun->nonLazyScript()->enclosingStaticScope());
StaticScopeIter ssi(cx, staticScope);
RootedObject enclosingScope(cx);
@ -2736,6 +2745,31 @@ JSScript::markChildren(JSTracer *trc)
#endif
}
void
LazyScript::markChildren(JSTracer *trc)
{
if (parent_)
MarkScriptUnbarriered(trc, &parent_, "lazyScriptParent");
if (script_)
MarkScriptUnbarriered(trc, &script_, "lazyScript");
HeapPtrAtom *freeVariables = this->freeVariables();
for (size_t i = 0; i < numFreeVariables(); i++)
MarkString(trc, &freeVariables[i], "lazyScriptFreeVariable");
HeapPtrFunction *innerFunctions = this->innerFunctions();
for (size_t i = 0; i < numInnerFunctions(); i++)
MarkObject(trc, &innerFunctions[i], "lazyScriptInnerFunction");
}
void
LazyScript::finalize(FreeOp *fop)
{
if (table_)
fop->free_(table_);
}
void
JSScript::setArgumentsHasVarBinding()
{
@ -2882,6 +2916,29 @@ JSScript::formalLivesInArgumentsObject(unsigned argSlot)
return argsObjAliasesFormals() && !formalIsAliased(argSlot);
}
/* static */ LazyScript *
LazyScript::Create(JSContext *cx, uint32_t numFreeVariables, uint32_t numInnerFunctions,
uint32_t begin, uint32_t end, uint32_t lineno, uint32_t column)
{
JS_ASSERT(begin <= end);
size_t bytes = (numFreeVariables * sizeof(HeapPtrAtom))
+ (numInnerFunctions * sizeof(HeapPtrFunction));
void *table = NULL;
if (bytes) {
table = cx->malloc_(bytes);
if (!table)
return NULL;
}
LazyScript *res = js_NewGCLazyScript(cx);
if (!res)
return NULL;
return new (res) LazyScript(table, numFreeVariables, numInnerFunctions, begin, end, lineno, column);
}
void
JSScript::updateBaselineOrIonRaw()
{

View File

@ -336,6 +336,7 @@ class JSScript : public js::gc::Cell
uint32_t natoms; /* length of atoms array */
/* Range of characters in scriptSource which contains this script's source. */
uint32_t sourceStart;
uint32_t sourceEnd;
@ -416,6 +417,9 @@ class JSScript : public js::gc::Cell
bool isCachedEval:1; /* script came from eval(), and is in eval cache */
bool uninlineable:1; /* script is considered uninlineable by analysis */
/* Set for functions defined at the top level within an 'eval' script. */
bool directlyInsideEval:1;
/* script is attempted to be cloned anew at each callsite. This is
temporarily needed for ParallelArray selfhosted code until type
information can be made context sensitive. See discussion in
@ -792,6 +796,11 @@ class JSScript : public js::gc::Cell
return arr->vector[index];
}
size_t innerObjectsStart() {
// The first object contains the caller if savedCallerFun is used.
return savedCallerFun ? 1 : 0;
}
JSObject *getObject(jsbytecode *pc) {
JS_ASSERT(pc >= code && pc + sizeof(uint32_t) < code + length);
return getObject(GET_UINT32_INDEX(pc));
@ -1041,6 +1050,7 @@ struct ScriptSource
JS_ASSERT(hasSourceData());
return argumentsNotIncluded_;
}
const jschar *chars(JSContext *cx);
JSStableString *substring(JSContext *cx, uint32_t start, uint32_t stop);
size_t sizeOfIncludingThis(JSMallocSizeOfFun mallocSizeOf);
@ -1103,6 +1113,161 @@ class ScriptSourceObject : public JSObject {
static const uint32_t SOURCE_SLOT = 0;
};
// Information about a script which may be (or has been) lazily compiled to
// bytecode from its source.
class LazyScript : public js::gc::Cell
{
// Immediate parent in which the script is nested, or NULL if the parent
// has not been compiled yet. Lazy scripts are always functions within a
// global or eval script so there will be a parent.
JSScript *parent_;
// If non-NULL, the script has been compiled and this is a forwarding
// pointer to the result.
JSScript *script_;
// Heap allocated table with any free variables or inner functions.
void *table_;
#if JS_BITS_PER_WORD == 32
uint32_t padding;
#endif
uint32_t numFreeVariables_;
uint32_t numInnerFunctions_ : 26;
bool strict_ : 1;
bool bindingsAccessedDynamically_ : 1;
bool hasDebuggerStatement_ : 1;
bool directlyInsideEval_:1;
bool hasBeenCloned_:1;
// Source location for the script.
uint32_t begin_;
uint32_t end_;
uint32_t lineno_;
uint32_t column_;
LazyScript(void *table, uint32_t numFreeVariables, uint32_t numInnerFunctions,
uint32_t begin, uint32_t end, uint32_t lineno, uint32_t column)
: parent_(NULL),
script_(NULL),
table_(table),
numFreeVariables_(numFreeVariables),
numInnerFunctions_(numInnerFunctions),
strict_(false),
bindingsAccessedDynamically_(false),
hasDebuggerStatement_(false),
directlyInsideEval_(false),
hasBeenCloned_(false),
begin_(begin),
end_(end),
lineno_(lineno),
column_(column)
{
JS_ASSERT(begin <= end);
}
public:
static LazyScript *Create(JSContext *cx, uint32_t numFreeVariables, uint32_t numInnerFunctions,
uint32_t begin, uint32_t end, uint32_t lineno, uint32_t column);
void initParent(JSScript *parent) {
JS_ASSERT(parent && !parent_);
parent_ = parent;
}
JSScript *parent() const {
return parent_;
}
void initScript(JSScript *script) {
JS_ASSERT(script && !script_);
script_ = script;
}
JSScript *maybeScript() {
return script_;
}
uint32_t numFreeVariables() const {
return numFreeVariables_;
}
HeapPtrAtom *freeVariables() {
return (HeapPtrAtom *)table_;
}
uint32_t numInnerFunctions() const {
return numInnerFunctions_;
}
HeapPtrFunction *innerFunctions() {
return (HeapPtrFunction *)&freeVariables()[numFreeVariables()];
}
bool strict() const {
return strict_;
}
void setStrict() {
strict_ = true;
}
bool bindingsAccessedDynamically() const {
return bindingsAccessedDynamically_;
}
void setBindingsAccessedDynamically() {
bindingsAccessedDynamically_ = true;
}
bool hasDebuggerStatement() const {
return hasDebuggerStatement_;
}
void setHasDebuggerStatement() {
hasDebuggerStatement_ = true;
}
bool directlyInsideEval() const {
return directlyInsideEval_;
}
void setDirectlyInsideEval() {
directlyInsideEval_ = true;
}
bool hasBeenCloned() const {
return hasBeenCloned_;
}
void setHasBeenCloned() {
hasBeenCloned_ = true;
}
ScriptSource *source() const {
return parent()->scriptSource();
}
uint32_t begin() const {
return begin_;
}
uint32_t end() const {
return end_;
}
uint32_t lineno() const {
return lineno_;
}
uint32_t column() const {
return column_;
}
Zone *zone() const {
return Cell::tenuredZone();
}
void markChildren(JSTracer *trc);
void finalize(js::FreeOp *fop);
size_t sizeOfExcludingThis(JSMallocSizeOfFun mallocSizeOf)
{
return mallocSizeOf(table_);
}
static inline void writeBarrierPre(LazyScript *lazy);
};
#ifdef JS_THREADSAFE
/*
* Background thread to compress JS source code. This happens only while parsing

View File

@ -184,6 +184,23 @@ JSScript::writeBarrierPost(JSScript *script, void *addr)
{
}
/* static */ inline void
js::LazyScript::writeBarrierPre(js::LazyScript *lazy)
{
#ifdef JSGC_INCREMENTAL
if (!lazy)
return;
JS::Zone *zone = lazy->zone();
if (zone->needsBarrier()) {
JS_ASSERT(!zone->rt->isHeapBusy());
js::LazyScript *tmp = lazy;
MarkLazyScriptUnbarriered(zone->barrierTracer(), &tmp, "write barrier");
JS_ASSERT(tmp == lazy);
}
#endif
}
inline JSPrincipals *
JSScript::principals()
{

View File

@ -3168,7 +3168,7 @@ Parse(JSContext *cx, unsigned argc, jsval *vp)
Parser<FullParseHandler> parser(cx, options,
JS_GetStringCharsZ(cx, scriptContents),
JS_GetStringLength(scriptContents),
/* foldConstants = */ true);
/* foldConstants = */ true, NULL, NULL);
if (!parser.init())
return false;
@ -3208,8 +3208,7 @@ SyntaxParse(JSContext *cx, unsigned argc, jsval *vp)
const jschar *chars = JS_GetStringCharsZ(cx, scriptContents);
size_t length = JS_GetStringLength(scriptContents);
Parser<frontend::SyntaxParseHandler> parser(cx, options, chars, length,
/* foldConstants = */ false);
Parser<frontend::SyntaxParseHandler> parser(cx, options, chars, length, false, NULL, NULL);
if (!parser.init())
return false;
@ -3217,7 +3216,7 @@ SyntaxParse(JSContext *cx, unsigned argc, jsval *vp)
if (cx->isExceptionPending())
return false;
if (!succeeded && !parser.hadUnknownResult()) {
if (!succeeded && !parser.hadAbortedSyntaxParse()) {
// If no exception is posted, either there was an OOM or a language
// feature unhandled by the syntax parser was encountered.
JS_ASSERT(cx->runtime->hadOutOfMemory);

View File

@ -2892,6 +2892,16 @@ DebuggerScript_getSourceMapUrl(JSContext *cx, unsigned argc, Value *vp)
return true;
}
static bool
EnsureFunctionHasScript(JSContext *cx, JSFunction *fun)
{
if (fun->isInterpretedLazy()) {
AutoCompartment ac(cx, fun);
return fun->getOrCreateScript(cx);
}
return true;
}
static JSBool
DebuggerScript_getChildScripts(JSContext *cx, unsigned argc, Value *vp)
{
@ -2905,16 +2915,21 @@ DebuggerScript_getChildScripts(JSContext *cx, unsigned argc, Value *vp)
/*
* script->savedCallerFun indicates that this is a direct eval script
* and the calling function is stored as script->objects()->vector[0].
* It is not really a child script of this script, so skip it.
* It is not really a child script of this script, so skip it using
* innerObjectsStart().
*/
ObjectArray *objects = script->objects();
RootedFunction fun(cx);
RootedScript funScript(cx);
RootedObject obj(cx), s(cx);
for (uint32_t i = script->savedCallerFun ? 1 : 0; i < objects->length; i++) {
for (uint32_t i = script->innerObjectsStart(); i < objects->length; i++) {
obj = objects->vector[i];
if (obj->isFunction()) {
fun = static_cast<JSFunction *>(obj.get());
if (!EnsureFunctionHasScript(cx, fun))
return false;
funScript = fun->nonLazyScript();
s = dbg->wrapScript(cx, funScript);
if (!s || !js_NewbornArrayPush(cx, result, ObjectValue(*s)))
@ -4122,7 +4137,8 @@ js::EvaluateInEnv(JSContext *cx, Handle<Env*> env, HandleValue thisv, AbstractFr
.setCompileAndGo(true)
.setForEval(true)
.setNoScriptRval(false)
.setFileAndLine(filename, lineno);
.setFileAndLine(filename, lineno)
.setCanLazilyParse(false);
RootedScript callerScript(cx, frame ? frame.script() : NULL);
RootedScript script(cx, frontend::CompileScript(cx, env, callerScript,
options, chars.get(), length,
@ -4455,6 +4471,9 @@ DebuggerObject_getParameterNames(JSContext *cx, unsigned argc, Value *vp)
result->ensureDenseInitializedLength(cx, 0, fun->nargs);
if (fun->isInterpreted()) {
if (!EnsureFunctionHasScript(cx, fun))
return false;
JS_ASSERT(fun->nargs == fun->nonLazyScript()->bindings.numArgs());
if (fun->nargs > 0) {
@ -4496,6 +4515,9 @@ DebuggerObject_getScript(JSContext *cx, unsigned argc, Value *vp)
return true;
}
if (!EnsureFunctionHasScript(cx, fun))
return false;
RootedScript script(cx, fun->nonLazyScript());
RootedObject scriptObject(cx, dbg->wrapScript(cx, script));
if (!scriptObject)

View File

@ -615,12 +615,11 @@ js::ParallelDo::enqueueInitialScript(ExecutionStatus *status)
if (!callee->isInterpreted() || !callee->isSelfHostedBuiltin())
return sequentialExecution(true, status);
if (callee->isInterpretedLazy() && !callee->initializeLazyScript(cx_))
return sequentialExecution(true, status);
// If this function has not been run enough to enable parallel
// execution, perform a warmup.
RootedScript script(cx_, callee->nonLazyScript());
RootedScript script(cx_, callee->getOrCreateScript(cx_));
if (!script)
return RedLight;
if (script->getUseCount() < js_IonOptions.usesBeforeCompileParallel) {
if (warmupExecution(status) == RedLight)
return RedLight;

View File

@ -517,6 +517,7 @@ JSRuntime::initSelfHosting(JSContext *cx)
CompileOptions options(cx);
options.setFileAndLine("self-hosted", 1);
options.setSelfHostingMode(true);
options.setCanLazilyParse(false);
options.setSourcePolicy(CompileOptions::NO_SOURCE);
options.setVersion(JSVERSION_LATEST);

View File

@ -1662,6 +1662,11 @@ ReportZoneStats(const JS::ZoneStats &zStats,
"heap that holds over-sized string headers, in which "
"string characters are stored inline.");
ZCREPORT_GC_BYTES(pathPrefix + NS_LITERAL_CSTRING("gc-heap/lazy-scripts"),
zStats.gcHeapLazyScripts,
"Memory on the garbage-collected JavaScript "
"heap that represents scripts which haven't executed yet.");
ZCREPORT_GC_BYTES(pathPrefix + NS_LITERAL_CSTRING("gc-heap/type-objects"),
zStats.gcHeapTypeObjects,
"Memory on the garbage-collected JavaScript "
@ -1673,6 +1678,11 @@ ReportZoneStats(const JS::ZoneStats &zStats,
"heap that holds references to executable code pools "
"used by the IonMonkey JIT.");
ZCREPORT_BYTES(pathPrefix + NS_LITERAL_CSTRING("lazy-scripts"),
zStats.lazyScripts,
"Memory holding miscellaneous additional information associated with lazy "
"scripts.");
ZCREPORT_BYTES(pathPrefix + NS_LITERAL_CSTRING("type-objects"),
zStats.typeObjects,
"Memory holding miscellaneous additional information associated with type "

View File

@ -722,6 +722,7 @@ DescribeGCThing(bool isMarked, void *p, JSGCTraceKind traceKind,
"Object",
"String",
"Script",
"LazyScript",
"IonCode",
"Shape",
"BaseShape",

View File

@ -196,6 +196,58 @@ class Maybe
constructed = true;
}
template<class T1, class T2, class T3, class T4, class T5>
void construct(const T1& t1, const T2& t2, const T3& t3, const T4& t4, const T5& t5) {
MOZ_ASSERT(!constructed);
::new (storage.addr()) T(t1, t2, t3, t4, t5);
constructed = true;
}
template<class T1, class T2, class T3, class T4, class T5,
class T6>
void construct(const T1& t1, const T2& t2, const T3& t3, const T4& t4, const T5& t5,
const T6& t6) {
MOZ_ASSERT(!constructed);
::new (storage.addr()) T(t1, t2, t3, t4, t5, t6);
constructed = true;
}
template<class T1, class T2, class T3, class T4, class T5,
class T6, class T7>
void construct(const T1& t1, const T2& t2, const T3& t3, const T4& t4, const T5& t5,
const T6& t6, const T7& t7) {
MOZ_ASSERT(!constructed);
::new (storage.addr()) T(t1, t2, t3, t4, t5, t6, t7);
constructed = true;
}
template<class T1, class T2, class T3, class T4, class T5,
class T6, class T7, class T8>
void construct(const T1& t1, const T2& t2, const T3& t3, const T4& t4, const T5& t5,
const T6& t6, const T7& t7, const T8& t8) {
MOZ_ASSERT(!constructed);
::new (storage.addr()) T(t1, t2, t3, t4, t5, t6, t7, t8);
constructed = true;
}
template<class T1, class T2, class T3, class T4, class T5,
class T6, class T7, class T8, class T9>
void construct(const T1& t1, const T2& t2, const T3& t3, const T4& t4, const T5& t5,
const T6& t6, const T7& t7, const T8& t8, const T9& t9) {
MOZ_ASSERT(!constructed);
::new (storage.addr()) T(t1, t2, t3, t4, t5, t6, t7, t8, t9);
constructed = true;
}
template<class T1, class T2, class T3, class T4, class T5,
class T6, class T7, class T8, class T9, class T10>
void construct(const T1& t1, const T2& t2, const T3& t3, const T4& t4, const T5& t5,
const T6& t6, const T7& t7, const T8& t8, const T9& t9, const T10& t10) {
MOZ_ASSERT(!constructed);
::new (storage.addr()) T(t1, t2, t3, t4, t5, t6, t7, t8, t9, t10);
constructed = true;
}
T* addr() {
MOZ_ASSERT(constructed);
return &asT();