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

View File

@ -191,6 +191,30 @@ TryEvalJSON(JSContext *cx, JSScript *callerScript,
return EvalJSON_NotJSON; 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. // Define subset of ExecuteType so that casting performs the injection.
enum EvalType { DIRECT_EVAL = EXECUTE_DIRECT_EVAL, INDIRECT_EVAL = EXECUTE_INDIRECT_EVAL }; 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) if (!compiled)
return false; return false;
MarkFunctionsWithinEvalScript(compiled);
esg.setNewScript(compiled); esg.setNewScript(compiled);
} }
@ -357,6 +383,8 @@ js::DirectEvalFromIon(JSContext *cx,
if (!compiled) if (!compiled)
return false; return false;
MarkFunctionsWithinEvalScript(compiled);
esg.setNewScript(compiled); esg.setNewScript(compiled);
} }

View File

@ -23,6 +23,7 @@
using namespace js; using namespace js;
using namespace js::frontend; using namespace js::frontend;
using mozilla::Maybe;
static bool static bool
CheckLength(JSContext *cx, size_t length) CheckLength(JSContext *cx, size_t length)
@ -84,6 +85,7 @@ frontend::CompileScript(JSContext *cx, HandleObject scopeChain,
SourceCompressionToken *extraSct /* = NULL */) SourceCompressionToken *extraSct /* = NULL */)
{ {
RootedString source(cx, source_); RootedString source(cx, source_);
SkipRoot skip(cx, &chars);
/* /*
* The scripted callerFrame can only be given for compile-and-go scripts * The scripted callerFrame can only be given for compile-and-go scripts
@ -119,15 +121,28 @@ frontend::CompileScript(JSContext *cx, HandleObject scopeChain,
break; 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()) if (!parser.init())
return NULL; return NULL;
parser.sct = sct; parser.sct = sct;
GlobalSharedContext globalsc(cx, scopeChain, StrictModeFromContext(cx)); GlobalSharedContext globalsc(cx, scopeChain, StrictModeFromContext(cx));
ParseContext<FullParseHandler> pc(&parser, NULL, &globalsc, staticLevel, /* bodyid = */ 0); // Syntax parsing may cause us to restart processing of top level
if (!pc.init()) // 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; return NULL;
bool savedCallerFun = bool savedCallerFun =
@ -150,8 +165,10 @@ frontend::CompileScript(JSContext *cx, HandleObject scopeChain,
JS_ASSERT_IF(globalScope, globalScope->isNative()); JS_ASSERT_IF(globalScope, globalScope->isNative());
JS_ASSERT_IF(globalScope, JSCLASS_HAS_GLOBAL_FLAG_AND_SLOTS(globalScope->getClass())); 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, BytecodeEmitter bce(/* parent = */ NULL, &parser, &globalsc, script, options.forEval, evalCaller,
!!globalScope, options.lineno, options.selfHostingMode); !!globalScope, options.lineno, emitterMode);
if (!bce.init()) if (!bce.init())
return NULL; return NULL;
@ -178,7 +195,7 @@ frontend::CompileScript(JSContext *cx, HandleObject scopeChain,
* wishes to decompile it while it's running. * wishes to decompile it while it's running.
*/ */
JSFunction *fun = evalCaller->functionOrCallerFunction(); JSFunction *fun = evalCaller->functionOrCallerFunction();
ObjectBox *funbox = parser.newFunctionBox(fun, &pc, fun->strict()); ObjectBox *funbox = parser.newFunctionBox(fun, pc.addr(), fun->strict());
if (!funbox) if (!funbox)
return NULL; return NULL;
bce.objectList.add(funbox); bce.objectList.add(funbox);
@ -195,9 +212,32 @@ frontend::CompileScript(JSContext *cx, HandleObject scopeChain,
return NULL; return NULL;
} }
TokenStream::Position pos(parser.keepAtoms);
parser.tokenStream.tell(&pos);
ParseNode *pn = parser.statement(); ParseNode *pn = parser.statement();
if (!pn) if (!pn) {
return NULL; 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 (canHaveDirectives) {
if (!parser.maybeParseDirective(pn, &canHaveDirectives)) if (!parser.maybeParseDirective(pn, &canHaveDirectives))
@ -223,13 +263,13 @@ frontend::CompileScript(JSContext *cx, HandleObject scopeChain,
// free variables and as variables redeclared with 'var'. // free variables and as variables redeclared with 'var'.
RootedFunction fun(cx, evalCaller->functionOrCallerFunction()); RootedFunction fun(cx, evalCaller->functionOrCallerFunction());
HandlePropertyName arguments = cx->names().arguments; 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 (r.front().key() == arguments) {
if (!CheckArgumentsWithinEval(cx, parser, fun)) if (!CheckArgumentsWithinEval(cx, parser, fun))
return NULL; 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 (r.front().key() == arguments) {
if (!CheckArgumentsWithinEval(cx, parser, fun)) if (!CheckArgumentsWithinEval(cx, parser, fun))
return NULL; return NULL;
@ -239,7 +279,7 @@ frontend::CompileScript(JSContext *cx, HandleObject scopeChain,
// If the eval'ed script contains any debugger statement, force construction // If the eval'ed script contains any debugger statement, force construction
// of arguments objects for the caller script and any other scripts it is // of arguments objects for the caller script and any other scripts it is
// transitively nested inside. // transitively nested inside.
if (pc.sc->hasDebuggerStatement()) { if (pc.ref().sc->hasDebuggerStatement()) {
RootedObject scope(cx, scopeChain); RootedObject scope(cx, scopeChain);
while (scope->isScope() || scope->isDebugScope()) { while (scope->isScope() || scope->isDebugScope()) {
if (scope->isCall() && !scope->asCall().isForEval()) { if (scope->isCall() && !scope->asCall().isForEval()) {
@ -273,43 +313,55 @@ frontend::CompileScript(JSContext *cx, HandleObject scopeChain,
} }
bool bool
frontend::ParseScript(JSContext *cx, HandleObject scopeChain, frontend::CompileLazyFunction(JSContext *cx, HandleFunction fun, LazyScript *lazy,
const CompileOptions &options, StableCharPtr chars, size_t length) 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; return false;
Parser<SyntaxParseHandler> parser(cx, options, chars.get(), length, /* foldConstants = */ false); RootedObject enclosingScope(cx, lazy->parent()->function());
if (!parser.init()) {
cx->clearPendingException(); ParseNode *pn = parser.standaloneLazyFunction(fun, lazy->parent()->staticLevel + 1,
lazy->strict());
if (!pn)
return false; return false;
}
GlobalSharedContext globalsc(cx, scopeChain, StrictModeFromContext(cx)); JS::RootedScriptSource sourceObject(cx, ScriptSourceObject::create(cx, lazy->source()));
if (!sourceObject)
ParseContext<SyntaxParseHandler> pc(&parser, NULL, &globalsc, 0, /* bodyid = */ 0);
if (!pc.init()) {
cx->clearPendingException();
return false; return false;
}
for (;;) { Rooted<JSScript*> script(cx, JSScript::Create(cx, enclosingScope, false,
TokenKind tt = parser.tokenStream.peekToken(TSF_OPERAND); options, lazy->parent()->staticLevel + 1,
if (tt <= TOK_EOF) { sourceObject, lazy->begin(), lazy->end()));
if (tt == TOK_EOF) if (!script)
break; return false;
JS_ASSERT(tt == TOK_ERROR);
cx->clearPendingException();
return false;
}
if (!parser.statement()) { script->bindings = pn->pn_funbox->bindings;
cx->clearPendingException();
return false;
}
}
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 // 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, const AutoNameVector &formals, const jschar *chars, size_t length,
bool isAsmJSRecompile) bool isAsmJSRecompile)
{ {
SkipRoot skip(cx, &chars);
if (!CheckLength(cx, length)) if (!CheckLength(cx, length))
return false; return false;
ScriptSource *ss = cx->new_<ScriptSource>(); ScriptSource *ss = cx->new_<ScriptSource>();
@ -336,9 +390,17 @@ frontend::CompileFunctionBody(JSContext *cx, MutableHandleFunction fun, CompileO
return false; 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); 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()) if (!parser.init())
return false; return false;
parser.sct = &sct; parser.sct = &sct;
@ -374,21 +436,29 @@ frontend::CompileFunctionBody(JSContext *cx, MutableHandleFunction fun, CompileO
// directive, we backup and reparse it as strict. // directive, we backup and reparse it as strict.
TokenStream::Position start(parser.keepAtoms); TokenStream::Position start(parser.keepAtoms);
parser.tokenStream.tell(&start); parser.tokenStream.tell(&start);
bool initiallyStrict = StrictModeFromContext(cx); bool strict = StrictModeFromContext(cx);
bool becameStrict; bool becameStrict;
FunctionBox *funbox; FunctionBox *funbox;
ParseNode *pn = parser.standaloneFunctionBody(fun, formals, script, fn, &funbox, ParseNode *pn;
initiallyStrict, &becameStrict); while (true) {
if (!pn) {
if (initiallyStrict || !becameStrict || parser.tokenStream.hadError())
return false;
// Reparse in strict mode.
parser.tokenStream.seek(start);
pn = parser.standaloneFunctionBody(fun, formals, script, fn, &funbox, pn = parser.standaloneFunctionBody(fun, formals, script, fn, &funbox,
/* strict = */ true); strict, &becameStrict);
if (!pn) if (pn)
return false; 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)) if (!NameFunctions(cx, pn))

View File

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

View File

@ -92,7 +92,7 @@ struct frontend::StmtInfoBCE : public StmtInfoBase
BytecodeEmitter::BytecodeEmitter(BytecodeEmitter *parent, BytecodeEmitter::BytecodeEmitter(BytecodeEmitter *parent,
Parser<FullParseHandler> *parser, SharedContext *sc, Parser<FullParseHandler> *parser, SharedContext *sc,
HandleScript script, bool insideEval, HandleScript evalCaller, HandleScript script, bool insideEval, HandleScript evalCaller,
bool hasGlobalScope, uint32_t lineNum, bool selfHostingMode) bool hasGlobalScope, uint32_t lineNum, EmitterMode emitterMode)
: sc(sc), : sc(sc),
parent(parent), parent(parent),
script(sc->context, script), script(sc->context, script),
@ -117,7 +117,7 @@ BytecodeEmitter::BytecodeEmitter(BytecodeEmitter *parent,
emittingRunOnceLambda(false), emittingRunOnceLambda(false),
insideEval(insideEval), insideEval(insideEval),
hasGlobalScope(hasGlobalScope), hasGlobalScope(hasGlobalScope),
selfHostingMode(selfHostingMode) emitterMode(emitterMode)
{ {
JS_ASSERT_IF(evalCaller, insideEval); JS_ASSERT_IF(evalCaller, insideEval);
} }
@ -828,23 +828,24 @@ ClonedBlockDepth(BytecodeEmitter *bce)
return clonedBlockDepth; return clonedBlockDepth;
} }
static uint16_t static bool
AliasedNameToSlot(HandleScript script, PropertyName *name) LookupAliasedName(HandleScript script, PropertyName *name, uint16_t *pslot)
{ {
/* /*
* Beware: BindingIter may contain more than one Binding for a given name * 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. * (in the case of |function f(x,x) {}|) but only one will be aliased.
*/ */
unsigned slot = CallObject::RESERVED_SLOTS; unsigned slot = CallObject::RESERVED_SLOTS;
for (BindingIter bi(script); ; bi++) { for (BindingIter bi(script); !bi.done(); bi++) {
if (bi->aliased()) { if (bi->aliased()) {
if (bi->name() == name) if (bi->name() == name) {
return slot; *pslot = slot;
return true;
}
slot++; slot++;
} }
} }
return false;
return 0;
} }
static bool static bool
@ -876,13 +877,13 @@ EmitAliasedVarOp(JSContext *cx, JSOp op, ParseNode *pn, BytecodeEmitter *bce)
ScopeCoordinate sc; ScopeCoordinate sc;
if (IsArgOp(pn->getOp())) { if (IsArgOp(pn->getOp())) {
sc.hops = skippedScopes + ClonedBlockDepth(bceOfDef); sc.hops = skippedScopes + ClonedBlockDepth(bceOfDef);
sc.slot = AliasedNameToSlot(bceOfDef->script, pn->name()); JS_ALWAYS_TRUE(LookupAliasedName(bceOfDef->script, pn->name(), &sc.slot));
} else { } else {
JS_ASSERT(IsLocalOp(pn->getOp()) || pn->isKind(PNK_FUNCTION)); JS_ASSERT(IsLocalOp(pn->getOp()) || pn->isKind(PNK_FUNCTION));
unsigned local = pn->pn_cookie.slot(); unsigned local = pn->pn_cookie.slot();
if (local < bceOfDef->script->bindings.numVars()) { if (local < bceOfDef->script->bindings.numVars()) {
sc.hops = skippedScopes + ClonedBlockDepth(bceOfDef); sc.hops = skippedScopes + ClonedBlockDepth(bceOfDef);
sc.slot = AliasedNameToSlot(bceOfDef->script, pn->name()); JS_ALWAYS_TRUE(LookupAliasedName(bceOfDef->script, pn->name(), &sc.slot));
} else { } else {
unsigned depth = local - bceOfDef->script->bindings.numVars(); unsigned depth = local - bceOfDef->script->bindings.numVars();
StaticBlockObject *b = bceOfDef->blockChain; StaticBlockObject *b = bceOfDef->blockChain;
@ -903,9 +904,17 @@ static bool
EmitVarOp(JSContext *cx, ParseNode *pn, JSOp op, BytecodeEmitter *bce) EmitVarOp(JSContext *cx, ParseNode *pn, JSOp op, BytecodeEmitter *bce)
{ {
JS_ASSERT(pn->isKind(PNK_FUNCTION) || pn->isKind(PNK_NAME)); 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()); 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)) { if (!bce->isAliasedName(pn)) {
JS_ASSERT(pn->isUsed() || pn->isDefn()); JS_ASSERT(pn->isUsed() || pn->isDefn());
JS_ASSERT_IF(pn->isUsed(), pn->pn_cookie.level() == 0); JS_ASSERT_IF(pn->isUsed(), pn->pn_cookie.level() == 0);
@ -936,14 +945,24 @@ static bool
EmitVarIncDec(JSContext *cx, ParseNode *pn, BytecodeEmitter *bce) EmitVarIncDec(JSContext *cx, ParseNode *pn, BytecodeEmitter *bce)
{ {
JSOp op = pn->pn_kid->getOp(); 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->isKind(PNK_NAME));
JS_ASSERT(!pn->pn_kid->pn_cookie.isFree()); JS_ASSERT(!pn->pn_kid->pn_cookie.isFree());
bool post; bool post;
JSOp binop = GetIncDecInfo(pn->getKind(), &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 if (!EmitVarOp(cx, pn->pn_kid, getOp, bce)) // V
return false; 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 * Try to convert a *NAME op with a free name to a more specialized GNAME,
* globals. Return true if a conversion was made. * INTRINSIC or ALIASEDVAR op, which optimize accesses on that name.
* * 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.
*/ */
static bool static bool
TryConvertToGname(BytecodeEmitter *bce, ParseNode *pn, JSOp *op) TryConvertFreeName(BytecodeEmitter *bce, ParseNode *pn)
{ {
if (bce->selfHostingMode) { /*
switch (*op) { * In self-hosting mode, JSOP_*NAME is unconditionally converted to
case JSOP_NAME: *op = JSOP_GETINTRINSIC; break; * JSOP_*INTRINSIC. This causes lookups to be redirected to the special
case JSOP_SETNAME: *op = JSOP_SETINTRINSIC; break; * 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. */ /* Other *NAME ops aren't (yet) supported in self-hosted code. */
default: JS_NOT_REACHED("intrinsic"); default: JS_NOT_REACHED("intrinsic");
} }
pn->setOp(op);
return true; 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 && if (bce->script->compileAndGo &&
bce->hasGlobalScope && bce->hasGlobalScope &&
!(bce->sc->isFunctionBox() && bce->sc->asFunctionBox()->mightAliasLocals()) && !(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 // If you change anything here, you might also need to change
// js::ReportIfUndeclaredVarAssignment. // js::ReportIfUndeclaredVarAssignment.
switch (*op) { JSOp op;
case JSOP_NAME: *op = JSOP_GETGNAME; break; switch (pn->getOp()) {
case JSOP_SETNAME: *op = JSOP_SETGNAME; break; case JSOP_NAME: op = JSOP_GETGNAME; break;
case JSOP_SETNAME: op = JSOP_SETGNAME; break;
case JSOP_SETCONST: case JSOP_SETCONST:
/* Not supported. */ /* Not supported. */
return false; return false;
default: JS_NOT_REACHED("gname"); default: JS_NOT_REACHED("gname");
} }
pn->setOp(op);
return true; return true;
} }
return false; 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 * If this is an eval in the global scope, then unbound variables
* must be globals, so try to use GNAME ops. * must be globals, so try to use GNAME ops.
*/ */
if (!caller->functionOrCallerFunction() && TryConvertToGname(bce, pn, &op)) { if (!caller->functionOrCallerFunction() && TryConvertFreeName(bce, pn)) {
pn->setOp(op);
pn->pn_dflags |= PND_BOUND; pn->pn_dflags |= PND_BOUND;
return true; return true;
} }
@ -1239,12 +1319,10 @@ BindNameToSlotHelper(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn)
} }
/* Optimize accesses to undeclared globals. */ /* Optimize accesses to undeclared globals. */
if (!TryConvertToGname(bce, pn, &op)) if (!TryConvertFreeName(bce, pn))
return true; return true;
pn->setOp(op);
pn->pn_dflags |= PND_BOUND; pn->pn_dflags |= PND_BOUND;
return true; return true;
} }
@ -1394,7 +1472,7 @@ BindNameToSlot(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn)
if (!BindNameToSlotHelper(cx, bce, pn)) if (!BindNameToSlotHelper(cx, bce, pn))
return false; return false;
if (bce->selfHostingMode && !pn->isBound()) { if (bce->emitterMode == BytecodeEmitter::SelfHosting && !pn->isBound()) {
bce->reportError(pn, JSMSG_SELFHOSTED_UNBOUND_NAME); bce->reportError(pn, JSMSG_SELFHOSTED_UNBOUND_NAME);
return false; return false;
} }
@ -1721,6 +1799,9 @@ EmitNameOp(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn, bool callContext)
case JSOP_GETLOCAL: case JSOP_GETLOCAL:
op = JSOP_CALLLOCAL; op = JSOP_CALLLOCAL;
break; break;
case JSOP_GETALIASEDVAR:
op = JSOP_CALLALIASEDVAR;
break;
default: default:
JS_ASSERT(op == JSOP_CALLEE); JS_ASSERT(op == JSOP_CALLEE);
break; break;
@ -2459,7 +2540,7 @@ frontend::EmitFunctionScript(JSContext *cx, BytecodeEmitter *bce, ParseNode *bod
if (bce->script->varIsAliased(varIndex)) { if (bce->script->varIsAliased(varIndex)) {
ScopeCoordinate sc; ScopeCoordinate sc;
sc.hops = 0; 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)) if (!EmitAliasedVarOp(cx, JSOP_SETALIASEDVAR, sc, bce))
return false; return false;
} else { } else {
@ -2498,28 +2579,10 @@ frontend::EmitFunctionScript(JSContext *cx, BytecodeEmitter *bce, ParseNode *bod
if (bce->parent && bce->parent->emittingRunOnceLambda) if (bce->parent && bce->parent->emittingRunOnceLambda)
bce->script->treatAsRunOnce = true; 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(). */ /* Initialize fun->script() so that the debugger has a valid fun->script(). */
RootedFunction fun(cx, bce->script->function()); RootedFunction fun(cx, bce->script->function());
JS_ASSERT(fun->isInterpreted()); JS_ASSERT(fun->isInterpreted());
JS_ASSERT(!fun->hasScript());
fun->setScript(bce->script); fun->setScript(bce->script);
if (!JSFunction::setTypeForScriptedFunction(cx, fun, singleton))
return false;
bce->tellDebuggerAboutCompiledScript(cx); bce->tellDebuggerAboutCompiledScript(cx);
@ -3256,7 +3319,13 @@ EmitAssignment(JSContext *cx, BytecodeEmitter *bce, ParseNode *lhs, JSOp op, Par
if (!EmitAtomOp(cx, lhs, JSOP_GETINTRINSIC, bce)) if (!EmitAtomOp(cx, lhs, JSOP_GETINTRINSIC, bce))
return false; return false;
} else { } 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)) if (!EmitVarOp(cx, lhs, op, bce))
return false; return false;
} }
@ -3320,7 +3389,7 @@ EmitAssignment(JSContext *cx, BytecodeEmitter *bce, ParseNode *lhs, JSOp op, Par
} }
break; 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)) if (!EmitVarOp(cx, lhs, lhs->getOp(), bce))
return false; return false;
} else { } else {
@ -4333,20 +4402,49 @@ EmitFor(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn, ptrdiff_t top)
static JS_NEVER_INLINE bool static JS_NEVER_INLINE bool
EmitFunc(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn) 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()) { if (fun->isNative()) {
JS_ASSERT(IsAsmJSModuleNative(fun->native())); JS_ASSERT(IsAsmJSModuleNative(fun->native()));
return true; 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(pn->functionIsHoisted());
JS_ASSERT(bce->sc->isFunctionBox()); JS_ASSERT(bce->sc->isFunctionBox());
return true; 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; SharedContext *outersc = bce->sc;
FunctionBox *funbox = pn->pn_funbox;
if (outersc->isFunctionBox() && outersc->asFunctionBox()->mightAliasLocals()) if (outersc->isFunctionBox() && outersc->asFunctionBox()->mightAliasLocals())
funbox->setMightAliasLocals(); // inherit mightAliasLocals from parent 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); uint32_t lineNum = bce->parser->tokenStream.srcCoords.lineNum(pn->pn_pos.begin);
BytecodeEmitter bce2(bce, bce->parser, funbox, script, bce->insideEval, BytecodeEmitter bce2(bce, bce->parser, funbox, script, bce->insideEval,
bce->evalCaller, bce->hasGlobalScope, lineNum, bce->evalCaller, bce->hasGlobalScope, lineNum,
bce->selfHostingMode); bce->emitterMode);
if (!bce2.init()) if (!bce2.init())
return false; return false;
@ -4841,7 +4939,8 @@ EmitCallOrNew(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn)
ParseNode *pn2 = pn->pn_head; ParseNode *pn2 = pn->pn_head;
switch (pn2->getKind()) { switch (pn2->getKind()) {
case PNK_NAME: 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 * Special-casing of callFunction to emit bytecode that directly
@ -5065,6 +5164,7 @@ EmitIncOrDec(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn)
switch (op) { switch (op) {
case JSOP_SETLOCAL: case JSOP_SETLOCAL:
case JSOP_SETARG: case JSOP_SETARG:
case JSOP_SETALIASEDVAR:
case JSOP_SETNAME: case JSOP_SETNAME:
case JSOP_SETGNAME: case JSOP_SETGNAME:
maySet = true; maySet = true;

View File

@ -130,10 +130,24 @@ struct BytecodeEmitter
const bool hasGlobalScope:1; /* frontend::CompileScript's scope chain is the const bool hasGlobalScope:1; /* frontend::CompileScript's scope chain is the
global object */ global object */
const bool selfHostingMode:1; /* Emit JSOP_CALLINTRINSIC instead of JSOP_NAME enum EmitterMode {
and assert that JSOP_NAME and JSOP_*GNAME Normal,
don't ever get emitted. See the comment for
the field |selfHostingMode| in Parser.h for details. */ /*
* 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" * 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, BytecodeEmitter(BytecodeEmitter *parent, Parser<FullParseHandler> *parser, SharedContext *sc,
HandleScript script, bool insideEval, HandleScript evalCaller, 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 init();
bool isAliasedName(ParseNode *pn); bool isAliasedName(ParseNode *pn);

View File

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

View File

@ -15,6 +15,13 @@
namespace js { namespace js {
namespace frontend { 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 class FullParseHandler
{ {
ParseNodeAllocator allocator; ParseNodeAllocator allocator;
@ -34,17 +41,38 @@ class FullParseHandler
return node; 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: 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. */ /* new_ methods for creating parse nodes. These report OOM on context. */
JS_DECLARE_NEW_METHODS(new_, allocParseNode, inline) JS_DECLARE_NEW_METHODS(new_, allocParseNode, inline)
typedef ParseNode *Node; typedef ParseNode *Node;
typedef Definition *DefinitionNode; typedef Definition *DefinitionNode;
FullParseHandler(JSContext *cx, TokenStream &tokenStream, bool foldConstants) FullParseHandler(JSContext *cx, TokenStream &tokenStream, bool foldConstants,
Parser<SyntaxParseHandler> *syntaxParser, LazyScript *lazyOuterFunction)
: allocator(cx), : allocator(cx),
tokenStream(tokenStream), tokenStream(tokenStream),
foldConstants(foldConstants) foldConstants(foldConstants),
lazyOuterFunction_(lazyOuterFunction),
lazyInnerFunctionIndex(0),
syntaxParser(syntaxParser)
{} {}
static ParseNode *null() { return NULL; } static ParseNode *null() { return NULL; }
@ -61,8 +89,8 @@ class FullParseHandler
pn->setOp(JSOP_NAME); pn->setOp(JSOP_NAME);
return pn; return pn;
} }
Definition *newPlaceholder(ParseNode *pn, ParseContext<FullParseHandler> *pc) { Definition *newPlaceholder(JSAtom *atom, ParseContext<FullParseHandler> *pc) {
Definition *dn = (Definition *) NameNode::create(PNK_NAME, pn->pn_atom, this, pc); Definition *dn = (Definition *) NameNode::create(PNK_NAME, atom, this, pc);
if (!dn) if (!dn)
return NULL; return NULL;
@ -313,6 +341,17 @@ class FullParseHandler
static Definition *nullDefinition() { static Definition *nullDefinition() {
return NULL; return NULL;
} }
void disableSyntaxParser() {
syntaxParser = NULL;
}
LazyScript *lazyOuterFunction() {
return lazyOuterFunction_;
}
JSFunction *nextLazyInnerFunction() {
JS_ASSERT(lazyInnerFunctionIndex < lazyOuterFunction()->numInnerFunctions());
return lazyOuterFunction()->innerFunctions()[lazyInnerFunctionIndex++];
}
}; };
inline bool inline bool

View File

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

View File

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

View File

@ -59,6 +59,7 @@
using namespace js; using namespace js;
using namespace js::gc; using namespace js::gc;
using mozilla::Maybe;
namespace js { namespace js {
namespace frontend { namespace frontend {
@ -213,6 +214,10 @@ ParseContext<SyntaxParseHandler>::define(JSContext *cx, HandlePropertyName name,
if (lexdeps.lookupDefn<SyntaxParseHandler>(name)) if (lexdeps.lookupDefn<SyntaxParseHandler>(name))
lexdeps->remove(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); return decls_.addUnique(name, kind);
} }
@ -378,9 +383,27 @@ Parser<ParseHandler>::reportWithOffset(ParseReportKind kind, bool strict, uint32
return result; return result;
} }
template <>
bool
Parser<FullParseHandler>::abortIfSyntaxParser()
{
handler.disableSyntaxParser();
return true;
}
template <>
bool
Parser<SyntaxParseHandler>::abortIfSyntaxParser()
{
abortedSyntaxParse = true;
return false;
}
template <typename ParseHandler> template <typename ParseHandler>
Parser<ParseHandler>::Parser(JSContext *cx, const CompileOptions &options, 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), : AutoGCRooter(cx, PARSER),
context(cx), context(cx),
tokenStream(cx, options, chars, length, thisForCtor(), keepAtoms), tokenStream(cx, options, chars, length, thisForCtor(), keepAtoms),
@ -391,10 +414,19 @@ Parser<ParseHandler>::Parser(JSContext *cx, const CompileOptions &options,
foldConstants(foldConstants), foldConstants(foldConstants),
compileAndGo(options.compileAndGo), compileAndGo(options.compileAndGo),
selfHostingMode(options.selfHostingMode), selfHostingMode(options.selfHostingMode),
unknownResult(false), abortedSyntaxParse(false),
handler(cx, tokenStream, foldConstants) handler(cx, tokenStream, foldConstants, syntaxParser, lazyOuterFunction)
{ {
// XXX bug 678037 always disable syntax parsing for now.
handler.disableSyntaxParser();
cx->activeCompilations++; 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> template <typename ParseHandler>
@ -984,6 +1016,18 @@ template <>
bool bool
Parser<SyntaxParseHandler>::checkFunctionArguments() 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; return true;
} }
@ -1223,6 +1267,49 @@ MatchOrInsertSemicolon(JSContext *cx, TokenStream *ts)
return true; 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 * Beware: this function is called for functions nested in other functions or
* global scripts but not for functions compiled through the Function * 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()); JS_ASSERT(dn->isPlaceholder());
if (atom == funName && kind == Expression) { if (atom == funName && kind == Expression) {
dn->setOp(JSOP_CALLEE); if (!ConvertDefinitionToNamedLambdaUse(context, pc, funbox, dn))
if (!dn->pn_cookie.set(context, pc->staticLevel,
UpvarCookie::CALLEE_SLOT))
return false; 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; continue;
} }
@ -1293,38 +1362,30 @@ Parser<FullParseHandler>::leaveFunction(ParseNode *fn, HandlePropertyName funNam
handler.deoptimizeUsesWithin(dn, fn->pn_pos); handler.deoptimizeUsesWithin(dn, fn->pn_pos);
if (!outer_dn) { if (!outer_dn) {
AtomDefnAddPtr p = outerpc->lexdeps->lookupForAdd(atom); /*
if (p) { * Create a new placeholder for our outer lexdep. We could
outer_dn = p.value().get<FullParseHandler>(); * simply re-use the inner placeholder, but that introduces
} else { * subtleties in the case where we find a later definition
/* * that captures an existing lexdep. For example:
* Create a new placeholder for our outer lexdep. We could *
* simply re-use the inner placeholder, but that introduces * function f() { function g() { x; } let x; }
* subtleties in the case where we find a later definition *
* that captures an existing lexdep. For example: * Here, g's TOK_UPVARS node lists the placeholder for x,
* * which must be captured by the 'let' declaration later,
* function f() { function g() { x; } let x; } * since 'let's are hoisted. Taking g's placeholder as our
* * own would work fine. But consider:
* Here, g's TOK_UPVARS node lists the placeholder for x, *
* which must be captured by the 'let' declaration later, * function f() { x; { function g() { x; } let x; } }
* since 'let's are hoisted. Taking g's placeholder as our *
* own would work fine. But consider: * Here, the 'let' must not capture all the uses of f's
* * lexdep entry for x, but it must capture the x node
* function f() { x; { function g() { x; } let x; } } * referred to from g's TOK_UPVARS node. Always turning
* * inherited lexdeps into uses of a new outer definition
* Here, the 'let' must not capture all the uses of f's * allows us to handle both these cases in a natural way.
* lexdep entry for x, but it must capture the x node */
* referred to from g's TOK_UPVARS node. Always turning outer_dn = getOrCreateLexicalDependency(outerpc, atom);
* inherited lexdeps into uses of a new outer definition if (!outer_dn)
* allows us to handle both these cases in a natural way. return false;
*/
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;
}
} }
/* /*
@ -1363,11 +1424,7 @@ Parser<FullParseHandler>::leaveFunction(ParseNode *fn, HandlePropertyName funNam
InternalHandle<Bindings*> bindings = InternalHandle<Bindings*> bindings =
InternalHandle<Bindings*>::fromMarkedLocation(&funbox->bindings); InternalHandle<Bindings*>::fromMarkedLocation(&funbox->bindings);
if (!pc->generateFunctionBindings(context, bindings)) return pc->generateFunctionBindings(context, bindings);
return false;
pc->lexdeps.releaseMap(context);
return true;
} }
template <> template <>
@ -1376,8 +1433,10 @@ Parser<SyntaxParseHandler>::leaveFunction(Node fn, HandlePropertyName funName,
ParseContext<SyntaxParseHandler> *outerpc, ParseContext<SyntaxParseHandler> *outerpc,
FunctionSyntaxKind kind) FunctionSyntaxKind kind)
{ {
pc->lexdeps.releaseMap(context); outerpc->blockidGen = pc->blockidGen;
return true;
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 // Record the start of function source (for FunctionToString). If we
// are parenFreeArrow, we will set this below, after consuming the NAME. // are parenFreeArrow, we will set this below, after consuming the NAME.
funbox->bufStart = tokenStream.currentToken().pos.begin; funbox->setStart(tokenStream);
} }
hasRest = false; hasRest = false;
@ -1587,7 +1646,7 @@ Parser<ParseHandler>::functionArguments(FunctionSyntaxKind kind, Node *listp, No
case TOK_NAME: case TOK_NAME:
{ {
if (parenFreeArrow) if (parenFreeArrow)
funbox->bufStart = tokenStream.currentToken().pos.begin; funbox->setStart(tokenStream);
RootedPropertyName name(context, tokenStream.currentToken().name()); RootedPropertyName name(context, tokenStream.currentToken().name());
bool disallowDuplicateArgs = destructuringArg || hasDefaults; bool disallowDuplicateArgs = destructuringArg || hasDefaults;
@ -1643,9 +1702,11 @@ Parser<ParseHandler>::functionArguments(FunctionSyntaxKind kind, Node *listp, No
template <> template <>
bool bool
Parser<FullParseHandler>::checkFunctionDefinition(HandlePropertyName funName, Parser<FullParseHandler>::checkFunctionDefinition(HandlePropertyName funName,
ParseNode **pn_, FunctionSyntaxKind kind) ParseNode **pn_, FunctionSyntaxKind kind,
bool *pbodyProcessed)
{ {
ParseNode *&pn = *pn_; ParseNode *&pn = *pn_;
*pbodyProcessed = false;
/* Function statements add a binding to the enclosing scope. */ /* Function statements add a binding to the enclosing scope. */
bool bodyLevel = pc->atBodyLevel(); bool bodyLevel = pc->atBodyLevel();
@ -1712,7 +1773,6 @@ Parser<FullParseHandler>::checkFunctionDefinition(HandlePropertyName funName,
* statements (e.g., functions in an "if" or "while" block) are * statements (e.g., functions in an "if" or "while" block) are
* dynamically bound when control flow reaches the statement. The * dynamically bound when control flow reaches the statement. The
* emitter normally emits functions in two passes (see PNK_ARGSBODY). * emitter normally emits functions in two passes (see PNK_ARGSBODY).
* To distinguish
*/ */
if (bodyLevel) { if (bodyLevel) {
JS_ASSERT(pn->functionIsHoisted()); JS_ASSERT(pn->functionIsHoisted());
@ -1759,14 +1819,86 @@ Parser<FullParseHandler>::checkFunctionDefinition(HandlePropertyName funName,
pn->setOp(JSOP_LAMBDA); 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; return true;
} }
template <> template <>
bool bool
Parser<SyntaxParseHandler>::checkFunctionDefinition(HandlePropertyName funName, 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. */ /* Function statements add a binding to the enclosing scope. */
bool bodyLevel = pc->atBodyLevel(); bool bodyLevel = pc->atBodyLevel();
@ -1797,6 +1929,11 @@ Parser<SyntaxParseHandler>::checkFunctionDefinition(HandlePropertyName funName,
pc->sc->setBindingsAccessedDynamically(); pc->sc->setBindingsAccessedDynamically();
} }
if (kind == Arrow) {
/* Arrow functions cannot yet be parsed lazily. */
return abortIfSyntaxParser();
}
return true; return true;
} }
@ -1812,9 +1949,13 @@ Parser<ParseHandler>::functionDef(HandlePropertyName funName, const TokenStream:
if (!pn) if (!pn)
return null(); return null();
if (!checkFunctionDefinition(funName, &pn, kind)) bool bodyProcessed;
if (!checkFunctionDefinition(funName, &pn, kind, &bodyProcessed))
return null(); return null();
if (bodyProcessed)
return pn;
RootedFunction fun(context, newFunction(pc, funName, kind)); RootedFunction fun(context, newFunction(pc, funName, kind));
if (!fun) if (!fun)
return null(); return null();
@ -1825,8 +1966,8 @@ Parser<ParseHandler>::functionDef(HandlePropertyName funName, const TokenStream:
handler.setFunctionBody(pn, null()); handler.setFunctionBody(pn, null());
bool initiallyStrict = kind == Arrow || pc->sc->strict; bool initiallyStrict = kind == Arrow || pc->sc->strict;
bool becameStrict; bool becameStrict;
if (!functionArgsAndBody(pn, fun, funName, startOffset, type, kind, initiallyStrict, if (!functionArgsAndBody(pn, fun, funName, startOffset,
&becameStrict)) type, kind, initiallyStrict, &becameStrict))
{ {
if (initiallyStrict || !becameStrict || tokenStream.hadError()) if (initiallyStrict || !becameStrict || tokenStream.hadError())
return null(); return null();
@ -1846,22 +1987,10 @@ Parser<ParseHandler>::functionDef(HandlePropertyName funName, const TokenStream:
template <> template <>
bool bool
Parser<FullParseHandler>::finishFunctionDefinition(ParseNode *pn, FunctionBox *funbox, Parser<FullParseHandler>::finishFunctionDefinition(ParseNode *pn, FunctionBox *funbox,
ParseNode *prelude, ParseNode *body, ParseNode *prelude, ParseNode *body)
ParseContext<FullParseHandler> *outerpc)
{ {
pn->pn_pos.end = tokenStream.currentToken().pos.end; 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 JS_HAS_DESTRUCTURING
/* /*
* If there were destructuring formal parameters, prepend the initializing * 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_funbox = funbox;
pn->pn_body->append(body); pn->pn_body->append(body);
pn->pn_body->pn_pos = body->pn_pos; pn->pn_body->pn_pos = body->pn_pos;
pn->pn_blockid = outerpc->blockid();
return true; return true;
} }
@ -1908,21 +2036,144 @@ Parser<FullParseHandler>::finishFunctionDefinition(ParseNode *pn, FunctionBox *f
template <> template <>
bool bool
Parser<SyntaxParseHandler>::finishFunctionDefinition(Node pn, FunctionBox *funbox, Parser<SyntaxParseHandler>::finishFunctionDefinition(Node pn, FunctionBox *funbox,
Node prelude, Node body, Node prelude, Node body)
ParseContext<SyntaxParseHandler> *outerpc)
{ {
// 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; return true;
} }
template <typename ParseHandler> template <>
bool bool
Parser<ParseHandler>::functionArgsAndBody(Node pn, HandleFunction fun, HandlePropertyName funName, Parser<FullParseHandler>::functionArgsAndBody(ParseNode *pn, HandleFunction fun,
size_t startOffset, FunctionType type, HandlePropertyName funName,
FunctionSyntaxKind kind, bool strict, bool *becameStrict) size_t startOffset, FunctionType type,
FunctionSyntaxKind kind,
bool strict, bool *becameStrict)
{ {
if (becameStrict) if (becameStrict)
*becameStrict = false; *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. // Create box for fun->object early to protect against last-ditch GC.
FunctionBox *funbox = newFunctionBox(fun, pc, strict); FunctionBox *funbox = newFunctionBox(fun, pc, strict);
@ -1930,17 +2181,82 @@ Parser<ParseHandler>::functionArgsAndBody(Node pn, HandleFunction fun, HandlePro
return false; return false;
// Initialize early for possible flags mutation via destructuringExpr. // 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()) if (!funpc.init())
return false; 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(); Node prelude = null();
bool hasRest; bool hasRest;
if (!functionArguments(kind, &prelude, pn, hasRest)) if (!functionArguments(kind, &prelude, pn, hasRest))
return false; return false;
fun->setArgCount(funpc.numArgs()); FunctionBox *funbox = pc->sc->asFunctionBox();
fun->setArgCount(pc->numArgs());
if (funbox->ndefaults) if (funbox->ndefaults)
fun->setHasDefaults(); fun->setHasDefaults();
if (hasRest) if (hasRest)
@ -1961,7 +2277,7 @@ Parser<ParseHandler>::functionArgsAndBody(Node pn, HandleFunction fun, HandlePro
} }
// Parse the function body. // Parse the function body.
mozilla::Maybe<GenexpGuard<ParseHandler> > yieldGuard; Maybe<GenexpGuard<ParseHandler> > yieldGuard;
if (kind == Arrow) if (kind == Arrow)
yieldGuard.construct(this); yieldGuard.construct(this);
@ -2004,10 +2320,7 @@ Parser<ParseHandler>::functionArgsAndBody(Node pn, HandleFunction fun, HandlePro
} }
#endif #endif
if (!finishFunctionDefinition(pn, funbox, prelude, body, outerpc)) return finishFunctionDefinition(pn, funbox, prelude, body);
return false;
return leaveFunction(pn, funName, outerpc, kind);
} }
template <> template <>
@ -2050,7 +2363,7 @@ template <>
SyntaxParseHandler::Node SyntaxParseHandler::Node
Parser<SyntaxParseHandler>::moduleDecl() Parser<SyntaxParseHandler>::moduleDecl()
{ {
setUnknownResult(); JS_ALWAYS_FALSE(abortIfSyntaxParser());
return SyntaxParseHandler::NodeFailure; return SyntaxParseHandler::NodeFailure;
} }
@ -2179,6 +2492,8 @@ Parser<ParseHandler>::maybeParseDirective(Node pn, bool *cont)
if (pc->sc->isFunctionBox()) { if (pc->sc->isFunctionBox()) {
pc->sc->asFunctionBox()->useAsm = true; pc->sc->asFunctionBox()->useAsm = true;
pc->sc->asFunctionBox()->asmStart = handler.getPosition(pn).begin; pc->sc->asFunctionBox()->asmStart = handler.getPosition(pn).begin;
if (!abortIfSyntaxParser())
return false;
} else { } else {
if (!report(ParseWarning, false, pn, JSMSG_USE_ASM_DIRECTIVE_FAIL)) if (!report(ParseWarning, false, pn, JSMSG_USE_ASM_DIRECTIVE_FAIL))
return false; return false;
@ -2586,24 +2901,17 @@ Parser<ParseHandler>::noteNameUse(HandlePropertyName name, Node pn)
if (!defs.empty()) { if (!defs.empty()) {
dn = defs.front<ParseHandler>(); dn = defs.front<ParseHandler>();
} else { } else {
if (AtomDefnAddPtr p = pc->lexdeps->lookupForAdd(name)) { /*
dn = p.value().get<ParseHandler>(); * No definition before this use in any lexical scope.
} else { * Create a placeholder definition node to either:
/* * - Be adopted when we parse the real defining
* No definition before this use in any lexical scope. * declaration, or
* Create a placeholder definition node to either: * - Be left as a free variable definition if we never
* - Be adopted when we parse the real defining * see the real definition.
* declaration, or */
* - Be left as a free variable definition if we never dn = getOrCreateLexicalDependency(pc, name);
* see the real definition. if (!dn)
*/ return false;
dn = handler.newPlaceholder(pn, pc);
if (!dn)
return false;
DefinitionSingle def = DefinitionSingle::new_<ParseHandler>(dn);
if (!pc->lexdeps->add(p, name, def))
return false;
}
} }
handler.linkUseToDef(pn, dn); handler.linkUseToDef(pn, dn);
@ -2847,8 +3155,7 @@ bool
Parser<SyntaxParseHandler>::checkDestructuring(BindData<SyntaxParseHandler> *data, Parser<SyntaxParseHandler>::checkDestructuring(BindData<SyntaxParseHandler> *data,
Node left, bool toplevel) Node left, bool toplevel)
{ {
setUnknownResult(); return abortIfSyntaxParser();
return false;
} }
template <typename ParseHandler> template <typename ParseHandler>
@ -2889,6 +3196,9 @@ Parser<ParseHandler>::returnOrYield(bool useAssignExpr)
#if JS_HAS_GENERATORS #if JS_HAS_GENERATORS
if (tt == TOK_YIELD) { 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 * 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. * a |for| token, so we have to delay flagging the current function.
@ -3023,7 +3333,7 @@ template <>
SyntaxParseHandler::Node SyntaxParseHandler::Node
Parser<SyntaxParseHandler>::pushLetScope(HandleStaticBlockObject blockObj, StmtInfoPC *stmt) Parser<SyntaxParseHandler>::pushLetScope(HandleStaticBlockObject blockObj, StmtInfoPC *stmt)
{ {
setUnknownResult(); JS_ALWAYS_FALSE(abortIfSyntaxParser());
return SyntaxParseHandler::NodeFailure; return SyntaxParseHandler::NodeFailure;
} }
@ -3401,6 +3711,7 @@ Parser<FullParseHandler>::forStatement()
} }
#if JS_HAS_BLOCK_SCOPE #if JS_HAS_BLOCK_SCOPE
else if (tt == TOK_LET) { else if (tt == TOK_LET) {
handler.disableSyntaxParser();
(void) tokenStream.getToken(); (void) tokenStream.getToken();
if (tokenStream.peekToken() == TOK_LP) { if (tokenStream.peekToken() == TOK_LP) {
pn1 = letBlock(LetExpresion); pn1 = letBlock(LetExpresion);
@ -3695,6 +4006,12 @@ Parser<SyntaxParseHandler>::forStatement()
StmtInfoPC forStmt(context); StmtInfoPC forStmt(context);
PushStatementPC(pc, &forStmt, STMT_FOR_LOOP); 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); MUST_MATCH_TOKEN(TOK_LP, JSMSG_PAREN_AFTER_FOR);
/* True if we have 'for (var ...)'. */ /* True if we have 'for (var ...)'. */
@ -3718,7 +4035,7 @@ Parser<SyntaxParseHandler>::forStatement()
} }
#if JS_HAS_BLOCK_SCOPE #if JS_HAS_BLOCK_SCOPE
else if (tt == TOK_CONST || tt == TOK_LET) { else if (tt == TOK_CONST || tt == TOK_LET) {
setUnknownResult(); JS_ALWAYS_FALSE(abortIfSyntaxParser());
return null(); return null();
} }
#endif #endif
@ -3747,12 +4064,12 @@ Parser<SyntaxParseHandler>::forStatement()
lhsNode != SyntaxParseHandler::NodeName && lhsNode != SyntaxParseHandler::NodeName &&
lhsNode != SyntaxParseHandler::NodeLValue) lhsNode != SyntaxParseHandler::NodeLValue)
{ {
setUnknownResult(); JS_ALWAYS_FALSE(abortIfSyntaxParser());
return null(); return null();
} }
if (!simpleForDecl) { if (!simpleForDecl) {
setUnknownResult(); JS_ALWAYS_FALSE(abortIfSyntaxParser());
return null(); return null();
} }
@ -3961,6 +4278,9 @@ template <typename ParseHandler>
typename ParseHandler::Node typename ParseHandler::Node
Parser<ParseHandler>::withStatement() Parser<ParseHandler>::withStatement()
{ {
if (!abortIfSyntaxParser())
return null();
JS_ASSERT(tokenStream.isCurrentTokenType(TOK_WITH)); JS_ASSERT(tokenStream.isCurrentTokenType(TOK_WITH));
uint32_t begin = tokenStream.currentToken().pos.begin; uint32_t begin = tokenStream.currentToken().pos.begin;
@ -4017,6 +4337,8 @@ template <>
ParseNode * ParseNode *
Parser<FullParseHandler>::letStatement() Parser<FullParseHandler>::letStatement()
{ {
handler.disableSyntaxParser();
ParseNode *pn; ParseNode *pn;
do { do {
/* Check for a let statement or let expression. */ /* Check for a let statement or let expression. */
@ -4134,7 +4456,7 @@ template <>
SyntaxParseHandler::Node SyntaxParseHandler::Node
Parser<SyntaxParseHandler>::letStatement() Parser<SyntaxParseHandler>::letStatement()
{ {
setUnknownResult(); JS_ALWAYS_FALSE(abortIfSyntaxParser());
return SyntaxParseHandler::NodeFailure; return SyntaxParseHandler::NodeFailure;
} }
@ -4417,6 +4739,9 @@ Parser<ParseHandler>::statement()
break; break;
case TOK_CONST: case TOK_CONST:
if (!abortIfSyntaxParser())
return null();
pn = variables(PNK_CONST); pn = variables(PNK_CONST);
if (!pn) if (!pn)
return null(); return null();
@ -4873,10 +5198,8 @@ bool
Parser<SyntaxParseHandler>::setAssignmentLhsOps(Node pn, JSOp op) Parser<SyntaxParseHandler>::setAssignmentLhsOps(Node pn, JSOp op)
{ {
/* Full syntax checking of valid assignment LHS terms requires a parse tree. */ /* Full syntax checking of valid assignment LHS terms requires a parse tree. */
if (pn != SyntaxParseHandler::NodeName && pn != SyntaxParseHandler::NodeLValue) { if (pn != SyntaxParseHandler::NodeName && pn != SyntaxParseHandler::NodeLValue)
setUnknownResult(); return abortIfSyntaxParser();
return false;
}
return checkStrictAssignment(pn); return checkStrictAssignment(pn);
} }
@ -4919,6 +5242,8 @@ Parser<ParseHandler>::assignExpr()
case TOK_ARROW: { case TOK_ARROW: {
tokenStream.seek(start); tokenStream.seek(start);
if (!abortIfSyntaxParser())
return null();
if (tokenStream.getToken() == TOK_ERROR) if (tokenStream.getToken() == TOK_ERROR)
return null(); return null();
@ -5054,6 +5379,12 @@ Parser<SyntaxParseHandler>::checkDeleteExpression(Node *pn)
PropertyName *name = handler.isName(*pn); PropertyName *name = handler.isName(*pn);
if (name) if (name)
return report(ParseStrictError, pc->sc->strict, *pn, JSMSG_DEPRECATED_DELETE_OPERAND); 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; return true;
} }
@ -5409,7 +5740,7 @@ CompExprTransplanter::transplant(ParseNode *pn)
* generator) a use of a new placeholder in the generator's * generator) a use of a new placeholder in the generator's
* lexdeps. * lexdeps.
*/ */
Definition *dn2 = parser->handler.newPlaceholder(pn, parser->pc); Definition *dn2 = parser->handler.newPlaceholder(atom, parser->pc);
if (!dn2) if (!dn2)
return false; return false;
dn2->pn_pos = root->pn_pos; dn2->pn_pos = root->pn_pos;
@ -5491,6 +5822,18 @@ Parser<FullParseHandler>::comprehensionTail(ParseNode *kid, unsigned blockid, bo
ParseContext<FullParseHandler> *outerpc, ParseContext<FullParseHandler> *outerpc,
ParseNodeKind kind, JSOp op) 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; unsigned adjust;
ParseNode *pn, *pn2, *pn3, **pnp; ParseNode *pn, *pn2, *pn3, **pnp;
StmtInfoPC stmtInfo(context); StmtInfoPC stmtInfo(context);
@ -5746,8 +6089,7 @@ template <>
bool bool
Parser<SyntaxParseHandler>::arrayInitializerComprehensionTail(Node pn) Parser<SyntaxParseHandler>::arrayInitializerComprehensionTail(Node pn)
{ {
setUnknownResult(); return abortIfSyntaxParser();
return false;
} }
#if JS_HAS_GENERATOR_EXPRS #if JS_HAS_GENERATOR_EXPRS
@ -5853,7 +6195,7 @@ template <>
SyntaxParseHandler::Node SyntaxParseHandler::Node
Parser<SyntaxParseHandler>::generatorExpr(Node kid) Parser<SyntaxParseHandler>::generatorExpr(Node kid)
{ {
setUnknownResult(); JS_ALWAYS_FALSE(abortIfSyntaxParser());
return SyntaxParseHandler::NodeFailure; return SyntaxParseHandler::NodeFailure;
} }

View File

@ -40,6 +40,7 @@ typedef HashSet<JSAtom *> FuncStmtSet;
class SharedContext; class SharedContext;
typedef Vector<Definition *, 16> DeclVector; typedef Vector<Definition *, 16> DeclVector;
typedef Vector<JSFunction *, 4> FunctionVector;
struct GenericParseContext struct GenericParseContext
{ {
@ -203,6 +204,9 @@ struct ParseContext : public GenericParseContext
that will alias any top-level bindings with that will alias any top-level bindings with
the same name. */ 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 // Set when parsing a declaration-like destructuring pattern. This flag
// causes PrimaryExpr to create PN_NAME parse nodes for variable references // 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 // 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 LetContext { LetExpresion, LetStatement };
enum VarContext { HoistVars, DontHoistVars }; enum VarContext { HoistVars, DontHoistVars };
enum FunctionType { Getter, Setter, Normal };
template <typename ParseHandler> template <typename ParseHandler>
struct Parser : private AutoGCRooter, public StrictModeGetter 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 * is not known whether the parse succeeds or fails, this bit is set and
* the parse will return false. * the parse will return false.
*/ */
bool unknownResult; bool abortedSyntaxParse;
typedef typename ParseHandler::Node Node; typedef typename ParseHandler::Node Node;
typedef typename ParseHandler::DefinitionNode DefinitionNode; typedef typename ParseHandler::DefinitionNode DefinitionNode;
@ -319,7 +324,9 @@ struct Parser : private AutoGCRooter, public StrictModeGetter
...); ...);
Parser(JSContext *cx, const CompileOptions &options, 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(); ~Parser();
friend void AutoGCRooter::trace(JSTracer *trc); friend void AutoGCRooter::trace(JSTracer *trc);
@ -356,8 +363,11 @@ struct Parser : private AutoGCRooter, public StrictModeGetter
void trace(JSTracer *trc); void trace(JSTracer *trc);
bool hadUnknownResult() { bool hadAbortedSyntaxParse() {
return unknownResult; return abortedSyntaxParse;
}
void clearAbortedSyntaxParse() {
abortedSyntaxParse = false;
} }
private: 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 * Create a parse node with the given kind and op using the current token's
* atom. * atom.
*/ */
Node atomNode(ParseNodeKind kind, JSOp op); Node atomNode(ParseNodeKind kind, JSOp op);
void setUnknownResult() { inline bool abortIfSyntaxParser();
unknownResult = true;
}
public: public:
@ -384,6 +392,10 @@ struct Parser : private AutoGCRooter, public StrictModeGetter
Node fn, FunctionBox **funbox, bool strict, Node fn, FunctionBox **funbox, bool strict,
bool *becameStrict = NULL); 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 * Parse a function body. Pass StatementListBody if the body is a list of
* statements; pass ExpressionBody if the body is a single expression. * statements; pass ExpressionBody if the body is a single expression.
@ -391,6 +403,10 @@ struct Parser : private AutoGCRooter, public StrictModeGetter
enum FunctionBodyType { StatementListBody, ExpressionBody }; enum FunctionBodyType { StatementListBody, ExpressionBody };
Node functionBody(FunctionSyntaxKind kind, FunctionBodyType type); 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; } virtual bool strictMode() { return pc->sc->strict; }
private: private:
@ -439,7 +455,6 @@ struct Parser : private AutoGCRooter, public StrictModeGetter
/* /*
* Additional JS parsers. * Additional JS parsers.
*/ */
enum FunctionType { Getter, Setter, Normal };
bool functionArguments(FunctionSyntaxKind kind, Node *list, Node funcpn, bool &hasRest); bool functionArguments(FunctionSyntaxKind kind, Node *list, Node funcpn, bool &hasRest);
Node functionDef(HandlePropertyName name, const TokenStream::Position &start, 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); void addStatementToList(Node pn, Node kid, bool *hasFunctionStmt);
bool checkFunctionArguments(); bool checkFunctionArguments();
bool makeDefIntoUse(Definition *dn, Node pn, JSAtom *atom); bool makeDefIntoUse(Definition *dn, Node pn, JSAtom *atom);
bool checkFunctionDefinition(HandlePropertyName funName, Node *pn, FunctionSyntaxKind kind); bool checkFunctionDefinition(HandlePropertyName funName, Node *pn, FunctionSyntaxKind kind,
bool finishFunctionDefinition(Node pn, FunctionBox *funbox, bool *pbodyProcessed);
Node prelude, Node body, bool finishFunctionDefinition(Node pn, FunctionBox *funbox, Node prelude, Node body);
ParseContext<ParseHandler> *outerpc); bool addFreeVariablesFromLazyFunction(JSFunction *fun, ParseContext<ParseHandler> *pc);
bool isValidForStatementLHS(Node pn1, JSVersion version, bool isValidForStatementLHS(Node pn1, JSVersion version,
bool forDecl, bool forEach, bool forOf); 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 reportRedeclaration(Node pn, bool isConst, JSAtom *atom);
bool reportBadReturn(Node pn, ParseReportKind kind, unsigned errnum, unsigned anonerrnum); bool reportBadReturn(Node pn, ParseReportKind kind, unsigned errnum, unsigned anonerrnum);
bool checkFinalReturn(Node pn); bool checkFinalReturn(Node pn);
DefinitionNode getOrCreateLexicalDependency(ParseContext<ParseHandler> *pc, JSAtom *atom);
bool leaveFunction(Node fn, HandlePropertyName funName, bool leaveFunction(Node fn, HandlePropertyName funName,
ParseContext<ParseHandler> *outerpc, ParseContext<ParseHandler> *outerpc,

View File

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

View File

@ -10,9 +10,19 @@
namespace js { namespace js {
namespace frontend { 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 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; JSAtom *lastAtom;
TokenPos lastStringPos; TokenPos lastStringPos;
TokenStream &tokenStream; TokenStream &tokenStream;
@ -28,7 +38,8 @@ class SyntaxParseHandler
}; };
typedef Definition::Kind DefinitionNode; 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), : lastAtom(NULL),
tokenStream(tokenStream) tokenStream(tokenStream)
{} {}
@ -42,7 +53,7 @@ class SyntaxParseHandler
lastAtom = name; lastAtom = name;
return NodeName; return NodeName;
} }
DefinitionNode newPlaceholder(Node pn, ParseContext<SyntaxParseHandler> *pc) { DefinitionNode newPlaceholder(JSAtom *atom, ParseContext<SyntaxParseHandler> *pc) {
return Definition::PLACEHOLDER; return Definition::PLACEHOLDER;
} }
Node newAtom(ParseNodeKind kind, JSAtom *atom, JSOp op = JSOP_NOP) { Node newAtom(ParseNodeKind kind, JSAtom *atom, JSOp op = JSOP_NOP) {
@ -182,6 +193,8 @@ class SyntaxParseHandler
static DefinitionNode nullDefinition() { static DefinitionNode nullDefinition() {
return Definition::MISSING; return Definition::MISSING;
} }
void disableSyntaxParser() {
}
}; };
} // namespace frontend } // 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 JS_ALWAYS_INLINE uint32_t
TokenStream::SourceCoords::lineIndexOf(uint32_t offset) const TokenStream::SourceCoords::lineIndexOf(uint32_t offset) const
{ {
@ -251,9 +267,9 @@ TokenStream::TokenStream(JSContext *cx, const CompileOptions &options,
lookahead(), lookahead(),
lineno(options.lineno), lineno(options.lineno),
flags(), flags(),
linebase(base), linebase(base - options.column),
prevLinebase(NULL), prevLinebase(NULL),
userbuf(cx, base, length), userbuf(cx, base - options.column, length + options.column), // See comment below
filename(options.filename), filename(options.filename),
sourceMap(NULL), sourceMap(NULL),
listenerTSData(), listenerTSData(),
@ -268,6 +284,12 @@ TokenStream::TokenStream(JSContext *cx, const CompileOptions &options,
linebaseSkip(cx, &linebase), linebaseSkip(cx, &linebase),
prevLinebaseSkip(cx, &prevLinebase) 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) if (originPrincipals)
JS_HoldPrincipals(originPrincipals); JS_HoldPrincipals(originPrincipals);
@ -501,6 +523,19 @@ TokenStream::TokenBuf::findEOLMax(const jschar *p, size_t max)
return p; 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 void
TokenStream::tell(Position *pos) TokenStream::tell(Position *pos)
{ {
@ -530,6 +565,13 @@ TokenStream::seek(const Position &pos)
tokens[(cursor + 1 + i) & ntokensMask] = pos.lookaheadTokens[i]; tokens[(cursor + 1 + i) & ntokensMask] = pos.lookaheadTokens[i];
} }
void
TokenStream::seek(const Position &pos, const TokenStream &other)
{
srcCoords.fill(other.srcCoords);
seek(pos);
}
void void
TokenStream::positionAfterLastFunctionKeyword(Position &pos) TokenStream::positionAfterLastFunctionKeyword(Position &pos)
{ {

View File

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

View File

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

View File

@ -81,6 +81,7 @@ namespace gc {
static void MarkChildren(JSTracer *trc, JSString *str); static void MarkChildren(JSTracer *trc, JSString *str);
static void MarkChildren(JSTracer *trc, JSScript *script); 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, Shape *shape);
static void MarkChildren(JSTracer *trc, BaseShape *base); static void MarkChildren(JSTracer *trc, BaseShape *base);
static void MarkChildren(JSTracer *trc, types::TypeObject *type); static void MarkChildren(JSTracer *trc, types::TypeObject *type);
@ -360,6 +361,7 @@ DeclMarkerImpl(Object, JSObject)
DeclMarkerImpl(Object, JSFunction) DeclMarkerImpl(Object, JSFunction)
DeclMarkerImpl(Object, ScopeObject) DeclMarkerImpl(Object, ScopeObject)
DeclMarkerImpl(Script, JSScript) DeclMarkerImpl(Script, JSScript)
DeclMarkerImpl(LazyScript, LazyScript)
DeclMarkerImpl(Shape, Shape) DeclMarkerImpl(Shape, Shape)
DeclMarkerImpl(String, JSAtom) DeclMarkerImpl(String, JSAtom)
DeclMarkerImpl(String, JSString) DeclMarkerImpl(String, JSString)
@ -390,6 +392,9 @@ gc::MarkKind(JSTracer *trc, void **thingp, JSGCTraceKind kind)
case JSTRACE_SCRIPT: case JSTRACE_SCRIPT:
MarkInternal(trc, reinterpret_cast<JSScript **>(thingp)); MarkInternal(trc, reinterpret_cast<JSScript **>(thingp));
break; break;
case JSTRACE_LAZY_SCRIPT:
MarkInternal(trc, reinterpret_cast<LazyScript **>(thingp));
break;
case JSTRACE_SHAPE: case JSTRACE_SHAPE:
MarkInternal(trc, reinterpret_cast<Shape **>(thingp)); MarkInternal(trc, reinterpret_cast<Shape **>(thingp));
break; break;
@ -792,6 +797,20 @@ PushMarkStack(GCMarker *gcmarker, JSScript *thing)
MarkChildren(gcmarker, 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 static void
ScanShape(GCMarker *gcmarker, Shape *shape); ScanShape(GCMarker *gcmarker, Shape *shape);
@ -997,6 +1016,12 @@ gc::MarkChildren(JSTracer *trc, JSScript *script)
script->markChildren(trc); script->markChildren(trc);
} }
static void
gc::MarkChildren(JSTracer *trc, LazyScript *lazy)
{
lazy->markChildren(trc);
}
static void static void
gc::MarkChildren(JSTracer *trc, Shape *shape) gc::MarkChildren(JSTracer *trc, Shape *shape)
{ {
@ -1153,6 +1178,10 @@ gc::PushArena(GCMarker *gcmarker, ArenaHeader *aheader)
PushArenaTyped<JSScript>(gcmarker, aheader); PushArenaTyped<JSScript>(gcmarker, aheader);
break; break;
case JSTRACE_LAZY_SCRIPT:
PushArenaTyped<LazyScript>(gcmarker, aheader);
break;
case JSTRACE_SHAPE: case JSTRACE_SHAPE:
PushArenaTyped<js::Shape>(gcmarker, aheader); PushArenaTyped<js::Shape>(gcmarker, aheader);
break; break;
@ -1501,6 +1530,10 @@ js::TraceChildren(JSTracer *trc, void *thing, JSGCTraceKind kind)
MarkChildren(trc, static_cast<JSScript *>(thing)); MarkChildren(trc, static_cast<JSScript *>(thing));
break; break;
case JSTRACE_LAZY_SCRIPT:
MarkChildren(trc, static_cast<LazyScript *>(thing));
break;
case JSTRACE_SHAPE: case JSTRACE_SHAPE:
MarkChildren(trc, static_cast<Shape *>(thing)); MarkChildren(trc, static_cast<Shape *>(thing));
break; break;

View File

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

View File

@ -1550,7 +1550,7 @@ CodeGenerator::visitCallGeneric(LCallGeneric *call)
masm.branchIfFunctionHasNoScript(calleereg, &uncompiled); masm.branchIfFunctionHasNoScript(calleereg, &uncompiled);
// Knowing that calleereg is a non-native function, load the JSScript. // 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. // Load script jitcode.
masm.loadBaselineOrIonRaw(objreg, objreg, executionMode, &uncompiled); 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. // 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. // Load script jitcode.
if (call->mir()->needsArgCheck()) if (call->mir()->needsArgCheck())
@ -1894,7 +1894,7 @@ CodeGenerator::visitApplyArgsGeneric(LApplyArgsGeneric *apply)
} }
// Knowing that calleereg is a non-native function, load the JSScript. // 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. // Load script jitcode.
masm.loadBaselineOrIonRaw(objreg, objreg, executionMode, &invoke); masm.loadBaselineOrIonRaw(objreg, objreg, executionMode, &invoke);

View File

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

View File

@ -416,7 +416,7 @@ IonRuntime::generateArgumentsRectifier(JSContext *cx, ExecutionMode mode, void *
// Call the target function. // Call the target function.
// Note that this code assumes the function is JITted. // 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.loadBaselineOrIonRaw(r3, r3, mode, NULL);
masm.ma_callIonHalfPush(r3); masm.ma_callIonHalfPush(r3);

View File

@ -388,7 +388,7 @@ IonRuntime::generateArgumentsRectifier(JSContext *cx, ExecutionMode mode, void *
// Call the target function. // Call the target function.
// Note that this code assumes the function is JITted. // 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.loadBaselineOrIonRaw(rax, rax, mode, NULL);
masm.call(rax); masm.call(rax);
uint32_t returnOffset = masm.currentOffset(); uint32_t returnOffset = masm.currentOffset();

View File

@ -379,7 +379,7 @@ IonRuntime::generateArgumentsRectifier(JSContext *cx, ExecutionMode mode, void *
// Call the target function. // Call the target function.
// Note that this assumes the function is JITted. // 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.loadBaselineOrIonRaw(eax, eax, mode, NULL);
masm.call(eax); masm.call(eax);
uint32_t returnOffset = masm.currentOffset(); uint32_t returnOffset = masm.currentOffset();

View File

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

View File

@ -3894,10 +3894,12 @@ struct JS_PUBLIC_API(CompileOptions) {
bool utf8; bool utf8;
const char *filename; const char *filename;
unsigned lineno; unsigned lineno;
unsigned column;
bool compileAndGo; bool compileAndGo;
bool forEval; bool forEval;
bool noScriptRval; bool noScriptRval;
bool selfHostingMode; bool selfHostingMode;
bool canLazilyParse;
enum SourcePolicy { enum SourcePolicy {
NO_SOURCE, NO_SOURCE,
LAZY_SOURCE, LAZY_SOURCE,
@ -3912,10 +3914,12 @@ struct JS_PUBLIC_API(CompileOptions) {
CompileOptions &setFileAndLine(const char *f, unsigned l) { CompileOptions &setFileAndLine(const char *f, unsigned l) {
filename = f; lineno = l; return *this; filename = f; lineno = l; return *this;
} }
CompileOptions &setColumn(unsigned c) { column = c; return *this; }
CompileOptions &setCompileAndGo(bool cng) { compileAndGo = cng; return *this; } CompileOptions &setCompileAndGo(bool cng) { compileAndGo = cng; return *this; }
CompileOptions &setForEval(bool eval) { forEval = eval; return *this; } CompileOptions &setForEval(bool eval) { forEval = eval; return *this; }
CompileOptions &setNoScriptRval(bool nsr) { noScriptRval = nsr; return *this; } CompileOptions &setNoScriptRval(bool nsr) { noScriptRval = nsr; return *this; }
CompileOptions &setSelfHostingMode(bool shm) { selfHostingMode = shm; 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; } 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)); JSString::writeBarrierPre(static_cast<JSString*>(cell));
else if (kind == JSTRACE_SCRIPT) else if (kind == JSTRACE_SCRIPT)
JSScript::writeBarrierPre(static_cast<JSScript*>(cell)); JSScript::writeBarrierPre(static_cast<JSScript*>(cell));
else if (kind == JSTRACE_LAZY_SCRIPT)
LazyScript::writeBarrierPre(static_cast<LazyScript*>(cell));
else if (kind == JSTRACE_SHAPE) else if (kind == JSTRACE_SHAPE)
Shape::writeBarrierPre(static_cast<Shape*>(cell)); Shape::writeBarrierPre(static_cast<Shape*>(cell));
else if (kind == JSTRACE_BASE_SHAPE) else if (kind == JSTRACE_BASE_SHAPE)

View File

@ -491,8 +491,13 @@ JSFunction::trace(JSTracer *trc)
MarkString(trc, &atom_, "atom"); MarkString(trc, &atom_, "atom");
if (isInterpreted()) { if (isInterpreted()) {
if (hasScript()) // Functions can be be marked as interpreted despite having no script
MarkScriptUnbarriered(trc, &u.i.script_, "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_) if (u.i.env_)
MarkObjectUnbarriered(trc, &u.i.env_, "fun_callscope"); MarkObjectUnbarriered(trc, &u.i.env_, "fun_callscope");
} }
@ -584,8 +589,8 @@ FindBody(JSContext *cx, HandleFunction fun, StableCharPtr chars, size_t length,
JSString * JSString *
js::FunctionToString(JSContext *cx, HandleFunction fun, bool bodyOnly, bool lambdaParen) js::FunctionToString(JSContext *cx, HandleFunction fun, bool bodyOnly, bool lambdaParen)
{ {
StringBuffer out(cx); if (fun->isInterpretedLazy() && !fun->getOrCreateScript(cx))
RootedScript script(cx); return NULL;
// If the object is an automatically-bound arrow function, get the source // If the object is an automatically-bound arrow function, get the source
// of the pre-binding target. // 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); return FunctionToString(cx, targetFun, bodyOnly, lambdaParen);
} }
StringBuffer out(cx);
RootedScript script(cx);
if (fun->hasScript()) { if (fun->hasScript()) {
script = fun->nonLazyScript(); script = fun->nonLazyScript();
if (script->isGeneratorExp) { if (script->isGeneratorExp) {
@ -1061,17 +1069,65 @@ JSFunction::getBoundFunctionArgumentCount() const
return getSlot(JSSLOT_BOUND_FUNCTION_ARGS_COUNT).toPrivateUint32(); return getSlot(JSSLOT_BOUND_FUNCTION_ARGS_COUNT).toPrivateUint32();
} }
bool /* static */ bool
JSFunction::initializeLazyScript(JSContext *cx) JSFunction::createScriptForLazilyInterpretedFunction(JSContext *cx, HandleFunction fun)
{ {
JS_ASSERT(isInterpretedLazy()); JS_ASSERT(fun->isInterpretedLazy());
const JSFunctionSpec *fs = static_cast<JSFunctionSpec *>(getExtendedSlot(0).toPrivate());
Rooted<JSFunction*> self(cx, this); 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))); RootedAtom funAtom(cx, Atomize(cx, fs->selfHostedName, strlen(fs->selfHostedName)));
if (!funAtom) if (!funAtom)
return false; return false;
Rooted<PropertyName *> funName(cx, funAtom->asPropertyName()); 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. */ /* 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() && bool useSameScript = cx->compartment == fun->compartment() &&
!fun->hasSingletonType() && !fun->hasSingletonType() &&
!types::UseNewTypeForClone(fun); !types::UseNewTypeForClone(fun);
if (!useSameScript && fun->isInterpretedLazy() && !fun->getOrCreateScript(cx))
return NULL;
NewObjectKind newKind = useSameScript ? newKindArg : SingletonObject; NewObjectKind newKind = useSameScript ? newKindArg : SingletonObject;
JSObject *cloneobj = NewObjectWithClassProto(cx, &FunctionClass, NULL, SkipScopeParent(parent), JSObject *cloneobj = NewObjectWithClassProto(cx, &FunctionClass, NULL, SkipScopeParent(parent),
allocKind, newKind); allocKind, newKind);
@ -1513,16 +1573,13 @@ js::CloneFunctionObject(JSContext *cx, HandleFunction fun, HandleObject parent,
clone->nargs = fun->nargs; clone->nargs = fun->nargs;
clone->flags = fun->flags & ~JSFunction::EXTENDED; clone->flags = fun->flags & ~JSFunction::EXTENDED;
if (fun->isInterpreted()) { if (fun->hasScript()) {
if (fun->isInterpretedLazy()) {
RootedFunction cloneRoot(cx, clone);
AutoCompartment ac(cx, fun);
if (!fun->getOrCreateScript(cx))
return NULL;
clone = cloneRoot;
}
clone->initScript(fun->nonLazyScript()); clone->initScript(fun->nonLazyScript());
clone->initEnvironment(parent); clone->initEnvironment(parent);
} else if (fun->isInterpretedLazy()) {
LazyScript *lazy = fun->lazyScriptOrNull();
clone->initLazyScript(lazy);
clone->initEnvironment(parent);
} else { } else {
clone->initNative(fun->native(), fun->jitInfo()); clone->initNative(fun->native(), fun->jitInfo());
} }

View File

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

View File

@ -168,13 +168,32 @@ SkipScopeParent(JSObject *parent)
return 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 * inline JSFunction *
CloneFunctionObjectIfNotSingleton(JSContext *cx, HandleFunction fun, HandleObject parent, CloneFunctionObjectIfNotSingleton(JSContext *cx, HandleFunction fun, HandleObject parent,
NewObjectKind newKind = GenericObject) NewObjectKind newKind = GenericObject)
{ {
/* /*
* For attempts to clone functions at a function definition opcode, * 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 * was called pessimistically, and we need to preserve the type's
* property that if it is singleton there is only a single object * property that if it is singleton there is only a single object
* with its type in existence. * with its type in existence.
@ -184,16 +203,12 @@ CloneFunctionObjectIfNotSingleton(JSContext *cx, HandleFunction fun, HandleObjec
* cases, fall through to CloneFunctionObject, which will deep clone * cases, fall through to CloneFunctionObject, which will deep clone
* the function's script. * the function's script.
*/ */
if (fun->hasSingletonType()) { if (CanReuseFunctionForClone(cx, fun)) {
RootedScript script(cx, fun->getOrCreateScript(cx)); RootedObject obj(cx, SkipScopeParent(parent));
if (!script->hasBeenCloned) { if (!JSObject::setParent(cx, fun, obj))
script->hasBeenCloned = true; return NULL;
Rooted<JSObject*> obj(cx, SkipScopeParent(parent)); fun->setEnvironment(parent);
if (!JSObject::setParent(cx, fun, obj)) return fun;
return NULL;
fun->setEnvironment(parent);
return fun;
}
} }
// These intermediate variables are needed to avoid link errors on some // These intermediate variables are needed to avoid link errors on some
@ -222,6 +237,17 @@ JSFunction::initScript(JSScript *script_)
mutableScript().init(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 * inline JSObject *
JSFunction::getBoundFunctionTarget() const JSFunction::getBoundFunctionTarget() const
{ {

View File

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

View File

@ -119,6 +119,7 @@ MapAllocToTraceKind(AllocKind kind)
JSTRACE_OBJECT, /* FINALIZE_OBJECT16 */ JSTRACE_OBJECT, /* FINALIZE_OBJECT16 */
JSTRACE_OBJECT, /* FINALIZE_OBJECT16_BACKGROUND */ JSTRACE_OBJECT, /* FINALIZE_OBJECT16_BACKGROUND */
JSTRACE_SCRIPT, /* FINALIZE_SCRIPT */ JSTRACE_SCRIPT, /* FINALIZE_SCRIPT */
JSTRACE_LAZY_SCRIPT,/* FINALIZE_LAZY_SCRIPT */
JSTRACE_SHAPE, /* FINALIZE_SHAPE */ JSTRACE_SHAPE, /* FINALIZE_SHAPE */
JSTRACE_BASE_SHAPE, /* FINALIZE_BASE_SHAPE */ JSTRACE_BASE_SHAPE, /* FINALIZE_BASE_SHAPE */
JSTRACE_TYPE_OBJECT,/* FINALIZE_TYPE_OBJECT */ 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<GlobalObject> { const static JSGCTraceKind kind = JSTRACE_OBJECT; };
template <> struct MapTypeToTraceKind<ScopeObject> { 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<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<Shape> { const static JSGCTraceKind kind = JSTRACE_SHAPE; };
template <> struct MapTypeToTraceKind<BaseShape> { const static JSGCTraceKind kind = JSTRACE_BASE_SHAPE; }; template <> struct MapTypeToTraceKind<BaseShape> { const static JSGCTraceKind kind = JSTRACE_BASE_SHAPE; };
template <> struct MapTypeToTraceKind<UnownedBaseShape> { 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 */ false, /* FINALIZE_OBJECT16 */
true, /* FINALIZE_OBJECT16_BACKGROUND */ true, /* FINALIZE_OBJECT16_BACKGROUND */
false, /* FINALIZE_SCRIPT */ false, /* FINALIZE_SCRIPT */
false, /* FINALIZE_LAZY_SCRIPT */
false, /* FINALIZE_SHAPE */ false, /* FINALIZE_SHAPE */
false, /* FINALIZE_BASE_SHAPE */ false, /* FINALIZE_BASE_SHAPE */
false, /* FINALIZE_TYPE_OBJECT */ false, /* FINALIZE_TYPE_OBJECT */
@ -201,6 +204,7 @@ IsBackgroundFinalized(AllocKind kind)
false, /* FINALIZE_OBJECT16 */ false, /* FINALIZE_OBJECT16 */
true, /* FINALIZE_OBJECT16_BACKGROUND */ true, /* FINALIZE_OBJECT16_BACKGROUND */
false, /* FINALIZE_SCRIPT */ false, /* FINALIZE_SCRIPT */
true, /* FINALIZE_LAZY_SCRIPT */
true, /* FINALIZE_SHAPE */ true, /* FINALIZE_SHAPE */
true, /* FINALIZE_BASE_SHAPE */ true, /* FINALIZE_BASE_SHAPE */
true, /* FINALIZE_TYPE_OBJECT */ true, /* FINALIZE_TYPE_OBJECT */

View File

@ -591,6 +591,13 @@ js_NewGCScript(JSContext *cx)
sizeof(JSScript), js::gc::TenuredHeap); 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 * inline js::Shape *
js_NewGCShape(JSContext *cx) js_NewGCShape(JSContext *cx)
{ {

View File

@ -5949,20 +5949,12 @@ JSScript::makeAnalysis(JSContext *cx)
/* static */ bool /* static */ bool
JSFunction::setTypeForScriptedFunction(JSContext *cx, HandleFunction fun, bool singleton /* = false */) JSFunction::setTypeForScriptedFunction(JSContext *cx, HandleFunction fun, bool singleton /* = false */)
{ {
JS_ASSERT(fun->nonLazyScript());
JS_ASSERT(fun->nonLazyScript()->function() == fun);
if (!cx->typeInferenceEnabled()) if (!cx->typeInferenceEnabled())
return true; return true;
if (singleton) { if (singleton) {
if (!setSingletonType(cx, fun)) if (!setSingletonType(cx, fun))
return false; 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 { } else {
RootedObject funProto(cx, fun->getProto()); RootedObject funProto(cx, fun->getProto());
TypeObject *type = cx->compartment->types.newTypeObject(cx, &FunctionClass, funProto); TypeObject *type = cx->compartment->types.newTypeObject(cx, &FunctionClass, funProto);

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -3069,7 +3069,9 @@ reflect_parse(JSContext *cx, uint32_t argc, jsval *vp)
size_t length = stable->length(); size_t length = stable->length();
CompileOptions options(cx); CompileOptions options(cx);
options.setFileAndLine(filename, lineno); 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()) if (!parser.init())
return JS_FALSE; return JS_FALSE;

View File

@ -1213,17 +1213,15 @@ SourceDataCache::purge()
map_ = NULL; map_ = NULL;
} }
JSStableString * const jschar *
ScriptSource::substring(JSContext *cx, uint32_t start, uint32_t stop) ScriptSource::chars(JSContext *cx)
{ {
const jschar *chars;
#ifdef USE_ZLIB #ifdef USE_ZLIB
Rooted<JSStableString *> cached(cx, NULL); Rooted<JSStableString *> cached(cx, NULL);
#endif #endif
#ifdef JS_THREADSAFE #ifdef JS_THREADSAFE
if (!ready()) { if (!ready())
chars = cx->runtime->sourceCompressorThread.currentChars(); return cx->runtime->sourceCompressorThread.currentChars();
} else
#endif #endif
#ifdef USE_ZLIB #ifdef USE_ZLIB
if (compressed()) { if (compressed()) {
@ -1247,14 +1245,21 @@ ScriptSource::substring(JSContext *cx, uint32_t start, uint32_t stop)
} }
cx->runtime->sourceDataCache.put(this, cached); cx->runtime->sourceDataCache.put(this, cached);
} }
chars = cached->chars().get(); return cached->chars().get();
JS_ASSERT(chars);
} else {
chars = data.source;
} }
return data.source;
#else #else
chars = data.source; return data.source;
#endif #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); JSFlatString *flatStr = js_NewStringCopyN<CanGC>(cx, chars + start, stop - start);
if (!flatStr) if (!flatStr)
return NULL; return NULL;
@ -1667,6 +1672,8 @@ JSScript::Create(JSContext *cx, HandleObject enclosingScope, bool savedCallerFun
const CompileOptions &options, unsigned staticLevel, const CompileOptions &options, unsigned staticLevel,
JS::HandleScriptSource sourceObject, uint32_t bufStart, uint32_t bufEnd) JS::HandleScriptSource sourceObject, uint32_t bufStart, uint32_t bufEnd)
{ {
JS_ASSERT(bufStart <= bufEnd);
RootedScript script(cx, js_NewGCScript(cx)); RootedScript script(cx, js_NewGCScript(cx));
if (!script) if (!script)
return NULL; return NULL;
@ -1958,7 +1965,7 @@ JSScript::enclosingScriptsCompiledSuccessfully() const
while (enclosing) { while (enclosing) {
if (enclosing->isFunction()) { if (enclosing->isFunction()) {
JSFunction *fun = enclosing->toFunction(); JSFunction *fun = enclosing->toFunction();
if (!fun->hasScript()) if (!fun->hasScript() || !fun->nonLazyScript())
return false; return false;
enclosing = fun->nonLazyScript()->enclosingStaticScope(); enclosing = fun->nonLazyScript()->enclosingStaticScope();
} else { } else {
@ -2294,6 +2301,8 @@ js::CloneScript(JSContext *cx, HandleObject enclosingScope, HandleFunction fun,
clone = CloneStaticBlockObject(cx, enclosingScope, innerBlock); clone = CloneStaticBlockObject(cx, enclosingScope, innerBlock);
} else if (obj->isFunction()) { } else if (obj->isFunction()) {
RootedFunction innerFun(cx, obj->toFunction()); RootedFunction innerFun(cx, obj->toFunction());
if (!innerFun->getOrCreateScript(cx))
return NULL;
RootedObject staticScope(cx, innerFun->nonLazyScript()->enclosingStaticScope()); RootedObject staticScope(cx, innerFun->nonLazyScript()->enclosingStaticScope());
StaticScopeIter ssi(cx, staticScope); StaticScopeIter ssi(cx, staticScope);
RootedObject enclosingScope(cx); RootedObject enclosingScope(cx);
@ -2736,6 +2745,31 @@ JSScript::markChildren(JSTracer *trc)
#endif #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 void
JSScript::setArgumentsHasVarBinding() JSScript::setArgumentsHasVarBinding()
{ {
@ -2882,6 +2916,29 @@ JSScript::formalLivesInArgumentsObject(unsigned argSlot)
return argsObjAliasesFormals() && !formalIsAliased(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 void
JSScript::updateBaselineOrIonRaw() JSScript::updateBaselineOrIonRaw()
{ {

View File

@ -336,6 +336,7 @@ class JSScript : public js::gc::Cell
uint32_t natoms; /* length of atoms array */ uint32_t natoms; /* length of atoms array */
/* Range of characters in scriptSource which contains this script's source. */
uint32_t sourceStart; uint32_t sourceStart;
uint32_t sourceEnd; 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 isCachedEval:1; /* script came from eval(), and is in eval cache */
bool uninlineable:1; /* script is considered uninlineable by analysis */ 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 /* script is attempted to be cloned anew at each callsite. This is
temporarily needed for ParallelArray selfhosted code until type temporarily needed for ParallelArray selfhosted code until type
information can be made context sensitive. See discussion in information can be made context sensitive. See discussion in
@ -792,6 +796,11 @@ class JSScript : public js::gc::Cell
return arr->vector[index]; 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) { JSObject *getObject(jsbytecode *pc) {
JS_ASSERT(pc >= code && pc + sizeof(uint32_t) < code + length); JS_ASSERT(pc >= code && pc + sizeof(uint32_t) < code + length);
return getObject(GET_UINT32_INDEX(pc)); return getObject(GET_UINT32_INDEX(pc));
@ -1041,6 +1050,7 @@ struct ScriptSource
JS_ASSERT(hasSourceData()); JS_ASSERT(hasSourceData());
return argumentsNotIncluded_; return argumentsNotIncluded_;
} }
const jschar *chars(JSContext *cx);
JSStableString *substring(JSContext *cx, uint32_t start, uint32_t stop); JSStableString *substring(JSContext *cx, uint32_t start, uint32_t stop);
size_t sizeOfIncludingThis(JSMallocSizeOfFun mallocSizeOf); size_t sizeOfIncludingThis(JSMallocSizeOfFun mallocSizeOf);
@ -1103,6 +1113,161 @@ class ScriptSourceObject : public JSObject {
static const uint32_t SOURCE_SLOT = 0; 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 #ifdef JS_THREADSAFE
/* /*
* Background thread to compress JS source code. This happens only while parsing * 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 * inline JSPrincipals *
JSScript::principals() JSScript::principals()
{ {

View File

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

View File

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

View File

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

View File

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

View File

@ -1662,6 +1662,11 @@ ReportZoneStats(const JS::ZoneStats &zStats,
"heap that holds over-sized string headers, in which " "heap that holds over-sized string headers, in which "
"string characters are stored inline."); "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"), ZCREPORT_GC_BYTES(pathPrefix + NS_LITERAL_CSTRING("gc-heap/type-objects"),
zStats.gcHeapTypeObjects, zStats.gcHeapTypeObjects,
"Memory on the garbage-collected JavaScript " "Memory on the garbage-collected JavaScript "
@ -1673,6 +1678,11 @@ ReportZoneStats(const JS::ZoneStats &zStats,
"heap that holds references to executable code pools " "heap that holds references to executable code pools "
"used by the IonMonkey JIT."); "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"), ZCREPORT_BYTES(pathPrefix + NS_LITERAL_CSTRING("type-objects"),
zStats.typeObjects, zStats.typeObjects,
"Memory holding miscellaneous additional information associated with type " "Memory holding miscellaneous additional information associated with type "

View File

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

View File

@ -196,6 +196,58 @@ class Maybe
constructed = true; 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() { T* addr() {
MOZ_ASSERT(constructed); MOZ_ASSERT(constructed);
return &asT(); return &asT();