mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Bug 678037 - Add (disabled) ability to parse script bytecode lazily, r=luke.
This commit is contained in:
parent
d3b1307dba
commit
ddbb9d9519
@ -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;
|
||||||
|
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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) {
|
||||||
|
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;
|
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;
|
|
||||||
JS_ASSERT(tt == TOK_ERROR);
|
|
||||||
cx->clearPendingException();
|
|
||||||
return false;
|
return false;
|
||||||
}
|
|
||||||
|
|
||||||
if (!parser.statement()) {
|
script->bindings = pn->pn_funbox->bindings;
|
||||||
cx->clearPendingException();
|
|
||||||
|
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 false;
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
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)
|
||||||
|
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;
|
return false;
|
||||||
|
strict = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
parser.tokenStream.seek(start);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!NameFunctions(cx, pn))
|
if (!NameFunctions(cx, pn))
|
||||||
|
@ -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,
|
||||||
|
@ -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,11 +1096,87 @@ 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.
|
||||||
|
*/
|
||||||
|
static bool
|
||||||
|
TryConvertFreeName(BytecodeEmitter *bce, ParseNode *pn)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* In self-hosting mode, JSOP_*NAME is unconditionally converted to
|
||||||
|
* JSOP_*INTRINSIC. This causes lookups to be redirected to the special
|
||||||
|
* intrinsics holder in the global object, into which any missing values are
|
||||||
|
* cloned lazily upon first access.
|
||||||
|
*/
|
||||||
|
if (bce->emitterMode == BytecodeEmitter::SelfHosting) {
|
||||||
|
JSOp op;
|
||||||
|
switch (pn->getOp()) {
|
||||||
|
case JSOP_NAME: op = JSOP_GETINTRINSIC; break;
|
||||||
|
case JSOP_SETNAME: op = JSOP_SETINTRINSIC; break;
|
||||||
|
/* Other *NAME ops aren't (yet) supported in self-hosted code. */
|
||||||
|
default: JS_NOT_REACHED("intrinsic");
|
||||||
|
}
|
||||||
|
pn->setOp(op);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* When parsing inner functions lazily, parse nodes for outer functions no
|
||||||
|
* longer exist and only the function's scope chain is available for
|
||||||
|
* resolving upvar accesses within the inner function.
|
||||||
|
*/
|
||||||
|
if (bce->emitterMode == BytecodeEmitter::LazyFunction) {
|
||||||
|
size_t hops = 0;
|
||||||
|
FunctionBox *funbox = bce->sc->asFunctionBox();
|
||||||
|
if (funbox->hasExtensibleScope())
|
||||||
|
return false;
|
||||||
|
if (funbox->function()->atom() == pn->pn_atom)
|
||||||
|
return false;
|
||||||
|
if (funbox->function()->isHeavyweight()) {
|
||||||
|
hops++;
|
||||||
|
if (funbox->function()->isNamedLambda())
|
||||||
|
hops++;
|
||||||
|
}
|
||||||
|
if (bce->script->directlyInsideEval)
|
||||||
|
return false;
|
||||||
|
RootedObject outerScope(bce->sc->context, bce->script->enclosingStaticScope());
|
||||||
|
for (StaticScopeIter ssi(bce->sc->context, outerScope); !ssi.done(); ssi++) {
|
||||||
|
if (ssi.type() != StaticScopeIter::FUNCTION) {
|
||||||
|
if (ssi.hasDynamicScopeObject())
|
||||||
|
hops++;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
RootedScript script(bce->sc->context, ssi.funScript());
|
||||||
|
if (script->function()->atom() == pn->pn_atom)
|
||||||
|
return false;
|
||||||
|
if (ssi.hasDynamicScopeObject()) {
|
||||||
|
uint16_t slot;
|
||||||
|
if (LookupAliasedName(script, pn->pn_atom->asPropertyName(), &slot)) {
|
||||||
|
JSOp op;
|
||||||
|
switch (pn->getOp()) {
|
||||||
|
case JSOP_NAME: op = JSOP_GETALIASEDVAR; break;
|
||||||
|
case JSOP_SETNAME: op = JSOP_SETALIASEDVAR; break;
|
||||||
|
default: return false;
|
||||||
|
}
|
||||||
|
pn->setOp(op);
|
||||||
|
JS_ALWAYS_TRUE(pn->pn_cookie.set(bce->sc->context, hops, slot));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
hops++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (script->funHasExtensibleScope || script->directlyInsideEval)
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Try to convert free names in global scope to GNAME opcodes.
|
||||||
*
|
*
|
||||||
* Don't convert to *GNAME ops within strict-mode eval, since access
|
* This conversion is not made if we are in strict mode. In eval code nested
|
||||||
* to a "global" might merely be to a binding local to that eval:
|
* within (strict mode) eval code, access to an undeclared "global" might
|
||||||
|
* merely be to a binding local to that outer eval:
|
||||||
*
|
*
|
||||||
* "use strict";
|
* "use strict";
|
||||||
* var x = "global";
|
* var x = "global";
|
||||||
@ -1095,24 +1190,7 @@ EmitEnterBlock(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn, JSOp op)
|
|||||||
* undeclared = 17; // throws ReferenceError
|
* undeclared = 17; // throws ReferenceError
|
||||||
* }
|
* }
|
||||||
* foo();
|
* 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
|
|
||||||
TryConvertToGname(BytecodeEmitter *bce, ParseNode *pn, JSOp *op)
|
|
||||||
{
|
|
||||||
if (bce->selfHostingMode) {
|
|
||||||
switch (*op) {
|
|
||||||
case JSOP_NAME: *op = JSOP_GETINTRINSIC; break;
|
|
||||||
case JSOP_SETNAME: *op = JSOP_SETINTRINSIC; break;
|
|
||||||
/* Other *NAME ops aren't (yet) supported in self-hosted code. */
|
|
||||||
default: JS_NOT_REACHED("intrinsic");
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
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;
|
||||||
|
@ -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);
|
||||||
|
@ -272,10 +272,13 @@ 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 (pn->pn_body) {
|
||||||
if (!FoldConstants(cx, &pn->pn_body, parser, pn->pn_funbox->inGenexpLambda))
|
if (!FoldConstants(cx, &pn->pn_body, parser, pn->pn_funbox->inGenexpLambda))
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case PN_LIST:
|
case PN_LIST:
|
||||||
|
@ -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
|
||||||
|
@ -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)
|
||||||
|
@ -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)
|
||||||
{
|
{
|
||||||
|
@ -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,10 +1362,6 @@ 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) {
|
|
||||||
outer_dn = p.value().get<FullParseHandler>();
|
|
||||||
} else {
|
|
||||||
/*
|
/*
|
||||||
* Create a new placeholder for our outer lexdep. We could
|
* Create a new placeholder for our outer lexdep. We could
|
||||||
* simply re-use the inner placeholder, but that introduces
|
* simply re-use the inner placeholder, but that introduces
|
||||||
@ -1318,13 +1383,9 @@ Parser<FullParseHandler>::leaveFunction(ParseNode *fn, HandlePropertyName funNam
|
|||||||
* inherited lexdeps into uses of a new outer definition
|
* inherited lexdeps into uses of a new outer definition
|
||||||
* allows us to handle both these cases in a natural way.
|
* allows us to handle both these cases in a natural way.
|
||||||
*/
|
*/
|
||||||
outer_dn = handler.newPlaceholder(dn, outerpc);
|
outer_dn = getOrCreateLexicalDependency(outerpc, atom);
|
||||||
if (!outer_dn)
|
if (!outer_dn)
|
||||||
return false;
|
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,
|
||||||
|
HandlePropertyName funName,
|
||||||
size_t startOffset, FunctionType type,
|
size_t startOffset, FunctionType type,
|
||||||
FunctionSyntaxKind kind, bool strict, bool *becameStrict)
|
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;
|
||||||
@ -2585,9 +2900,6 @@ Parser<ParseHandler>::noteNameUse(HandlePropertyName name, Node pn)
|
|||||||
DefinitionNode dn;
|
DefinitionNode dn;
|
||||||
if (!defs.empty()) {
|
if (!defs.empty()) {
|
||||||
dn = defs.front<ParseHandler>();
|
dn = defs.front<ParseHandler>();
|
||||||
} else {
|
|
||||||
if (AtomDefnAddPtr p = pc->lexdeps->lookupForAdd(name)) {
|
|
||||||
dn = p.value().get<ParseHandler>();
|
|
||||||
} else {
|
} else {
|
||||||
/*
|
/*
|
||||||
* No definition before this use in any lexical scope.
|
* No definition before this use in any lexical scope.
|
||||||
@ -2597,13 +2909,9 @@ Parser<ParseHandler>::noteNameUse(HandlePropertyName name, Node pn)
|
|||||||
* - Be left as a free variable definition if we never
|
* - Be left as a free variable definition if we never
|
||||||
* see the real definition.
|
* see the real definition.
|
||||||
*/
|
*/
|
||||||
dn = handler.newPlaceholder(pn, pc);
|
dn = getOrCreateLexicalDependency(pc, name);
|
||||||
if (!dn)
|
if (!dn)
|
||||||
return false;
|
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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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:
|
||||||
@ -369,9 +379,7 @@ struct Parser : private AutoGCRooter, public StrictModeGetter
|
|||||||
*/
|
*/
|
||||||
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,
|
||||||
|
@ -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 *
|
||||||
|
@ -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
|
||||||
|
@ -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)
|
||||||
{
|
{
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
|
@ -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,
|
||||||
|
@ -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;
|
||||||
|
@ -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
|
||||||
|
@ -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);
|
||||||
|
@ -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");
|
||||||
|
@ -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);
|
||||||
|
|
||||||
|
@ -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();
|
||||||
|
@ -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();
|
||||||
|
@ -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) &&
|
||||||
|
@ -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; }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -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)
|
||||||
|
@ -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());
|
||||||
}
|
}
|
||||||
|
@ -68,8 +68,11 @@ class JSFunction : public JSObject
|
|||||||
use the accessor! */
|
use the accessor! */
|
||||||
} n;
|
} n;
|
||||||
struct Scripted {
|
struct Scripted {
|
||||||
|
union {
|
||||||
JSScript *script_; /* interpreted bytecode descriptor or null;
|
JSScript *script_; /* interpreted bytecode descriptor or null;
|
||||||
use the accessor! */
|
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);
|
||||||
}
|
}
|
||||||
|
@ -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,17 +203,13 @@ 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) {
|
|
||||||
script->hasBeenCloned = true;
|
|
||||||
Rooted<JSObject*> obj(cx, SkipScopeParent(parent));
|
|
||||||
if (!JSObject::setParent(cx, fun, obj))
|
if (!JSObject::setParent(cx, fun, obj))
|
||||||
return NULL;
|
return NULL;
|
||||||
fun->setEnvironment(parent);
|
fun->setEnvironment(parent);
|
||||||
return fun;
|
return fun;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// These intermediate variables are needed to avoid link errors on some
|
// These intermediate variables are needed to avoid link errors on some
|
||||||
// platforms. Sigh.
|
// platforms. Sigh.
|
||||||
@ -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
|
||||||
{
|
{
|
||||||
|
@ -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
|
||||||
|
@ -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 */
|
||||||
|
@ -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)
|
||||||
{
|
{
|
||||||
|
@ -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);
|
||||||
|
@ -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())
|
||||||
|
@ -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;
|
||||||
|
@ -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)
|
||||||
{
|
{
|
||||||
|
@ -83,6 +83,7 @@ class RegExpShared;
|
|||||||
class RegExpStatics;
|
class RegExpStatics;
|
||||||
class MatchPairs;
|
class MatchPairs;
|
||||||
class PropertyName;
|
class PropertyName;
|
||||||
|
class LazyScript;
|
||||||
|
|
||||||
enum RegExpFlag
|
enum RegExpFlag
|
||||||
{
|
{
|
||||||
|
@ -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,
|
||||||
|
@ -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;
|
||||||
|
|
||||||
|
@ -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()
|
||||||
{
|
{
|
||||||
|
@ -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
|
||||||
|
@ -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()
|
||||||
{
|
{
|
||||||
|
@ -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);
|
||||||
|
@ -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)
|
||||||
|
@ -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;
|
||||||
|
@ -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);
|
||||||
|
|
||||||
|
@ -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 "
|
||||||
|
@ -722,6 +722,7 @@ DescribeGCThing(bool isMarked, void *p, JSGCTraceKind traceKind,
|
|||||||
"Object",
|
"Object",
|
||||||
"String",
|
"String",
|
||||||
"Script",
|
"Script",
|
||||||
|
"LazyScript",
|
||||||
"IonCode",
|
"IonCode",
|
||||||
"Shape",
|
"Shape",
|
||||||
"BaseShape",
|
"BaseShape",
|
||||||
|
52
mfbt/Util.h
52
mfbt/Util.h
@ -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();
|
||||||
|
Loading…
Reference in New Issue
Block a user