Bug 678037 - Add (disabled) ability to parse script bytecode lazily, r=luke.
This commit is contained in:
@@ -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))
|
||||
|
||||
Reference in New Issue
Block a user