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 92d13b7935
commit d42bb0d1bf
50 changed files with 1587 additions and 378 deletions

View File

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