diff --git a/js/src/frontend/Parser.cpp b/js/src/frontend/Parser.cpp index 8fc7b4552ab1..da6991091047 100644 --- a/js/src/frontend/Parser.cpp +++ b/js/src/frontend/Parser.cpp @@ -2069,9 +2069,9 @@ Parser::finishFunctionDefinition(Node pn, FunctionBox *funbo size_t numInnerFunctions = pc->innerFunctions.length(); RootedFunction fun(context, funbox->function()); - LazyScript *lazy = LazyScript::Create(context, fun, numFreeVariables, numInnerFunctions, versionNumber(), - funbox->bufStart, funbox->bufEnd, - funbox->startLine, funbox->startColumn); + LazyScript *lazy = LazyScript::CreateRaw(context, fun, numFreeVariables, numInnerFunctions, + versionNumber(), funbox->bufStart, funbox->bufEnd, + funbox->startLine, funbox->startColumn); if (!lazy) return false; diff --git a/js/src/jsfun.cpp b/js/src/jsfun.cpp index e6fffff38e90..984db5e5bd75 100644 --- a/js/src/jsfun.cpp +++ b/js/src/jsfun.cpp @@ -357,8 +357,9 @@ js::XDRInterpretedFunction(XDRState *xdr, HandleObject enclosingScope, Han MutableHandleObject objp) { enum FirstWordFlag { - HasAtom = 0x1, - IsStarGenerator = 0x2 + HasAtom = 0x1, + IsStarGenerator = 0x2, + IsLazy = 0x4 }; /* NB: Keep this in sync with CloneFunctionAndScript. */ @@ -369,6 +370,8 @@ js::XDRInterpretedFunction(XDRState *xdr, HandleObject enclosingScope, Han JSContext *cx = xdr->cx(); RootedFunction fun(cx); RootedScript script(cx); + Rooted lazy(cx); + if (mode == XDR_ENCODE) { fun = &objp->as(); if (!fun->isInterpreted()) { @@ -379,13 +382,27 @@ js::XDRInterpretedFunction(XDRState *xdr, HandleObject enclosingScope, Han } return false; } + if (fun->atom() || fun->hasGuessedAtom()) firstword |= HasAtom; + if (fun->isStarGenerator()) firstword |= IsStarGenerator; - script = fun->getOrCreateScript(cx); - if (!script) - return false; + + if (fun->isInterpretedLazy()) { + // This can only happen for re-lazified cloned functions, so this + // does not apply to any JSFunction produced by the parser, only to + // JSFunction created by the runtime. + JS_ASSERT(!fun->lazyScript()->maybeScript()); + + // Encode a lazy script. + firstword |= IsLazy; + lazy = fun->lazyScript(); + } else { + // Encode the script. + script = fun->nonLazyScript(); + } + atom = fun->displayAtom(); flagsword = (fun->nargs() << 16) | fun->flags(); } @@ -414,18 +431,29 @@ js::XDRInterpretedFunction(XDRState *xdr, HandleObject enclosingScope, Han if (!xdr->codeUint32(&flagsword)) return false; - if (!XDRScript(xdr, enclosingScope, enclosingScript, fun, &script)) - return false; + if (firstword & IsLazy) { + if (!XDRLazyScript(xdr, enclosingScope, enclosingScript, fun, &lazy)) + return false; + } else { + if (!XDRScript(xdr, enclosingScope, enclosingScript, fun, &script)) + return false; + } if (mode == XDR_DECODE) { fun->setArgCount(flagsword >> 16); fun->setFlags(uint16_t(flagsword)); fun->initAtom(atom); - fun->initScript(script); - script->setFunction(fun); + if (firstword & IsLazy) { + fun->initLazyScript(lazy); + } else { + fun->initScript(script); + script->setFunction(fun); + JS_ASSERT(fun->nargs() == script->bindings.numArgs()); + } + + if (!JSFunction::setTypeForScriptedFunction(cx, fun)) return false; - JS_ASSERT(fun->nargs() == fun->nonLazyScript()->bindings.numArgs()); objp.set(fun); } diff --git a/js/src/jsscript.cpp b/js/src/jsscript.cpp index 309804f46618..a9e26ec6c071 100644 --- a/js/src/jsscript.cpp +++ b/js/src/jsscript.cpp @@ -414,6 +414,75 @@ js::XDRScriptConst(XDRState *, MutableHandleValue); template bool js::XDRScriptConst(XDRState *, MutableHandleValue); +// Code LazyScript's free variables. +template +static bool +XDRLazyFreeVariables(XDRState *xdr, MutableHandle lazy) +{ + JSContext *cx = xdr->cx(); + RootedAtom atom(cx); + HeapPtrAtom *freeVariables = lazy->freeVariables(); + size_t numFreeVariables = lazy->numFreeVariables(); + for (size_t i = 0; i < numFreeVariables; i++) { + if (mode == XDR_ENCODE) + atom = freeVariables[i]; + + if (!XDRAtom(xdr, &atom)) + return false; + + if (mode == XDR_DECODE) + freeVariables[i] = atom; + } + + return true; +} + +// Code the missing part needed to re-create a LazyScript from a JSScript. +template +static bool +XDRRelazificationInfo(XDRState *xdr, HandleFunction fun, HandleScript script, + MutableHandle lazy) +{ + MOZ_ASSERT_IF(mode == XDR_ENCODE, script->isRelazifiable() && script->maybeLazyScript()); + MOZ_ASSERT_IF(mode == XDR_ENCODE, !lazy->numInnerFunctions()); + + JSContext *cx = xdr->cx(); + + uint64_t packedFields; + { + uint32_t begin = script->sourceStart(); + uint32_t end = script->sourceEnd(); + uint32_t lineno = script->lineno(); + uint32_t column = script->column(); + + if (mode == XDR_ENCODE) { + packedFields = lazy->packedFields(); + MOZ_ASSERT(begin == lazy->begin()); + MOZ_ASSERT(end == lazy->end()); + MOZ_ASSERT(lineno == lazy->lineno()); + MOZ_ASSERT(column == lazy->column()); + } + + if (!xdr->codeUint64(&packedFields)) + return false; + + if (mode == XDR_DECODE) { + lazy.set(LazyScript::Create(cx, fun, packedFields, begin, end, lineno, column)); + + // As opposed to XDRLazyScript, we need to restore the runtime bits + // of the script, as we are trying to match the fact this function + // has already been parsed and that it would need to be re-lazified. + lazy->initRuntimeFields(packedFields); + } + } + + // Code free variables. + if (!XDRLazyFreeVariables(xdr, lazy)) + return false; + + return true; +} + static inline uint32_t FindScopeObjectIndex(JSScript *script, NestedScopeObject &scope) { @@ -463,7 +532,8 @@ js::XDRScript(XDRState *xdr, HandleObject enclosingScope, HandleScript enc SelfHosted, IsCompileAndGo, HasSingleton, - TreatAsRunOnce + TreatAsRunOnce, + HasLazyScript }; uint32_t length, lineno, column, nslots; @@ -561,6 +631,8 @@ js::XDRScript(XDRState *xdr, HandleObject enclosingScope, HandleScript enc scriptBits |= (1 << HasSingleton); if (script->treatAsRunOnce()) scriptBits |= (1 << TreatAsRunOnce); + if (script->isRelazifiable()) + scriptBits |= (1 << HasLazyScript); } if (!xdr->codeUint32(&prologLength)) @@ -821,12 +893,16 @@ js::XDRScript(XDRState *xdr, HandleObject enclosingScope, HandleScript enc case CK_JSFunction: { /* Code the nested function's enclosing scope. */ uint32_t funEnclosingScopeIndex = 0; + RootedObject funEnclosingScope(cx); if (mode == XDR_ENCODE) { - JSScript *innerScript = (*objp)->as().getOrCreateScript(cx); - if (!innerScript) - return false; - RootedObject staticScope(cx, innerScript->enclosingStaticScope()); - StaticScopeIter ssi(staticScope); + RootedFunction function(cx, &(*objp)->as()); + + if (function->isInterpretedLazy()) + funEnclosingScope = function->lazyScript()->enclosingScope(); + else + funEnclosingScope = function->nonLazyScript()->enclosingStaticScope(); + + StaticScopeIter ssi(funEnclosingScope); if (ssi.done() || ssi.type() == StaticScopeIter::FUNCTION) { JS_ASSERT(ssi.done() == !fun); funEnclosingScopeIndex = UINT32_MAX; @@ -835,9 +911,10 @@ js::XDRScript(XDRState *xdr, HandleObject enclosingScope, HandleScript enc JS_ASSERT(funEnclosingScopeIndex < i); } } + if (!xdr->codeUint32(&funEnclosingScopeIndex)) return false; - Rooted funEnclosingScope(cx); + if (mode == XDR_DECODE) { if (funEnclosingScopeIndex == UINT32_MAX) { funEnclosingScope = fun; @@ -847,6 +924,7 @@ js::XDRScript(XDRState *xdr, HandleObject enclosingScope, HandleScript enc } } + // Code nested function and script. RootedObject tmp(cx, *objp); if (!XDRInterpretedFunction(xdr, funEnclosingScope, script, &tmp)) return false; @@ -901,6 +979,18 @@ js::XDRScript(XDRState *xdr, HandleObject enclosingScope, HandleScript enc } } + if (scriptBits & (1 << HasLazyScript)) { + Rooted lazy(cx); + if (mode == XDR_ENCODE) + lazy = script->maybeLazyScript(); + + if (!XDRRelazificationInfo(xdr, fun, script, &lazy)) + return false; + + if (mode == XDR_DECODE) + script->setLazyScript(lazy); + } + if (mode == XDR_DECODE) { scriptp.set(script); @@ -923,6 +1013,83 @@ template bool js::XDRScript(XDRState *, HandleObject, HandleScript, HandleFunction, MutableHandleScript); +template +bool +js::XDRLazyScript(XDRState *xdr, HandleObject enclosingScope, HandleScript enclosingScript, + HandleFunction fun, MutableHandle lazy) +{ + JSContext *cx = xdr->cx(); + + { + uint32_t begin; + uint32_t end; + uint32_t lineno; + uint32_t column; + uint64_t packedFields; + + if (mode == XDR_ENCODE) { + MOZ_ASSERT(!lazy->maybeScript()); + MOZ_ASSERT(fun == lazy->functionNonDelazifying()); + + begin = lazy->begin(); + end = lazy->end(); + lineno = lazy->lineno(); + column = lazy->column(); + packedFields = lazy->packedFields(); + } + + if (!xdr->codeUint32(&begin) || !xdr->codeUint32(&end) || + !xdr->codeUint32(&lineno) || !xdr->codeUint32(&column) || + !xdr->codeUint64(&packedFields)) + { + return false; + } + + if (mode == XDR_DECODE) + lazy.set(LazyScript::Create(cx, fun, packedFields, begin, end, lineno, column)); + } + + // Code free variables. + if (!XDRLazyFreeVariables(xdr, lazy)) + return false; + + // Code inner functions. + { + RootedObject func(cx); + HeapPtrFunction *innerFunctions = lazy->innerFunctions(); + size_t numInnerFunctions = lazy->numInnerFunctions(); + for (size_t i = 0; i < numInnerFunctions; i++) { + if (mode == XDR_ENCODE) + func = innerFunctions[i]; + + if (!XDRInterpretedFunction(xdr, fun, enclosingScript, &func)) + return false; + + if (mode == XDR_DECODE) + innerFunctions[i] = &func->as(); + } + } + + if (mode == XDR_DECODE) { + JS_ASSERT(!lazy->sourceObject()); + ScriptSourceObject *sourceObject = &enclosingScript->scriptSourceUnwrap(); + + // Set the enclosing scope of the lazy function, this would later be + // used to define the environment when the function would be used. + lazy->setParent(enclosingScope, sourceObject); + } + + return true; +} + +template bool +js::XDRLazyScript(XDRState *, HandleObject, HandleScript, + HandleFunction, MutableHandle); + +template bool +js::XDRLazyScript(XDRState *, HandleObject, HandleScript, + HandleFunction, MutableHandle); + void JSScript::setSourceObject(JSObject *object) { @@ -930,9 +1097,14 @@ JSScript::setSourceObject(JSObject *object) sourceObject_ = object; } +js::ScriptSourceObject & +JSScript::scriptSourceUnwrap() const { + return UncheckedUnwrap(sourceObject())->as(); +} + js::ScriptSource * JSScript::scriptSource() const { - return UncheckedUnwrap(sourceObject())->as().source(); + return scriptSourceUnwrap().source(); } bool @@ -3242,30 +3414,18 @@ JSScript::formalLivesInArgumentsObject(unsigned argSlot) return argsObjAliasesFormals() && !formalIsAliased(argSlot); } -LazyScript::LazyScript(JSFunction *fun, void *table, uint32_t numFreeVariables, uint32_t numInnerFunctions, - JSVersion version, uint32_t begin, uint32_t end, uint32_t lineno, uint32_t column) +LazyScript::LazyScript(JSFunction *fun, void *table, uint64_t packedFields, uint32_t begin, uint32_t end, uint32_t lineno, uint32_t column) : script_(nullptr), function_(fun), enclosingScope_(nullptr), sourceObject_(nullptr), table_(table), - version_(version), - numFreeVariables_(numFreeVariables), - numInnerFunctions_(numInnerFunctions), - generatorKindBits_(GeneratorKindAsBits(NotGenerator)), - strict_(false), - bindingsAccessedDynamically_(false), - hasDebuggerStatement_(false), - directlyInsideEval_(false), - usesArgumentsAndApply_(false), - hasBeenCloned_(false), - treatAsRunOnce_(false), + packedFields_(packedFields), begin_(begin), end_(end), lineno_(lineno), column_(column) { - JS_ASSERT(this->version() == version); JS_ASSERT(begin <= end); } @@ -3301,14 +3461,23 @@ LazyScript::sourceObject() const } /* static */ LazyScript * -LazyScript::Create(ExclusiveContext *cx, HandleFunction fun, - uint32_t numFreeVariables, uint32_t numInnerFunctions, JSVersion version, - uint32_t begin, uint32_t end, uint32_t lineno, uint32_t column) +LazyScript::CreateRaw(ExclusiveContext *cx, HandleFunction fun, + uint64_t packedFields, uint32_t begin, uint32_t end, + uint32_t lineno, uint32_t column) { - JS_ASSERT(begin <= end); + union { + PackedView p; + uint64_t packed; + }; - size_t bytes = (numFreeVariables * sizeof(HeapPtrAtom)) - + (numInnerFunctions * sizeof(HeapPtrFunction)); + packed = packedFields; + + // Reset runtime flags to obtain a fresh LazyScript. + p.hasBeenCloned = false; + p.treatAsRunOnce = false; + + size_t bytes = (p.numFreeVariables * sizeof(HeapPtrAtom)) + + (p.numInnerFunctions * sizeof(HeapPtrFunction)); void *table = nullptr; if (bytes) { @@ -3323,8 +3492,75 @@ LazyScript::Create(ExclusiveContext *cx, HandleFunction fun, cx->compartment()->scheduleDelazificationForDebugMode(); - return new (res) LazyScript(fun, table, numFreeVariables, numInnerFunctions, version, - begin, end, lineno, column); + return new (res) LazyScript(fun, table, packed, begin, end, lineno, column); +} + +/* static */ LazyScript * +LazyScript::CreateRaw(ExclusiveContext *cx, HandleFunction fun, + uint32_t numFreeVariables, uint32_t numInnerFunctions, JSVersion version, + uint32_t begin, uint32_t end, uint32_t lineno, uint32_t column) +{ + union { + PackedView p; + uint64_t packedFields; + }; + + p.version = version; + p.numFreeVariables = numFreeVariables; + p.numInnerFunctions = numInnerFunctions; + p.generatorKindBits = GeneratorKindAsBits(NotGenerator); + p.strict = false; + p.bindingsAccessedDynamically = false; + p.hasDebuggerStatement = false; + p.directlyInsideEval = false; + p.usesArgumentsAndApply = false; + + LazyScript *res = LazyScript::CreateRaw(cx, fun, packedFields, begin, end, lineno, column); + JS_ASSERT(res->version() == version); + return res; +} + +/* static */ LazyScript * +LazyScript::Create(ExclusiveContext *cx, HandleFunction fun, + uint64_t packedFields, uint32_t begin, uint32_t end, + uint32_t lineno, uint32_t column) +{ + // Dummy atom which is not a valid property name. + RootedAtom dummyAtom(cx, cx->names().comma); + + // Dummy function which is not a valid function as this is the one which is + // holding this lazy script. + HandleFunction dummyFun = fun; + + LazyScript *res = LazyScript::CreateRaw(cx, fun, packedFields, begin, end, lineno, column); + if (!res) + return nullptr; + + // Fill with dummies, to be GC-safe after the initialization of the free + // variables and inner functions. + size_t i, num; + HeapPtrAtom *variables = res->freeVariables(); + for (i = 0, num = res->numFreeVariables(); i < num; i++) + variables[i].init(dummyAtom); + + HeapPtrFunction *functions = res->innerFunctions(); + for (i = 0, num = res->numInnerFunctions(); i < num; i++) + functions[i].init(dummyFun); + + return res; +} + +void +LazyScript::initRuntimeFields(uint64_t packedFields) +{ + union { + PackedView p; + uint64_t packed; + }; + + packed = packedFields; + p_.hasBeenCloned = p.hasBeenCloned; + p_.treatAsRunOnce = p.treatAsRunOnce; } uint32_t diff --git a/js/src/jsscript.h b/js/src/jsscript.h index d7954220ae0a..5eb375266d6c 100644 --- a/js/src/jsscript.h +++ b/js/src/jsscript.h @@ -595,6 +595,11 @@ JSScript * CloneScript(JSContext *cx, HandleObject enclosingScope, HandleFunction fun, HandleScript script, NewObjectKind newKind = GenericObject); +template +bool +XDRLazyScript(XDRState *xdr, HandleObject enclosingScope, HandleScript enclosingScript, + HandleFunction fun, MutableHandle lazy); + /* * Code any constant value. */ @@ -1240,6 +1245,7 @@ class JSScript : public js::gc::BarrieredCell JSObject *sourceObject() const { return sourceObject_; } + js::ScriptSourceObject &scriptSourceUnwrap() const; js::ScriptSource *scriptSource() const; JSPrincipals *originPrincipals() const { return scriptSource()->originPrincipals(); } const char *filename() const { return scriptSource()->filename(); } @@ -1607,22 +1613,29 @@ class LazyScript : public gc::BarrieredCell uint32_t padding; #endif - // Assorted bits that should really be in ScriptSourceObject. - uint32_t version_ : 8; + struct PackedView { + // Assorted bits that should really be in ScriptSourceObject. + uint32_t version : 8; - uint32_t numFreeVariables_ : 24; - uint32_t numInnerFunctions_ : 23; + uint32_t numFreeVariables : 24; + uint32_t numInnerFunctions : 23; - uint32_t generatorKindBits_:2; + uint32_t generatorKindBits : 2; - // N.B. These are booleans but need to be uint32_t to pack correctly on MSVC. - uint32_t strict_ : 1; - uint32_t bindingsAccessedDynamically_ : 1; - uint32_t hasDebuggerStatement_ : 1; - uint32_t directlyInsideEval_:1; - uint32_t usesArgumentsAndApply_:1; - uint32_t hasBeenCloned_:1; - uint32_t treatAsRunOnce_:1; + // N.B. These are booleans but need to be uint32_t to pack correctly on MSVC. + uint32_t strict : 1; + uint32_t bindingsAccessedDynamically : 1; + uint32_t hasDebuggerStatement : 1; + uint32_t directlyInsideEval : 1; + uint32_t usesArgumentsAndApply : 1; + uint32_t hasBeenCloned : 1; + uint32_t treatAsRunOnce : 1; + }; + + union { + PackedView p_; + uint64_t packedFields_; + }; // Source location for the script. uint32_t begin_; @@ -1630,16 +1643,34 @@ class LazyScript : public gc::BarrieredCell uint32_t lineno_; uint32_t column_; - LazyScript(JSFunction *fun, void *table, - uint32_t numFreeVariables, uint32_t numInnerFunctions, JSVersion version, + LazyScript(JSFunction *fun, void *table, uint64_t packedFields, uint32_t begin, uint32_t end, uint32_t lineno, uint32_t column); + // Create a LazyScript without initializing the freeVariables and the + // innerFunctions. To be GC-safe, the caller must initialize both vectors + // with valid atoms and functions. + static LazyScript *CreateRaw(ExclusiveContext *cx, HandleFunction fun, + uint64_t packedData, uint32_t begin, uint32_t end, + uint32_t lineno, uint32_t column); + public: + // Create a LazyScript without initializing the freeVariables and the + // innerFunctions. To be GC-safe, the caller must initialize both vectors + // with valid atoms and functions. + static LazyScript *CreateRaw(ExclusiveContext *cx, HandleFunction fun, + uint32_t numFreeVariables, uint32_t numInnerFunctions, + JSVersion version, uint32_t begin, uint32_t end, + uint32_t lineno, uint32_t column); + + // Create a LazyScript and initialize the freeVariables and the + // innerFunctions with dummy values to be replaced in a later initialization + // phase. static LazyScript *Create(ExclusiveContext *cx, HandleFunction fun, - uint32_t numFreeVariables, uint32_t numInnerFunctions, - JSVersion version, uint32_t begin, uint32_t end, + uint64_t packedData, uint32_t begin, uint32_t end, uint32_t lineno, uint32_t column); + void initRuntimeFields(uint64_t packedFields); + inline JSFunction *functionDelazifying(JSContext *cx) const; JSFunction *functionNonDelazifying() const { return function_; @@ -1663,26 +1694,26 @@ class LazyScript : public gc::BarrieredCell } JSVersion version() const { JS_STATIC_ASSERT(JSVERSION_UNKNOWN == -1); - return (version_ == JS_BIT(8) - 1) ? JSVERSION_UNKNOWN : JSVersion(version_); + return (p_.version == JS_BIT(8) - 1) ? JSVERSION_UNKNOWN : JSVersion(p_.version); } void setParent(JSObject *enclosingScope, ScriptSourceObject *sourceObject); uint32_t numFreeVariables() const { - return numFreeVariables_; + return p_.numFreeVariables; } HeapPtrAtom *freeVariables() { return (HeapPtrAtom *)table_; } uint32_t numInnerFunctions() const { - return numInnerFunctions_; + return p_.numInnerFunctions; } HeapPtrFunction *innerFunctions() { return (HeapPtrFunction *)&freeVariables()[numFreeVariables()]; } - GeneratorKind generatorKind() const { return GeneratorKindFromBits(generatorKindBits_); } + GeneratorKind generatorKind() const { return GeneratorKindFromBits(p_.generatorKindBits); } bool isGenerator() const { return generatorKind() != NotGenerator; } @@ -1696,56 +1727,56 @@ class LazyScript : public gc::BarrieredCell JS_ASSERT(!isGenerator()); // Legacy generators cannot currently be lazy. JS_ASSERT(kind != LegacyGenerator); - generatorKindBits_ = GeneratorKindAsBits(kind); + p_.generatorKindBits = GeneratorKindAsBits(kind); } bool strict() const { - return strict_; + return p_.strict; } void setStrict() { - strict_ = true; + p_.strict = true; } bool bindingsAccessedDynamically() const { - return bindingsAccessedDynamically_; + return p_.bindingsAccessedDynamically; } void setBindingsAccessedDynamically() { - bindingsAccessedDynamically_ = true; + p_.bindingsAccessedDynamically = true; } bool hasDebuggerStatement() const { - return hasDebuggerStatement_; + return p_.hasDebuggerStatement; } void setHasDebuggerStatement() { - hasDebuggerStatement_ = true; + p_.hasDebuggerStatement = true; } bool directlyInsideEval() const { - return directlyInsideEval_; + return p_.directlyInsideEval; } void setDirectlyInsideEval() { - directlyInsideEval_ = true; + p_.directlyInsideEval = true; } bool usesArgumentsAndApply() const { - return usesArgumentsAndApply_; + return p_.usesArgumentsAndApply; } void setUsesArgumentsAndApply() { - usesArgumentsAndApply_ = true; + p_.usesArgumentsAndApply = true; } bool hasBeenCloned() const { - return hasBeenCloned_; + return p_.hasBeenCloned; } void setHasBeenCloned() { - hasBeenCloned_ = true; + p_.hasBeenCloned = true; } bool treatAsRunOnce() const { - return treatAsRunOnce_; + return p_.treatAsRunOnce; } void setTreatAsRunOnce() { - treatAsRunOnce_ = true; + p_.treatAsRunOnce = true; } ScriptSource *source() const { @@ -1775,6 +1806,10 @@ class LazyScript : public gc::BarrieredCell { return mallocSizeOf(table_); } + + uint64_t packedFields() const { + return packedFields_; + } }; /* If this fails, add/remove padding within LazyScript. */