Bug 1165486 - Restructure function and script cloning in light of PollutingGlobal scope changes. (r=Waldo)

CloneFunctionObject is split into the following:

  - CloneFunctionAndScript, which deep clones the function and its
    script, giving the cloned script a new static scope chain. This is
    used for cloning singleton lambdas and JSAPI cloning. For singleton
    lambdas, the original and the clone script have the same static
    scope chain. For JSAPI cloning, a new static scope is provided
    (either null, for a clean global, or StaticPollutingGlobalObject,
    for a polluted global).

  - CloneFunctionReuseScript, which clones the function but reuses the
    script, and thus keeps the same static scope chain.

CloneScript is split into the following:

  - CloneGlobalScript, which clones a script with and gives it a new
    static scope.

  - CloneScriptIntoFunction, which clones a script into a JSFunction and
    gives it a new static scope. Cloning a script into a new function
    container requires slightly different logic to hook up the static
    scope chain before cloning inner scripts.
This commit is contained in:
Shu-yu Guo 2015-06-21 11:49:57 -07:00
parent 1aa1600b4e
commit eefda67985
8 changed files with 384 additions and 258 deletions

View File

@ -3325,11 +3325,23 @@ IsFunctionCloneable(HandleFunction fun, HandleObject dynamicScope)
// If a function was compiled to be lexically nested inside some other
// script, we cannot clone it without breaking the compiler's assumptions.
JSObject* scope = fun->nonLazyScript()->enclosingStaticScope();
if (scope && (!scope->is<StaticEvalObject>() ||
scope->as<StaticEvalObject>().isDirect() ||
scope->as<StaticEvalObject>().isStrict()))
{
if (JSObject* scope = fun->nonLazyScript()->enclosingStaticScope()) {
// If the script already deals with a non-syntactic scope, we can clone
// it.
if (scope->is<StaticNonSyntacticScopeObjects>())
return true;
// If the script is an indirect eval that is immediately scoped under
// the global, we can clone it.
if (scope->is<StaticEvalObject>() &&
!scope->as<StaticEvalObject>().isDirect() &&
!scope->as<StaticEvalObject>().isStrict())
{
return true;
}
// Any other enclosing static scope (e.g., function, block) cannot be
// cloned.
return false;
}
@ -3337,7 +3349,8 @@ IsFunctionCloneable(HandleFunction fun, HandleObject dynamicScope)
}
static JSObject*
CloneFunctionObject(JSContext* cx, HandleObject funobj, HandleObject dynamicScope)
CloneFunctionObject(JSContext* cx, HandleObject funobj, HandleObject dynamicScope,
Handle<ScopeObject*> staticScope)
{
AssertHeapIsIdle(cx);
CHECK_REQUEST(cx);
@ -3374,7 +3387,21 @@ CloneFunctionObject(JSContext* cx, HandleObject funobj, HandleObject dynamicScop
return nullptr;
}
return CloneFunctionObject(cx, fun, dynamicScope, fun->getAllocKind());
if (CanReuseScriptForClone(cx->compartment(), fun, dynamicScope)) {
// If the script is to be reused, either the script can already handle
// non-syntactic scopes, or there is no new static scope.
#ifdef DEBUG
// Fail here if we OOM during debug asserting.
// CloneFunctionReuseScript will delazify the script anyways, so we
// are not creating an extra failure condition for DEBUG builds.
if (!fun->getOrCreateScript(cx))
return nullptr;
MOZ_ASSERT(!staticScope || fun->nonLazyScript()->hasNonSyntacticScope());
#endif
return CloneFunctionReuseScript(cx, fun, dynamicScope, fun->getAllocKind());
}
return CloneFunctionAndScript(cx, fun, dynamicScope, staticScope, fun->getAllocKind());
}
namespace JS {
@ -3382,18 +3409,18 @@ namespace JS {
JS_PUBLIC_API(JSObject*)
CloneFunctionObject(JSContext* cx, JS::Handle<JSObject*> funobj)
{
return CloneFunctionObject(cx, funobj, cx->global());
return CloneFunctionObject(cx, funobj, cx->global(), /* staticScope = */ nullptr);
}
extern JS_PUBLIC_API(JSObject*)
CloneFunctionObject(JSContext* cx, HandleObject funobj, AutoObjectVector& scopeChain)
{
RootedObject dynamicScope(cx);
RootedObject unusedStaticScope(cx);
if (!CreateScopeObjectsForScopeChain(cx, scopeChain, &dynamicScope, &unusedStaticScope))
Rooted<ScopeObject*> staticScope(cx);
if (!CreateNonSyntacticScopeChain(cx, scopeChain, &dynamicScope, &staticScope))
return nullptr;
return CloneFunctionObject(cx, funobj, dynamicScope);
return CloneFunctionObject(cx, funobj, dynamicScope, staticScope);
}
} // namespace JS
@ -4238,7 +4265,7 @@ ExecuteScript(JSContext* cx, AutoObjectVector& scopeChain, HandleScript scriptAr
return false;
RootedScript script(cx, scriptArg);
if (!script->hasNonSyntacticScope()) {
if (!script->hasNonSyntacticScope() && !dynamicScope->is<GlobalObject>()) {
script = CloneGlobalScript(cx, staticScope, script);
if (!script)
return false;
@ -4279,7 +4306,7 @@ JS::CloneAndExecuteScript(JSContext* cx, HandleScript scriptArg)
CHECK_REQUEST(cx);
RootedScript script(cx, scriptArg);
if (script->compartment() != cx->compartment()) {
script = CloneScript(cx, nullptr, nullptr, script);
script = CloneGlobalScript(cx, /* enclosingScope = */ nullptr, script);
if (!script)
return false;

View File

@ -35,6 +35,7 @@
#include "jit/JitFrameIterator.h"
#include "js/CallNonGenericMethod.h"
#include "js/Proxy.h"
#include "vm/Debugger.h"
#include "vm/GlobalObject.h"
#include "vm/Interpreter.h"
#include "vm/Shape.h"
@ -524,7 +525,7 @@ js::XDRInterpretedFunction(XDRState<mode>* xdr, HandleObject enclosingScope, Han
HasSingletonType = 0x8
};
/* NB: Keep this in sync with CloneFunctionAndScript. */
/* NB: Keep this in sync with CloneInnerInterpretedFunction. */
RootedAtom atom(xdr->cx());
uint32_t firstword = 0; /* bitmask of FirstWordFlag */
uint32_t flagsword = 0; /* word for argument count and fun->flags */
@ -615,10 +616,9 @@ js::XDRInterpretedFunction(XDRState<mode>* xdr, HandleObject enclosingScope, Han
fun->setFlags(uint16_t(flagsword));
fun->initAtom(atom);
if (firstword & IsLazy) {
fun->initLazyScript(lazy);
MOZ_ASSERT(fun->lazyScript() == lazy);
} else {
fun->initScript(script);
script->setFunction(fun);
MOZ_ASSERT(fun->nonLazyScript() == script);
MOZ_ASSERT(fun->nargs() == script->bindings.numArgs());
}
@ -637,44 +637,6 @@ js::XDRInterpretedFunction(XDRState<XDR_ENCODE>*, HandleObject, HandleScript, Mu
template bool
js::XDRInterpretedFunction(XDRState<XDR_DECODE>*, HandleObject, HandleScript, MutableHandleFunction);
JSObject*
js::CloneFunctionAndScript(JSContext* cx, HandleObject enclosingScope, HandleFunction srcFun,
PollutedGlobalScopeOption polluted)
{
/* NB: Keep this in sync with XDRInterpretedFunction. */
RootedObject cloneProto(cx);
if (srcFun->isStarGenerator()) {
cloneProto = GlobalObject::getOrCreateStarGeneratorFunctionPrototype(cx, cx->global());
if (!cloneProto)
return nullptr;
}
gc::AllocKind allocKind = srcFun->getAllocKind();
RootedFunction clone(cx, NewFunctionWithProto(cx, nullptr, 0,
JSFunction::INTERPRETED, nullptr, nullptr,
cloneProto, allocKind, TenuredObject));
if (!clone)
return nullptr;
JSScript::AutoDelazify srcScript(cx, srcFun);
if (!srcScript)
return nullptr;
RootedScript clonedScript(cx, CloneScript(cx, enclosingScope, clone, srcScript, polluted));
if (!clonedScript)
return nullptr;
clone->setArgCount(srcFun->nargs());
clone->setFlags(srcFun->flags());
clone->initAtom(srcFun->displayAtom());
clone->initScript(clonedScript);
clonedScript->setFunction(clone);
if (!JSFunction::setTypeForScriptedFunction(cx, clone))
return nullptr;
RootedScript cloneScript(cx, clone->nonLazyScript());
return clone;
}
/*
* [[HasInstance]] internal method for Function objects: fetch the .prototype
* property of its 'this' parameter, and walks the prototype chain of v (only
@ -828,7 +790,7 @@ CreateFunctionPrototype(JSContext* cx, JSProtoKey key)
/*
* The default 'new' group of Function.prototype is required by type
* inference to have unknown properties, to simplify handling of e.g.
* CloneFunctionObject.
* NewFunctionClone.
*/
if (!JSObject::setNewGroupUnknown(cx, &JSFunction::class_, functionProto))
return nullptr;
@ -1423,16 +1385,13 @@ JSFunction::createScriptForLazilyInterpretedFunction(JSContext* cx, HandleFuncti
if (script) {
RootedObject enclosingScope(cx, lazy->enclosingScope());
RootedScript clonedScript(cx, CloneScript(cx, enclosingScope, fun, script));
RootedScript clonedScript(cx, CloneScriptIntoFunction(cx, enclosingScope, fun, script));
if (!clonedScript)
return false;
clonedScript->setSourceObject(lazy->sourceObject());
fun->initAtom(script->functionNonDelazifying()->displayAtom());
clonedScript->setFunction(fun);
fun->setUnlazifiedScript(clonedScript);
if (!lazy->maybeScript())
lazy->initScript(clonedScript);
@ -2086,8 +2045,8 @@ js::NewFunctionWithProto(ExclusiveContext* cx, Native native,
}
bool
js::CloneFunctionObjectUseSameScript(JSCompartment* compartment, HandleFunction fun,
HandleObject newParent)
js::CanReuseScriptForClone(JSCompartment* compartment, HandleFunction fun,
HandleObject newParent)
{
if (compartment != fun->compartment() ||
fun->isSingleton() ||
@ -2108,75 +2067,52 @@ js::CloneFunctionObjectUseSameScript(JSCompartment* compartment, HandleFunction
return true;
// We need to clone the script if we're interpreted and not already marked
// as having a non-syntactic scope. If we're lazy, go ahead and clone the
// as having a non-syntactic scope. If we're lazy, go ahead and clone the
// script; see the big comment at the end of CopyScriptInternal for the
// explanation of what's going on there.
return !fun->isInterpreted() ||
(fun->hasScript() && fun->nonLazyScript()->hasNonSyntacticScope());
}
JSFunction*
js::CloneFunctionObject(JSContext* cx, HandleFunction fun, HandleObject parent,
gc::AllocKind allocKind,
NewObjectKind newKindArg /* = GenericObject */,
HandleObject proto)
static bool
FunctionCloneScopeIsWellFormed(JSContext* cx, HandleObject parent)
{
MOZ_ASSERT(parent);
MOZ_ASSERT(!fun->isBoundFunction());
bool useSameScript = CloneFunctionObjectUseSameScript(cx->compartment(), fun, parent);
NewObjectKind newKind = useSameScript ? newKindArg : SingletonObject;
RootedObject cloneProto(cx, proto);
if (!cloneProto && fun->isStarGenerator()) {
cloneProto = GlobalObject::getOrCreateStarGeneratorFunctionPrototype(cx, cx->global());
if (!cloneProto)
return nullptr;
}
#ifdef DEBUG
RootedObject realParent(cx, SkipScopeParent(parent));
// We'd like to assert that realParent is null-or-global, but
// js::ExecuteInGlobalAndReturnScope and debugger eval bits mess that up.
// Assert that it's one of those or a debug scope proxy or the unqualified
// var obj, since it should still be ok to parent to the global in that
// case.
MOZ_ASSERT(!realParent || realParent == cx->global() ||
realParent->is<DebugScopeObject>() ||
realParent->isUnqualifiedVarObj());
#endif
return !realParent || realParent == cx->global() ||
realParent->is<DebugScopeObject>() ||
realParent->isUnqualifiedVarObj();
}
static inline JSFunction*
NewFunctionClone(JSContext* cx, HandleFunction fun, NewObjectKind newKind,
gc::AllocKind allocKind, HandleObject proto)
{
RootedObject cloneProto(cx, proto);
if (!proto && fun->isStarGenerator()) {
cloneProto = GlobalObject::getOrCreateStarGeneratorFunctionPrototype(cx, cx->global());
if (!cloneProto)
return nullptr;
}
JSObject* cloneobj = NewObjectWithClassProto(cx, &JSFunction::class_, cloneProto,
allocKind, newKind);
if (!cloneobj)
return nullptr;
RootedFunction clone(cx, &cloneobj->as<JSFunction>());
JSScript::AutoDelazify funScript(cx);
if (!useSameScript && fun->isInterpretedLazy()) {
funScript = fun;
if (!funScript)
return nullptr;
}
MOZ_ASSERT(useSameScript || !fun->isInterpretedLazy());
uint16_t flags = fun->flags() & ~JSFunction::EXTENDED;
if (allocKind == AllocKind::FUNCTION_EXTENDED)
flags |= JSFunction::EXTENDED;
clone->setArgCount(fun->nargs());
clone->setFlags(flags);
if (fun->hasScript()) {
clone->initScript(fun->nonLazyScript());
clone->initEnvironment(parent);
} else if (fun->isInterpretedLazy()) {
MOZ_ASSERT(fun->compartment() == clone->compartment());
MOZ_ASSERT(useSameScript);
LazyScript* lazy = fun->lazyScriptOrNull();
clone->initLazyScript(lazy);
clone->initEnvironment(parent);
} else {
clone->initNative(fun->native(), fun->jitInfo());
}
clone->initAtom(fun->displayAtom());
if (allocKind == AllocKind::FUNCTION_EXTENDED) {
@ -2188,34 +2124,103 @@ js::CloneFunctionObject(JSContext* cx, HandleFunction fun, HandleObject parent,
}
}
if (useSameScript) {
/*
* Clone the function, reusing its script. We can use the same group as
* the original function provided that its prototype is correct.
*/
if (fun->getProto() == clone->getProto())
clone->setGroup(fun->group());
return clone;
}
return clone;
}
RootedFunction cloneRoot(cx, clone);
JSFunction*
js::CloneFunctionReuseScript(JSContext* cx, HandleFunction fun, HandleObject parent,
gc::AllocKind allocKind /* = FUNCTION */ ,
NewObjectKind newKind /* = GenericObject */,
HandleObject proto /* = nullptr */)
{
MOZ_ASSERT(FunctionCloneScopeIsWellFormed(cx, parent));
MOZ_ASSERT(!fun->isBoundFunction());
MOZ_ASSERT(CanReuseScriptForClone(cx->compartment(), fun, parent));
RootedFunction clone(cx, NewFunctionClone(cx, fun, newKind, allocKind, proto));
if (!clone)
return nullptr;
if (fun->hasScript()) {
clone->initScript(fun->nonLazyScript());
clone->initEnvironment(parent);
} else if (fun->isInterpretedLazy()) {
MOZ_ASSERT(fun->compartment() == clone->compartment());
LazyScript* lazy = fun->lazyScriptOrNull();
clone->initLazyScript(lazy);
clone->initEnvironment(parent);
} else {
clone->initNative(fun->native(), fun->jitInfo());
}
/*
* Across compartments or if we have to introduce a polluted scope we have
* to clone the script for interpreted functions. Cross-compartment cloning
* only happens via JSAPI (JS::CloneFunctionObject) which dynamically
* ensures that 'script' has no enclosing lexical scope (only the global
* scope or other non-lexical scope).
* Clone the function, reusing its script. We can use the same group as
* the original function provided that its prototype is correct.
*/
PollutedGlobalScopeOption globalScopeOption = parent->is<GlobalObject>() ?
HasCleanGlobalScope : HasPollutedGlobalScope;
if (cloneRoot->isInterpreted() &&
!CloneFunctionScript(cx, fun, cloneRoot, globalScopeOption, newKindArg))
{
if (fun->getProto() == clone->getProto())
clone->setGroup(fun->group());
return clone;
}
JSFunction*
js::CloneFunctionAndScript(JSContext* cx, HandleFunction fun, HandleObject parent,
HandleObject newStaticScope,
gc::AllocKind allocKind /* = FUNCTION */,
HandleObject proto /* = nullptr */)
{
MOZ_ASSERT(FunctionCloneScopeIsWellFormed(cx, parent));
MOZ_ASSERT(!fun->isBoundFunction());
RootedFunction clone(cx, NewFunctionClone(cx, fun, SingletonObject, allocKind, proto));
if (!clone)
return nullptr;
if (fun->hasScript()) {
clone->initScript(nullptr);
clone->initEnvironment(parent);
} else {
clone->initNative(fun->native(), fun->jitInfo());
}
return cloneRoot;
/*
* Across compartments or if we have to introduce a non-syntactic scope we
* have to clone the script for interpreted functions. Cross-compartment
* cloning only happens via JSAPI (JS::CloneFunctionObject) which
* dynamically ensures that 'script' has no enclosing lexical scope (only
* the global scope or other non-lexical scope).
*/
#ifdef DEBUG
RootedObject terminatingScope(cx, parent);
while (IsSyntacticScope(terminatingScope))
terminatingScope = terminatingScope->enclosingScope();
MOZ_ASSERT_IF(!terminatingScope->is<GlobalObject>(),
HasNonSyntacticStaticScopeChain(newStaticScope));
#endif
if (clone->isInterpreted()) {
// The self-hosting compartment is shared across processes, and
// AutoDelazify enters fun->compartment(). We would get races if the
// self-hosting compartment has lazy interpreted functions.
MOZ_ASSERT_IF(fun->compartment()->isSelfHosting, !fun->isInterpretedLazy());
JSScript::AutoDelazify funScript(cx);
if (fun->isInterpretedLazy()) {
funScript = fun;
if (!funScript)
return nullptr;
}
RootedScript script(cx, fun->nonLazyScript());
MOZ_ASSERT(script->compartment() == fun->compartment());
MOZ_ASSERT(cx->compartment() == clone->compartment(),
"Otherwise we could relazify clone below!");
RootedScript clonedScript(cx, CloneScriptIntoFunction(cx, newStaticScope, clone, script));
if (!clonedScript)
return nullptr;
Debugger::onNewScript(cx, clonedScript);
}
return clone;
}
/*

View File

@ -633,14 +633,20 @@ class FunctionExtended : public JSFunction
};
extern bool
CloneFunctionObjectUseSameScript(JSCompartment* compartment, HandleFunction fun,
HandleObject newParent);
CanReuseScriptForClone(JSCompartment* compartment, HandleFunction fun, HandleObject newParent);
extern JSFunction*
CloneFunctionObject(JSContext* cx, HandleFunction fun, HandleObject parent,
gc::AllocKind kind = gc::AllocKind::FUNCTION,
NewObjectKind newKindArg = GenericObject,
HandleObject proto = nullptr);
CloneFunctionReuseScript(JSContext* cx, HandleFunction fun, HandleObject parent,
gc::AllocKind kind = gc::AllocKind::FUNCTION,
NewObjectKind newKindArg = GenericObject,
HandleObject proto = nullptr);
// Functions whose scripts are cloned are always given singleton types.
extern JSFunction*
CloneFunctionAndScript(JSContext* cx, HandleFunction fun, HandleObject parent,
HandleObject newStaticScope,
gc::AllocKind kind = gc::AllocKind::FUNCTION,
HandleObject proto = nullptr);
extern bool
FindBody(JSContext* cx, HandleFunction fun, HandleLinearString src, size_t* bodyStart,
@ -702,10 +708,6 @@ bool
XDRInterpretedFunction(XDRState<mode>* xdr, HandleObject enclosingScope,
HandleScript enclosingScript, MutableHandleFunction objp);
extern JSObject*
CloneFunctionAndScript(JSContext* cx, HandleObject enclosingScope, HandleFunction fun,
PollutedGlobalScopeOption polluted);
/*
* Report an error that call.thisv is not compatible with the specified class,
* assuming that the method (clasp->name).prototype.<name of callee function>

View File

@ -85,7 +85,12 @@ CloneFunctionObjectIfNotSingleton(JSContext* cx, HandleFunction fun, HandleObjec
gc::AllocKind kind = fun->isExtended()
? extendedFinalizeKind
: finalizeKind;
return CloneFunctionObject(cx, fun, parent, kind, newKind, proto);
if (CanReuseScriptForClone(cx->compartment(), fun, parent))
return CloneFunctionReuseScript(cx, fun, parent, kind, newKind, proto);
RootedObject staticScope(cx, fun->getOrCreateScript(cx)->enclosingStaticScope());
return CloneFunctionAndScript(cx, fun, parent, staticScope, kind, proto);
}
} /* namespace js */

View File

@ -576,10 +576,10 @@ enum XDRClassKind {
template<XDRMode mode>
bool
js::XDRScript(XDRState<mode>* xdr, HandleObject enclosingScope, HandleScript enclosingScript,
js::XDRScript(XDRState<mode>* xdr, HandleObject enclosingScopeArg, HandleScript enclosingScript,
HandleFunction fun, MutableHandleScript scriptp)
{
/* NB: Keep this in sync with CloneScript. */
/* NB: Keep this in sync with CopyScript. */
enum ScriptBits {
NoScriptRval,
@ -613,6 +613,7 @@ js::XDRScript(XDRState<mode>* xdr, HandleObject enclosingScope, HandleScript enc
JSContext* cx = xdr->cx();
RootedScript script(cx);
RootedObject enclosingScope(cx, enclosingScopeArg);
natoms = nsrcnotes = 0;
nconsts = nobjects = nregexps = ntrynotes = nblockscopes = nyieldoffsets = 0;
@ -801,10 +802,26 @@ js::XDRScript(XDRState<mode>* xdr, HandleObject enclosingScope, HandleScript enc
MOZ_ASSERT(enclosingScript->sourceObject()->is<ScriptSourceObject>());
sourceObject = &enclosingScript->sourceObject()->as<ScriptSourceObject>();
}
// If the outermost script has a non-syntactic scope, reflect that on
// the static scope chain.
if (scriptBits & (1 << HasNonSyntacticScope) && !enclosingScope) {
enclosingScope = StaticNonSyntacticScopeObjects::create(cx, nullptr);
if (!enclosingScope)
return false;
}
script = JSScript::Create(cx, enclosingScope, !!(scriptBits & (1 << SavedCallerFun)),
options, /* staticLevel = */ 0, sourceObject, 0, 0);
if (!script)
return false;
// Set the script in its function now so that inner scripts to be
// decoded may iterate the static scope chain.
if (fun) {
fun->initScript(script);
script->setFunction(fun);
}
}
/* JSScript::partiallyInit assumes script->bindings is fully initialized. */
@ -982,7 +999,12 @@ js::XDRScript(XDRState<mode>* xdr, HandleObject enclosingScope, HandleScript enc
MOZ_ASSERT(enclosingStaticScopeIndex < i);
enclosingStaticScope = script->objects()->vector[enclosingStaticScopeIndex];
} else {
enclosingStaticScope = fun;
// This is not ternary because MSVC can't typecheck the
// ternary.
if (fun)
enclosingStaticScope = fun;
else
enclosingStaticScope = enclosingScope;
}
}
@ -1019,8 +1041,15 @@ js::XDRScript(XDRState<mode>* xdr, HandleObject enclosingScope, HandleScript enc
StaticScopeIter<NoGC> ssi(funEnclosingScope);
if (ssi.done() || ssi.type() == StaticScopeIter<NoGC>::Function) {
MOZ_ASSERT(ssi.done() == !fun);
// Starting from a nested function, hitting a non-syntactic
// scope on the static scope chain means that its enclosing
// function has a non-syntactic scope. Nested functions
// themselves never have non-syntactic scope chains.
if (ssi.done() ||
ssi.type() == StaticScopeIter<NoGC>::NonSyntactic ||
ssi.type() == StaticScopeIter<NoGC>::Function)
{
MOZ_ASSERT_IF(ssi.done() || ssi.type() != StaticScopeIter<NoGC>::Function, !fun);
funEnclosingScopeIndex = UINT32_MAX;
} else if (ssi.type() == StaticScopeIter<NoGC>::Block) {
funEnclosingScopeIndex = FindScopeObjectIndex(script, ssi.block());
@ -1036,7 +1065,12 @@ js::XDRScript(XDRState<mode>* xdr, HandleObject enclosingScope, HandleScript enc
if (mode == XDR_DECODE) {
if (funEnclosingScopeIndex == UINT32_MAX) {
funEnclosingScope = fun;
// This is not ternary because MSVC can't typecheck the
// ternary.
if (fun)
funEnclosingScope = fun;
else
funEnclosingScope = enclosingScope;
} else {
MOZ_ASSERT(funEnclosingScopeIndex < i);
funEnclosingScope = script->objects()->vector[funEnclosingScopeIndex];
@ -1177,9 +1211,13 @@ js::XDRLazyScript(XDRState<mode>* xdr, HandleObject enclosingScope, HandleScript
return false;
}
if (mode == XDR_DECODE)
if (mode == XDR_DECODE) {
lazy.set(LazyScript::Create(cx, fun, nullptr, enclosingScope, enclosingScript,
packedFields, begin, end, lineno, column));
if (!lazy)
return false;
fun->initLazyScript(lazy);
}
}
// Code free variables.
@ -3013,15 +3051,48 @@ Rebase(JSScript* dst, JSScript* src, T* srcp)
return reinterpret_cast<T*>(dst->data + off);
}
JSScript*
js::CloneScript(JSContext* cx, HandleObject enclosingScope, HandleFunction fun, HandleScript src,
PollutedGlobalScopeOption polluted /* = HasCleanGlobalScope */,
NewObjectKind newKind /* = GenericObject */)
static JSObject*
CloneInnerInterpretedFunction(JSContext* cx, HandleObject enclosingScope, HandleFunction srcFun)
{
/* NB: Keep this in sync with XDRInterpretedFunction. */
RootedObject cloneProto(cx);
if (srcFun->isStarGenerator()) {
cloneProto = GlobalObject::getOrCreateStarGeneratorFunctionPrototype(cx, cx->global());
if (!cloneProto)
return nullptr;
}
gc::AllocKind allocKind = srcFun->getAllocKind();
RootedFunction clone(cx, NewFunctionWithProto(cx, nullptr, 0,
JSFunction::INTERPRETED, nullptr, nullptr,
cloneProto, allocKind, TenuredObject));
if (!clone)
return nullptr;
JSScript::AutoDelazify srcScript(cx, srcFun);
if (!srcScript)
return nullptr;
JSScript* cloneScript = CloneScriptIntoFunction(cx, enclosingScope, clone, srcScript);
if (!cloneScript)
return nullptr;
clone->setArgCount(srcFun->nargs());
clone->setFlags(srcFun->flags());
clone->initAtom(srcFun->displayAtom());
if (!JSFunction::setTypeForScriptedFunction(cx, clone))
return nullptr;
return clone;
}
bool
js::detail::CopyScript(JSContext* cx, HandleObject scriptStaticScope, HandleScript src,
HandleScript dst)
{
if (src->treatAsRunOnce() && !src->functionNonDelazifying()) {
// Toplevel run-once scripts may not be cloned.
JS_ReportError(cx, "No cloning toplevel run-once scripts");
return nullptr;
return false;
}
/* NB: Keep this in sync with XDRScript. */
@ -3040,7 +3111,7 @@ js::CloneScript(JSContext* cx, HandleObject enclosingScope, HandleFunction fun,
size_t size = src->dataSize();
uint8_t* data = AllocScriptData(cx->zone(), size);
if (size && !data)
return nullptr;
return false;
/* Bindings */
@ -3048,7 +3119,7 @@ js::CloneScript(JSContext* cx, HandleObject enclosingScope, HandleFunction fun,
InternalHandle<Bindings*> bindingsHandle =
InternalHandle<Bindings*>::fromMarkedLocation(bindings.address());
if (!Bindings::clone(cx, bindingsHandle, data, src))
return nullptr;
return false;
/* Objects */
@ -3065,7 +3136,7 @@ js::CloneScript(JSContext* cx, HandleObject enclosingScope, HandleFunction fun,
if (NestedScopeObject* enclosingBlock = innerBlock->enclosingNestedScope())
enclosingScope = objects[FindScopeObjectIndex(src, *enclosingBlock)];
else
enclosingScope = fun;
enclosingScope = scriptStaticScope;
clone = CloneNestedScopeObject(cx, enclosingScope, innerBlock);
} else if (obj->is<JSFunction>()) {
@ -3074,32 +3145,36 @@ js::CloneScript(JSContext* cx, HandleObject enclosingScope, HandleFunction fun,
if (cx->compartment() != innerFun->compartment()) {
MOZ_ASSERT(innerFun->isAsmJSNative());
JS_ReportError(cx, "AsmJS modules do not yet support cloning.");
return nullptr;
return false;
}
clone = innerFun;
} else {
if (innerFun->isInterpretedLazy()) {
AutoCompartment ac(cx, innerFun);
if (!innerFun->getOrCreateScript(cx))
return nullptr;
return false;
}
RootedObject staticScope(cx, innerFun->nonLazyScript()->enclosingStaticScope());
StaticScopeIter<CanGC> ssi(cx, staticScope);
RootedObject enclosingScope(cx);
if (ssi.done() || ssi.type() == StaticScopeIter<CanGC>::Function)
enclosingScope = fun;
else if (ssi.type() == StaticScopeIter<CanGC>::Block)
if (ssi.done() || ssi.type() == StaticScopeIter<CanGC>::NonSyntactic) {
enclosingScope = scriptStaticScope;
} else if (ssi.type() == StaticScopeIter<CanGC>::Function) {
MOZ_ASSERT(scriptStaticScope->is<JSFunction>());
enclosingScope = scriptStaticScope;
} else if (ssi.type() == StaticScopeIter<CanGC>::Block) {
enclosingScope = objects[FindScopeObjectIndex(src, ssi.block())];
else
} else {
enclosingScope = objects[FindScopeObjectIndex(src, ssi.staticWith())];
}
clone = CloneFunctionAndScript(cx, enclosingScope, innerFun, polluted);
clone = CloneInnerInterpretedFunction(cx, enclosingScope, innerFun);
}
} else {
clone = DeepCloneObjectLiteral(cx, obj, TenuredObject);
}
if (!clone || !objects.append(clone))
return nullptr;
return false;
}
}
@ -3111,50 +3186,11 @@ js::CloneScript(JSContext* cx, HandleObject enclosingScope, HandleFunction fun,
for (unsigned i = 0; i < nregexps; i++) {
JSObject* clone = CloneScriptRegExpObject(cx, vector[i]->as<RegExpObject>());
if (!clone || !regexps.append(clone))
return nullptr;
return false;
}
}
/*
* Wrap the script source object as needed. Self-hosted scripts may be
* in another runtime, so lazily create a new script source object to
* use for them.
*/
RootedObject sourceObject(cx);
if (cx->runtime()->isSelfHostingCompartment(src->compartment())) {
if (!cx->compartment()->selfHostingScriptSource) {
CompileOptions options(cx);
FillSelfHostingCompileOptions(options);
ScriptSourceObject* obj = frontend::CreateScriptSourceObject(cx, options);
if (!obj)
return nullptr;
cx->compartment()->selfHostingScriptSource.set(obj);
}
sourceObject = cx->compartment()->selfHostingScriptSource;
} else {
sourceObject = src->sourceObject();
if (!cx->compartment()->wrap(cx, &sourceObject))
return nullptr;
}
/* Now that all fallible allocation is complete, create the GC thing. */
CompileOptions options(cx);
options.setMutedErrors(src->mutedErrors())
.setHasPollutedScope(src->hasPollutedGlobalScope() ||
polluted == HasPollutedGlobalScope)
.setSelfHostingMode(src->selfHosted())
.setNoScriptRval(src->noScriptRval())
.setVersion(src->getVersion());
RootedScript dst(cx, JSScript::Create(cx, enclosingScope, src->savedCallerFun(),
options, src->staticLevel(),
sourceObject, src->sourceStart(), src->sourceEnd()));
if (!dst) {
js_free(data);
return nullptr;
}
/* Now that all fallible allocation is complete, do the copying. */
dst->bindings = bindings;
@ -3231,44 +3267,90 @@ js::CloneScript(JSContext* cx, HandleObject enclosingScope, HandleFunction fun,
*/
MOZ_ASSERT_IF(dst->hasNonSyntacticScope(), !dst->maybeLazyScript());
MOZ_ASSERT_IF(dst->hasNonSyntacticScope(), !dst->isRelazifiable());
return true;
}
static JSScript*
CreateEmptyScriptForClone(JSContext* cx, HandleObject enclosingScope, HandleScript src)
{
/*
* Wrap the script source object as needed. Self-hosted scripts may be
* in another runtime, so lazily create a new script source object to
* use for them.
*/
RootedObject sourceObject(cx);
if (cx->runtime()->isSelfHostingCompartment(src->compartment())) {
if (!cx->compartment()->selfHostingScriptSource) {
CompileOptions options(cx);
FillSelfHostingCompileOptions(options);
ScriptSourceObject* obj = frontend::CreateScriptSourceObject(cx, options);
if (!obj)
return nullptr;
cx->compartment()->selfHostingScriptSource.set(obj);
}
sourceObject = cx->compartment()->selfHostingScriptSource;
} else {
sourceObject = src->sourceObject();
if (!cx->compartment()->wrap(cx, &sourceObject))
return nullptr;
}
CompileOptions options(cx);
options.setMutedErrors(src->mutedErrors())
.setSelfHostingMode(src->selfHosted())
.setNoScriptRval(src->noScriptRval())
.setVersion(src->getVersion());
return JSScript::Create(cx, enclosingScope, src->savedCallerFun(),
options, src->staticLevel(),
sourceObject, src->sourceStart(), src->sourceEnd());
}
JSScript*
js::CloneGlobalScript(JSContext* cx, Handle<ScopeObject*> enclosingScope, HandleScript src)
{
// No enclosingScope means clean global.
MOZ_ASSERT(!enclosingScope || enclosingScope->is<StaticNonSyntacticScopeObjects>());
RootedScript dst(cx, CreateEmptyScriptForClone(cx, enclosingScope, src));
if (!dst)
return nullptr;
if (!detail::CopyScript(cx, enclosingScope, src, dst))
return nullptr;
return dst;
}
bool
js::CloneFunctionScript(JSContext* cx, HandleFunction original, HandleFunction clone,
PollutedGlobalScopeOption polluted, NewObjectKind newKind)
JSScript*
js::CloneScriptIntoFunction(JSContext* cx, HandleObject enclosingScope, HandleFunction fun,
HandleScript src)
{
RootedScript script(cx, clone->nonLazyScript());
MOZ_ASSERT(script);
MOZ_ASSERT(script->compartment() == original->compartment());
MOZ_ASSERT(cx->compartment() == clone->compartment(),
"Otherwise we could relazify clone below!");
MOZ_ASSERT(fun->isInterpreted());
// The only scripts with enclosing static scopes that may be cloned across
// compartments are non-strict, indirect eval scripts, as their dynamic
// scope chains terminate in the global scope immediately.
RootedObject scope(cx, script->enclosingStaticScope());
if (script->compartment() != cx->compartment() && scope) {
MOZ_ASSERT(!scope->as<StaticEvalObject>().isDirect() &&
!scope->as<StaticEvalObject>().isStrict());
scope = StaticEvalObject::create(cx, nullptr);
if (!scope)
return false;
// Allocate the destination script up front and set it as the script of
// |fun|, which is to be its container.
//
// This is so that when cloning nested functions, they can walk the static
// scope chain via fun and correctly compute the presence of a
// non-syntactic global.
RootedScript dst(cx, CreateEmptyScriptForClone(cx, enclosingScope, src));
if (!dst)
return nullptr;
dst->setFunction(fun);
if (fun->isInterpretedLazy())
fun->setUnlazifiedScript(dst);
else
fun->initScript(dst);
if (!detail::CopyScript(cx, fun, src, dst)) {
fun->setScript(nullptr);
return nullptr;
}
clone->initScript(nullptr);
JSScript* cscript = CloneScript(cx, scope, clone, script, polluted, newKind);
if (!cscript)
return false;
clone->setScript(cscript);
cscript->setFunction(clone);
script = clone->nonLazyScript();
Debugger::onNewScript(cx, script);
return true;
return dst;
}
DebugScript*

View File

@ -755,11 +755,6 @@ bool
XDRScript(XDRState<mode>* xdr, HandleObject enclosingScope, HandleScript enclosingScript,
HandleFunction fun, MutableHandleScript scriptp);
JSScript*
CloneScript(JSContext* cx, HandleObject enclosingScope, HandleFunction fun, HandleScript script,
PollutedGlobalScopeOption polluted = HasCleanGlobalScope,
NewObjectKind newKind = GenericObject);
template<XDRMode mode>
bool
XDRLazyScript(XDRState<mode>* xdr, HandleObject enclosingScope, HandleScript enclosingScript,
@ -772,6 +767,16 @@ template<XDRMode mode>
bool
XDRScriptConst(XDRState<mode>* xdr, MutableHandleValue vp);
namespace detail {
// Do not call this directly! It is exposed for the friend declaration in
// JSScript.
bool
CopyScript(JSContext* cx, HandleObject scriptStaticScope, HandleScript src, HandleScript dst);
} // namespace detail
} /* namespace js */
class JSScript : public js::gc::TenuredCell
@ -779,13 +784,13 @@ class JSScript : public js::gc::TenuredCell
template <js::XDRMode mode>
friend
bool
js::XDRScript(js::XDRState<mode>* xdr, js::HandleObject enclosingScope, js::HandleScript enclosingScript,
js::XDRScript(js::XDRState<mode>* xdr, js::HandleObject enclosingScope,
js::HandleScript enclosingScript,
js::HandleFunction fun, js::MutableHandleScript scriptp);
friend JSScript*
js::CloneScript(JSContext* cx, js::HandleObject enclosingScope, js::HandleFunction fun,
js::HandleScript src, js::PollutedGlobalScopeOption polluted,
js::NewObjectKind newKind);
friend bool
js::detail::CopyScript(JSContext* cx, js::HandleObject scriptStaticScope, js::HandleScript src,
js::HandleScript dst);
public:
//
@ -2288,9 +2293,12 @@ DescribeScriptedCallerForCompilation(JSContext* cx, MutableHandleScript maybeScr
uint32_t* pcOffset, bool* mutedErrors,
LineOption opt = NOT_CALLED_FROM_JSOP_EVAL);
bool
CloneFunctionScript(JSContext* cx, HandleFunction original, HandleFunction clone,
PollutedGlobalScopeOption polluted, NewObjectKind newKind);
JSScript*
CloneScriptIntoFunction(JSContext* cx, HandleObject enclosingScope, HandleFunction fun,
HandleScript src);
JSScript*
CloneGlobalScript(JSContext* cx, Handle<ScopeObject*> enclosingScope, HandleScript src);
} /* namespace js */

View File

@ -1722,7 +1722,9 @@ CloneObject(JSContext* cx, HandleNativeObject selfHostedObject)
js::gc::AllocKind kind = hasName
? gc::AllocKind::FUNCTION_EXTENDED
: selfHostedFunction->getAllocKind();
clone = CloneFunctionObject(cx, selfHostedFunction, cx->global(), kind, TenuredObject);
MOZ_ASSERT(!CanReuseScriptForClone(cx->compartment(), selfHostedFunction, cx->global()));
clone = CloneFunctionAndScript(cx, selfHostedFunction, cx->global(),
/* newStaticScope = */ nullptr, kind);
// To be able to re-lazify the cloned function, its name in the
// self-hosting compartment has to be stored on the clone.
if (clone && hasName)
@ -1806,22 +1808,17 @@ JSRuntime::cloneSelfHostedFunctionScript(JSContext* cx, HandlePropertyName name,
// JSFunction::generatorKind can't handle lazy self-hosted functions, so we make sure there
// aren't any.
MOZ_ASSERT(!sourceFun->isGenerator());
RootedScript sourceScript(cx, sourceFun->getOrCreateScript(cx));
if (!sourceScript)
return false;
MOZ_ASSERT(!sourceScript->enclosingStaticScope());
JSScript* cscript = CloneScript(cx, nullptr, targetFun, sourceScript);
if (!cscript)
return false;
cscript->setFunction(targetFun);
MOZ_ASSERT(sourceFun->nargs() == targetFun->nargs());
// The target function might have been relazified after it's flags changed.
targetFun->setFlags((targetFun->flags() & ~JSFunction::INTERPRETED_LAZY) |
sourceFun->flags() | JSFunction::EXTENDED);
targetFun->setScript(cscript);
MOZ_ASSERT(targetFun->isExtended());
return true;
RootedScript sourceScript(cx, sourceFun->getOrCreateScript(cx));
if (!sourceScript)
return false;
MOZ_ASSERT(!sourceScript->enclosingStaticScope());
return !!CloneScriptIntoFunction(cx, /* enclosingScope = */ nullptr, targetFun, sourceScript);
}
bool

View File

@ -1126,7 +1126,7 @@ FrameIter::matchCallee(JSContext* cx, HandleFunction fun) const
// expect both functions to have the same JSScript. If so, and if they are
// different, then they cannot be equal.
RootedObject global(cx, &fun->global());
bool useSameScript = CloneFunctionObjectUseSameScript(fun->compartment(), currentCallee, global);
bool useSameScript = CanReuseScriptForClone(fun->compartment(), currentCallee, global);
if (useSameScript &&
(currentCallee->hasScript() != fun->hasScript() ||
currentCallee->nonLazyScript() != fun->nonLazyScript()))