Bug 1202902 - Support non-syntactic extensible lexical scopes. (r=billm)

This commit is contained in:
Shu-yu Guo 2015-10-06 14:00:29 -07:00
parent 939c9e2109
commit 31d13da12c
31 changed files with 507 additions and 85 deletions

View File

@ -696,7 +696,8 @@ struct CompartmentStats
macro(Other, MallocHeap, objectMetadataTable) \
macro(Other, MallocHeap, crossCompartmentWrappersTable) \
macro(Other, MallocHeap, regexpCompartment) \
macro(Other, MallocHeap, savedStacksSet)
macro(Other, MallocHeap, savedStacksSet) \
macro(Other, MallocHeap, nonSyntacticLexicalScopesTable)
CompartmentStats()
: FOR_EACH_SIZE(ZERO_SIZE)

View File

@ -495,8 +495,10 @@ js::ExecuteInGlobalAndReturnScope(JSContext* cx, HandleObject global, HandleScri
MOZ_RELEASE_ASSERT(scriptArg->hasNonSyntacticScope());
RootedScript script(cx, scriptArg);
Rooted<GlobalObject*> globalRoot(cx, &global->as<GlobalObject>());
if (script->compartment() != cx->compartment()) {
Rooted<ScopeObject*> staticScope(cx, StaticNonSyntacticScopeObjects::create(cx, nullptr));
Rooted<ScopeObject*> staticScope(cx, &globalRoot->lexicalScope().staticBlock());
staticScope = StaticNonSyntacticScopeObjects::create(cx, staticScope);
if (!staticScope)
return false;
script = CloneGlobalScript(cx, staticScope, script);
@ -506,8 +508,15 @@ js::ExecuteInGlobalAndReturnScope(JSContext* cx, HandleObject global, HandleScri
Debugger::onNewScript(cx, script);
}
Rooted<GlobalObject*> globalRoot(cx, &global->as<GlobalObject>());
Rooted<ScopeObject*> scope(cx, NonSyntacticVariablesObject::create(cx, globalRoot));
Rooted<ClonedBlockObject*> globalLexical(cx, &globalRoot->lexicalScope());
Rooted<ScopeObject*> scope(cx, NonSyntacticVariablesObject::create(cx, globalLexical));
if (!scope)
return false;
// Unlike the non-syntactic scope chain API used by the subscript loader,
// this API creates a fresh block scope each time.
RootedObject enclosingStaticScope(cx, script->enclosingStaticScope());
scope = ClonedBlockObject::createNonSyntactic(cx, enclosingStaticScope, scope);
if (!scope)
return false;

View File

@ -2344,7 +2344,8 @@ EvalReturningScope(JSContext* cx, unsigned argc, Value* vp)
global = JS::CurrentGlobalOrNull(cx);
}
RootedObject scope(cx);
RootedObject varObj(cx);
RootedObject lexicalScope(cx);
{
// If we're switching globals here, ExecuteInGlobalAndReturnScope will
@ -2352,14 +2353,29 @@ EvalReturningScope(JSContext* cx, unsigned argc, Value* vp)
// executing it.
AutoCompartment ac(cx, global);
if (!js::ExecuteInGlobalAndReturnScope(cx, global, script, &scope))
if (!js::ExecuteInGlobalAndReturnScope(cx, global, script, &lexicalScope))
return false;
varObj = lexicalScope->enclosingScope();
}
if (!cx->compartment()->wrap(cx, &scope))
RootedObject rv(cx, JS_NewPlainObject(cx));
if (!rv)
return false;
args.rval().setObject(*scope);
RootedValue varObjVal(cx, ObjectValue(*varObj));
if (!cx->compartment()->wrap(cx, &varObjVal))
return false;
if (!JS_SetProperty(cx, rv, "vars", varObjVal))
return false;
RootedValue lexicalScopeVal(cx, ObjectValue(*lexicalScope));
if (!cx->compartment()->wrap(cx, &lexicalScopeVal))
return false;
if (!JS_SetProperty(cx, rv, "lexicals", lexicalScopeVal))
return false;
args.rval().setObject(*rv);
return true;
}

View File

@ -25,8 +25,8 @@ dbg.onDebuggerStatement = function (frame) {
};
assertEq(log, '');
var evalScope = g.evalReturningScope("canary = 'dead'; debugger; // nee", g2);
var evalScopes = g.evalReturningScope("canary = 'dead'; let lex = 42; debugger; // nee", g2);
assertEq(log, 'ecbd');
assertEq(canary, 42);
assertEq(evalScope.canary, 'dead');
assertEq(evalScopes.vars.canary, 'dead');
assertEq(evalScopes.lexicals.lex, 42);

View File

@ -2481,8 +2481,8 @@ BaselineCompiler::emit_JSOP_DEFVAR()
return callVM(DefVarInfo);
}
typedef bool (*DefLexicalFn)(JSContext*, HandlePropertyName, unsigned);
static const VMFunction DefLexicalInfo = FunctionInfo<DefLexicalFn>(DefLexicalOperation);
typedef bool (*DefLexicalFn)(JSContext*, HandlePropertyName, unsigned, HandleObject);
static const VMFunction DefLexicalInfo = FunctionInfo<DefLexicalFn>(DefLexical);
bool
BaselineCompiler::emit_JSOP_DEFCONST()
@ -2500,8 +2500,11 @@ BaselineCompiler::emit_JSOP_DEFLET()
attrs |= JSPROP_READONLY;
MOZ_ASSERT(attrs <= UINT32_MAX);
masm.loadPtr(frame.addressOfScopeChain(), R0.scratchReg());
prepareVMCall();
pushArg(R0.scratchReg());
pushArg(Imm32(attrs));
pushArg(ImmGCPtr(script->getName(pc)));

View File

@ -7617,7 +7617,12 @@ DoSetPropFallback(JSContext* cx, BaselineFrame* frame, ICSetProp_Fallback* stub_
obj->as<ScopeObject>().setAliasedVar(cx, ScopeCoordinate(pc), name, rhs);
} else if (op == JSOP_INITGLEXICAL) {
RootedValue v(cx, rhs);
InitGlobalLexicalOperation(cx, script, pc, v);
ClonedBlockObject* lexicalScope;
if (script->hasNonSyntacticScope())
lexicalScope = &NearestEnclosingExtensibleLexicalScope(frame->scopeChain());
else
lexicalScope = &cx->global()->lexicalScope();
InitGlobalLexicalOperation(cx, lexicalScope, script, pc, v);
} else {
MOZ_ASSERT(op == JSOP_SETPROP || op == JSOP_STRICTSETPROP);

View File

@ -3729,7 +3729,7 @@ CodeGenerator::visitDefVar(LDefVar* lir)
}
typedef bool (*DefLexicalFn)(JSContext*, HandlePropertyName, unsigned);
static const VMFunction DefLexicalInfo = FunctionInfo<DefLexicalFn>(DefLexicalOperation);
static const VMFunction DefLexicalInfo = FunctionInfo<DefLexicalFn>(DefGlobalLexical);
void
CodeGenerator::visitDefLexical(LDefLexical* lir)

View File

@ -1768,6 +1768,7 @@ IonBuilder::inspectOpcode(JSOp op)
return true;
case JSOP_INITGLEXICAL: {
MOZ_ASSERT(!script()->hasNonSyntacticScope());
MDefinition* value = current->pop();
current->push(constant(ObjectValue(script()->global().lexicalScope())));
current->push(value);
@ -12661,6 +12662,7 @@ IonBuilder::jsop_defvar(uint32_t index)
bool
IonBuilder::jsop_deflexical(uint32_t index)
{
MOZ_ASSERT(!script()->hasNonSyntacticScope());
MOZ_ASSERT(JSOp(*pc) == JSOP_DEFLET || JSOp(*pc) == JSOP_DEFCONST);
PropertyName* name = script()->getName(index);

View File

@ -3341,7 +3341,8 @@ SetPropertyIC::update(JSContext* cx, HandleScript outerScript, size_t cacheIndex
RootedScript script(cx);
jsbytecode* pc;
cache.getScriptedLocation(&script, &pc);
InitGlobalLexicalOperation(cx, script, pc, value);
MOZ_ASSERT(!script->hasNonSyntacticScope());
InitGlobalLexicalOperation(cx, &cx->global()->lexicalScope(), script, pc, value);
} else {
if (!SetProperty(cx, obj, name, value, cache.strict(), cache.pc()))
return false;

View File

@ -177,6 +177,27 @@ DefVar(JSContext* cx, HandlePropertyName dn, unsigned attrs, HandleObject scopeC
return DefVarOperation(cx, obj, dn, attrs);
}
bool
DefLexical(JSContext* cx, HandlePropertyName dn, unsigned attrs, HandleObject scopeChain)
{
// Find the extensible lexical scope.
Rooted<ClonedBlockObject*> lexical(cx, &NearestEnclosingExtensibleLexicalScope(scopeChain));
// Find the variables object.
RootedObject varObj(cx, scopeChain);
while (!varObj->isQualifiedVarObj())
varObj = varObj->enclosingScope();
return DefLexicalOperation(cx, lexical, varObj, dn, attrs);
}
bool
DefGlobalLexical(JSContext* cx, HandlePropertyName dn, unsigned attrs)
{
Rooted<ClonedBlockObject*> globalLexical(cx, &cx->global()->lexicalScope());
return DefLexicalOperation(cx, globalLexical, cx->global(), dn, attrs);
}
bool
MutatePrototype(JSContext* cx, HandlePlainObject obj, HandleValue value)
{

View File

@ -589,6 +589,8 @@ bool CheckOverRecursedWithExtra(JSContext* cx, BaselineFrame* frame,
uint32_t extra, uint32_t earlyCheck);
bool DefVar(JSContext* cx, HandlePropertyName dn, unsigned attrs, HandleObject scopeChain);
bool DefLexical(JSContext* cx, HandlePropertyName dn, unsigned attrs, HandleObject scopeChain);
bool DefGlobalLexical(JSContext* cx, HandlePropertyName dn, unsigned attrs);
bool MutatePrototype(JSContext* cx, HandlePlainObject obj, HandleValue value);
bool InitProp(JSContext* cx, HandleObject obj, HandlePropertyName name, HandleValue value,
jsbytecode* pc);

View File

@ -1458,6 +1458,30 @@ JS_IsGlobalObject(JSObject* obj)
return obj->is<GlobalObject>();
}
extern JS_PUBLIC_API(JSObject*)
JS_GlobalLexicalScope(JSObject* obj)
{
return &obj->as<GlobalObject>().lexicalScope();
}
extern JS_PUBLIC_API(bool)
JS_HasExtensibleLexicalScope(JSObject* obj)
{
return obj->is<GlobalObject>() || obj->compartment()->getNonSyntacticLexicalScope(obj);
}
extern JS_PUBLIC_API(JSObject*)
JS_ExtensibleLexicalScope(JSObject* obj)
{
JSObject* lexical = nullptr;
if (obj->is<GlobalObject>())
lexical = JS_GlobalLexicalScope(obj);
else
lexical = obj->compartment()->getNonSyntacticLexicalScope(obj);
MOZ_ASSERT(lexical);
return lexical;
}
JS_PUBLIC_API(JSObject*)
JS_GetGlobalForCompartmentOrNull(JSContext* cx, JSCompartment* c)
{
@ -3456,6 +3480,19 @@ CreateNonSyntacticScopeChain(JSContext* cx, AutoObjectVector& scopeChain,
// See JSObject::isQualifiedVarObj.
if (!dynamicScopeObj->setQualifiedVarObj(cx))
return false;
// Also get a non-syntactic lexical scope to capture 'let' and 'const'
// bindings. To persist lexical bindings, we have a 1-1 mapping with
// the final unwrapped dynamic scope object (the scope that stores the
// 'var' bindings) and the lexical scope.
//
// TODOshu: disallow the subscript loader from using non-distinguished
// objects as dynamic scopes.
dynamicScopeObj.set(
cx->compartment()->getOrCreateNonSyntacticLexicalScope(cx, staticScopeObj,
dynamicScopeObj));
if (!dynamicScopeObj)
return false;
}
return true;

View File

@ -1572,6 +1572,15 @@ JS_GetGlobalForObject(JSContext* cx, JSObject* obj);
extern JS_PUBLIC_API(bool)
JS_IsGlobalObject(JSObject* obj);
extern JS_PUBLIC_API(JSObject*)
JS_GlobalLexicalScope(JSObject* obj);
extern JS_PUBLIC_API(bool)
JS_HasExtensibleLexicalScope(JSObject* obj);
extern JS_PUBLIC_API(JSObject*)
JS_ExtensibleLexicalScope(JSObject* obj);
/*
* May return nullptr, if |c| never had a global (e.g. the atoms compartment),
* or if |c|'s global has been collected.

View File

@ -66,6 +66,7 @@ JSCompartment::JSCompartment(Zone* zone, const JS::CompartmentOptions& options =
selfHostingScriptSource(nullptr),
objectMetadataTable(nullptr),
lazyArrayBuffers(nullptr),
nonSyntacticLexicalScopes_(nullptr),
gcIncomingGrayPointers(nullptr),
gcPreserveJitCode(options.preserveJitCode()),
debugModeBits(0),
@ -104,6 +105,7 @@ JSCompartment::~JSCompartment()
js_delete(debugScopes);
js_delete(objectMetadataTable);
js_delete(lazyArrayBuffers);
js_delete(nonSyntacticLexicalScopes_),
js_free(enumerators);
runtime_->numCompartments--;
@ -494,6 +496,48 @@ JSCompartment::wrap(JSContext* cx, MutableHandle<PropertyDescriptor> desc)
return wrap(cx, desc.value());
}
ClonedBlockObject*
JSCompartment::getOrCreateNonSyntacticLexicalScope(JSContext* cx,
HandleObject enclosingStatic,
HandleObject enclosingScope)
{
if (!nonSyntacticLexicalScopes_) {
nonSyntacticLexicalScopes_ = cx->new_<ObjectWeakMap>(cx);
if (!nonSyntacticLexicalScopes_ || !nonSyntacticLexicalScopes_->init())
return nullptr;
}
// The key is the unwrapped dynamic scope, as we may be creating different
// DynamicWithObject wrappers each time.
MOZ_ASSERT(!enclosingScope->as<DynamicWithObject>().isSyntactic());
RootedObject key(cx, &enclosingScope->as<DynamicWithObject>().object());
RootedObject lexicalScope(cx, nonSyntacticLexicalScopes_->lookup(key));
if (!lexicalScope) {
lexicalScope = ClonedBlockObject::createNonSyntactic(cx, enclosingStatic, enclosingScope);
if (!lexicalScope)
return nullptr;
if (!nonSyntacticLexicalScopes_->add(cx, key, lexicalScope))
return nullptr;
}
return &lexicalScope->as<ClonedBlockObject>();
}
ClonedBlockObject*
JSCompartment::getNonSyntacticLexicalScope(JSObject* enclosingScope) const
{
if (!nonSyntacticLexicalScopes_)
return nullptr;
if (!enclosingScope->is<DynamicWithObject>())
return nullptr;
JSObject* key = &enclosingScope->as<DynamicWithObject>().object();
JSObject* lexicalScope = nonSyntacticLexicalScopes_->lookup(key);
if (!lexicalScope)
return nullptr;
return &lexicalScope->as<ClonedBlockObject>();
}
void
JSCompartment::traceOutgoingCrossCompartmentWrappers(JSTracer* trc)
{
@ -599,6 +643,9 @@ JSCompartment::traceRoots(JSTracer* trc, js::gc::GCRuntime::TraceOrMarkRuntime t
MOZ_ASSERT(script == r.front().key(), "const_cast is only a work-around");
}
}
if (nonSyntacticLexicalScopes_)
nonSyntacticLexicalScopes_->trace(trc);
}
void
@ -1052,7 +1099,8 @@ JSCompartment::addSizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf,
size_t* objectMetadataTablesArg,
size_t* crossCompartmentWrappersArg,
size_t* regexpCompartment,
size_t* savedStacksSet)
size_t* savedStacksSet,
size_t* nonSyntacticLexicalScopesArg)
{
*compartmentObject += mallocSizeOf(this);
objectGroups.addSizeOfExcludingThis(mallocSizeOf, tiAllocationSiteTables,
@ -1068,6 +1116,8 @@ JSCompartment::addSizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf,
*crossCompartmentWrappersArg += crossCompartmentWrappers.sizeOfExcludingThis(mallocSizeOf);
*regexpCompartment += regExps.sizeOfExcludingThis(mallocSizeOf);
*savedStacksSet += savedStacks_.sizeOfExcludingThis(mallocSizeOf);
if (nonSyntacticLexicalScopes_)
*nonSyntacticLexicalScopesArg += nonSyntacticLexicalScopes_->sizeOfIncludingThis(mallocSizeOf);
}
void

View File

@ -29,6 +29,7 @@ template<class Node> class ComponentFinder;
} // namespace gc
struct NativeIterator;
class ClonedBlockObject;
/*
* A single-entry cache for some base-10 double-to-string conversions. This
@ -397,7 +398,8 @@ struct JSCompartment
size_t* objectMetadataTables,
size_t* crossCompartmentWrappers,
size_t* regexpCompartment,
size_t* savedStacksSet);
size_t* savedStacksSet,
size_t* nonSyntacticLexicalScopes);
/*
* Shared scope property tree, and arena-pool for allocating its nodes.
@ -442,6 +444,13 @@ struct JSCompartment
// All unboxed layouts in the compartment.
mozilla::LinkedList<js::UnboxedLayout> unboxedLayouts;
private:
// All non-syntactic lexical scopes in the compartment. These are kept in
// a map because when loading scripts into a non-syntactic scope, we need
// to use the same lexical scope to persist lexical bindings.
js::ObjectWeakMap* nonSyntacticLexicalScopes_;
public:
/* During GC, stores the index of this compartment in rt->compartments. */
unsigned gcIndex;
@ -511,6 +520,11 @@ struct JSCompartment
explicit WrapperEnum(JSCompartment* c) : js::WrapperMap::Enum(c->crossCompartmentWrappers) {}
};
js::ClonedBlockObject* getOrCreateNonSyntacticLexicalScope(JSContext* cx,
js::HandleObject enclosingStatic,
js::HandleObject enclosingScope);
js::ClonedBlockObject* getNonSyntacticLexicalScope(JSObject* enclosingScope) const;
/*
* This method traces data that is live iff we know that this compartment's
* global is still live.

View File

@ -2795,13 +2795,12 @@ SetJitExceptionHandler(JitExceptionHandler handler);
#endif
/*
* Get the object underlying the object environment (in the ES
* NewObjectEnvironment) sense for a given function. If the function is not
* scripted or does not have an object environment, just returns the function's
* parent.
* Get the nearest enclosing with scope object for a given function. If the
* function is not scripted or is not enclosed by a with scope, returns the
* global.
*/
extern JS_FRIEND_API(JSObject*)
GetObjectEnvironmentObjectForFunction(JSFunction* fun);
GetNearestEnclosingWithScopeObjectForFunction(JSFunction* fun);
/*
* Get the first SavedFrame object in this SavedFrame stack whose principals are

View File

@ -6525,7 +6525,12 @@ EvaluateInEnv(JSContext* cx, Handle<Env*> env, HandleValue thisv, AbstractFrameP
*/
Rooted<ScopeObject*> enclosingStaticScope(cx);
if (!IsGlobalLexicalScope(env)) {
enclosingStaticScope = StaticNonSyntacticScopeObjects::create(cx, nullptr);
// If we are doing a global evalWithBindings, we will still need to
// link the static global lexical scope to the static non-syntactic
// scope.
if (IsGlobalLexicalScope(env->enclosingScope()))
enclosingStaticScope = &cx->global()->lexicalScope().staticBlock();
enclosingStaticScope = StaticNonSyntacticScopeObjects::create(cx, enclosingStaticScope);
if (!enclosingStaticScope)
return false;
} else {
@ -6675,9 +6680,9 @@ DebuggerGenericEval(JSContext* cx, const char* fullMethodName, const Value& code
return false;
} else {
/*
* Use the global as 'this'. If the global is an inner object, it
* should have a thisObject hook that returns the appropriate outer
* object.
* Use the global lexical scope as 'this'. If the global is an inner
* object, it should have a thisObject hook that returns the
* appropriate outer object.
*/
JSObject* thisObj = GetThisObject(cx, scope);
if (!thisObj)

View File

@ -300,19 +300,18 @@ SetNameOperation(JSContext* cx, JSScript* script, jsbytecode* pc, HandleObject s
}
inline bool
DefLexicalOperation(JSContext* cx, HandlePropertyName name, unsigned attrs)
DefLexicalOperation(JSContext* cx, Handle<ClonedBlockObject*> lexicalScope,
HandleObject varObj, HandlePropertyName name, unsigned attrs)
{
Rooted<ClonedBlockObject*> globalLexical(cx, &cx->global()->lexicalScope());
// Due to the extensibility of the global lexical scope, we must check for
// redeclaring a binding.
mozilla::Maybe<frontend::Definition::Kind> redeclKind;
RootedId id(cx, NameToId(name));
RootedShape shape(cx);
if ((shape = globalLexical->lookup(cx, name))) {
if ((shape = lexicalScope->lookup(cx, name))) {
redeclKind = mozilla::Some(shape->writable() ? frontend::Definition::LET
: frontend::Definition::CONST);
} else if ((shape = cx->global()->lookup(cx, name))) {
} else if (varObj->isNative() && (shape = varObj->as<NativeObject>().lookup(cx, name))) {
if (!shape->configurable())
redeclKind = mozilla::Some(frontend::Definition::VAR);
} else {
@ -332,7 +331,8 @@ DefLexicalOperation(JSContext* cx, HandlePropertyName name, unsigned attrs)
}
inline bool
DefLexicalOperation(JSContext* cx, JSScript* script, jsbytecode* pc)
DefLexicalOperation(JSContext* cx, ClonedBlockObject* lexicalScopeArg,
JSObject* varObjArg, JSScript* script, jsbytecode* pc)
{
MOZ_ASSERT(*pc == JSOP_DEFLET || *pc == JSOP_DEFCONST);
RootedPropertyName name(cx, script->getName(pc));
@ -341,17 +341,25 @@ DefLexicalOperation(JSContext* cx, JSScript* script, jsbytecode* pc)
if (*pc == JSOP_DEFCONST)
attrs |= JSPROP_READONLY;
return DefLexicalOperation(cx, name, attrs);
Rooted<ClonedBlockObject*> lexicalScope(cx, lexicalScopeArg);
RootedObject varObj(cx, varObjArg);
MOZ_ASSERT_IF(!script->hasNonSyntacticScope(),
lexicalScope == &cx->global()->lexicalScope() && varObj == cx->global());
return DefLexicalOperation(cx, lexicalScope, varObj, name, attrs);
}
inline void
InitGlobalLexicalOperation(JSContext* cx, JSScript* script, jsbytecode* pc, HandleValue value)
InitGlobalLexicalOperation(JSContext* cx, ClonedBlockObject* lexicalScopeArg,
JSScript* script, jsbytecode* pc, HandleValue value)
{
MOZ_ASSERT_IF(!script->hasNonSyntacticScope(),
lexicalScopeArg == &cx->global()->lexicalScope());
MOZ_ASSERT(*pc == JSOP_INITGLEXICAL);
Rooted<ClonedBlockObject*> globalLexical(cx, &cx->global()->lexicalScope());
RootedShape shape(cx, globalLexical->lookup(cx, script->getName(pc)));
Rooted<ClonedBlockObject*> lexicalScope(cx, lexicalScopeArg);
RootedShape shape(cx, lexicalScope->lookup(cx, script->getName(pc)));
MOZ_ASSERT(shape);
globalLexical->setSlot(shape->slot(), value);
lexicalScope->setSlot(shape->slot(), value);
}
inline bool

View File

@ -3429,8 +3429,13 @@ END_CASE(JSOP_INITALIASEDLEXICAL)
CASE(JSOP_INITGLEXICAL)
{
ClonedBlockObject* lexicalScope;
if (script->hasNonSyntacticScope())
lexicalScope = &REGS.fp()->extensibleLexicalScope();
else
lexicalScope = &cx->global()->lexicalScope();
HandleValue value = REGS.stackHandleAt(-1);
InitGlobalLexicalOperation(cx, script, REGS.pc, value);
InitGlobalLexicalOperation(cx, lexicalScope, script, REGS.pc, value);
}
END_CASE(JSOP_INITGLEXICAL)
@ -3502,7 +3507,16 @@ END_CASE(JSOP_DEFVAR)
CASE(JSOP_DEFCONST)
CASE(JSOP_DEFLET)
{
if (!DefLexicalOperation(cx, script, REGS.pc))
ClonedBlockObject* lexicalScope;
JSObject* varObj;
if (script->hasNonSyntacticScope()) {
lexicalScope = &REGS.fp()->extensibleLexicalScope();
varObj = &REGS.fp()->varObj();
} else {
lexicalScope = &cx->global()->lexicalScope();
varObj = cx->global();
}
if (!DefLexicalOperation(cx, lexicalScope, varObj, script, REGS.pc))
goto error;
}
END_CASE(JSOP_DEFLET)

View File

@ -342,7 +342,8 @@ StatsCompartmentCallback(JSRuntime* rt, void* data, JSCompartment* compartment)
&cStats.objectMetadataTable,
&cStats.crossCompartmentWrappersTable,
&cStats.regexpCompartment,
&cStats.savedStacksSet);
&cStats.savedStacksSet,
&cStats.nonSyntacticLexicalScopesTable);
}
static void

View File

@ -16,6 +16,14 @@
namespace js {
inline ClonedBlockObject&
NearestEnclosingExtensibleLexicalScope(JSObject* scope)
{
while (!IsExtensibleLexicalScope(scope))
scope = scope->enclosingScope();
return scope->as<ClonedBlockObject>();
}
inline void
ScopeObject::setAliasedVar(JSContext* cx, ScopeCoordinate sc, PropertyName* name, const Value& v)
{

View File

@ -782,8 +782,10 @@ const Class StaticNonSyntacticScopeObjects::class_ = {
};
/* static */ NonSyntacticVariablesObject*
NonSyntacticVariablesObject::create(JSContext* cx, Handle<GlobalObject*> global)
NonSyntacticVariablesObject::create(JSContext* cx, Handle<ClonedBlockObject*> globalLexical)
{
MOZ_ASSERT(globalLexical->isGlobal());
Rooted<NonSyntacticVariablesObject*> obj(cx,
NewObjectWithNullTaggedProto<NonSyntacticVariablesObject>(cx, TenuredObject,
BaseShape::DELEGATE));
@ -794,7 +796,7 @@ NonSyntacticVariablesObject::create(JSContext* cx, Handle<GlobalObject*> global)
if (!obj->setQualifiedVarObj(cx))
return nullptr;
obj->setEnclosingScope(global);
obj->setEnclosingScope(globalLexical);
return obj;
}
@ -869,6 +871,26 @@ ClonedBlockObject::createGlobal(JSContext* cx, Handle<GlobalObject*> global)
return lexical;
}
/* static */ ClonedBlockObject*
ClonedBlockObject::createNonSyntactic(JSContext* cx, HandleObject enclosingStatic,
HandleObject enclosingScope)
{
MOZ_ASSERT(enclosingStatic->is<StaticNonSyntacticScopeObjects>());
MOZ_ASSERT(!IsSyntacticScope(enclosingScope));
Rooted<StaticBlockObject*> staticLexical(cx, StaticBlockObject::create(cx));
if (!staticLexical)
return nullptr;
staticLexical->setLocalOffset(UINT32_MAX);
staticLexical->initEnclosingScope(enclosingStatic);
Rooted<ClonedBlockObject*> lexical(cx, ClonedBlockObject::create(cx, staticLexical,
enclosingScope));
if (!lexical)
return nullptr;
return lexical;
}
/* static */ ClonedBlockObject*
ClonedBlockObject::createHollowForDebug(JSContext* cx, Handle<StaticBlockObject*> block)
{
@ -987,9 +1009,11 @@ static JSObject*
block_ThisObject(JSContext* cx, HandleObject obj)
{
// No other block objects should ever get passed to the 'this' object
// hook.
MOZ_ASSERT(obj->as<ClonedBlockObject>().isGlobal());
MOZ_ASSERT(&obj->as<ClonedBlockObject>().enclosingScope() == cx->global());
// hook except the global lexical scope and non-syntactic ones.
MOZ_ASSERT(obj->as<ClonedBlockObject>().isGlobal() ||
!obj->as<ClonedBlockObject>().isSyntactic());
MOZ_ASSERT_IF(obj->as<ClonedBlockObject>().isGlobal(),
obj->enclosingScope() == cx->global());
RootedObject enclosing(cx, obj->enclosingScope());
return GetThisObject(cx, enclosing);
}
@ -1666,9 +1690,12 @@ class DebugScopeProxy : public BaseProxyHandler
if (!shape)
return true;
// Currently consider all global lexical bindings to be aliased.
if (IsGlobalLexicalScope(block))
// Currently consider all global and non-syntactic top-level lexical
// bindings to be aliased.
if (block->isExtensible()) {
MOZ_ASSERT(IsGlobalLexicalScope(block) || !IsSyntacticScope(block));
return true;
}
unsigned i = block->staticBlock().shapeToIndex(*shape);
if (block->staticBlock().isAliased(i))
@ -2833,13 +2860,16 @@ js::GetDebugScopeForFrame(JSContext* cx, AbstractFramePtr frame, jsbytecode* pc)
// See declaration and documentation in jsfriendapi.h
JS_FRIEND_API(JSObject*)
js::GetObjectEnvironmentObjectForFunction(JSFunction* fun)
js::GetNearestEnclosingWithScopeObjectForFunction(JSFunction* fun)
{
if (!fun->isInterpreted())
return &fun->global();
JSObject* env = fun->environment();
if (!env || !env->is<DynamicWithObject>())
while (env && !env->is<DynamicWithObject>())
env = env->enclosingScope();
if (!env)
return &fun->global();
return &env->as<DynamicWithObject>().object();

View File

@ -215,7 +215,7 @@ ScopeCoordinateFunctionScript(JSScript* script, jsbytecode* pc);
* |
* ScopeObject---+---+ Engine-internal scope
* | | | | |
* | | | | StaticNonSyntacticScopeObjects See NB2
* | | | | StaticNonSyntacticScopeObjects See "Non-syntactic scope objects"
* | | | |
* | | | StaticEvalObject Placeholder so eval scopes may be iterated through
* | | |
@ -247,9 +247,6 @@ ScopeCoordinateFunctionScript(JSScript* script, jsbytecode* pc);
* are cloned at runtime. These objects should never escape into the wild and
* support a restricted set of ScopeObject operations.
*
* NB2: StaticNonSyntacticScopeObjects notify either of 0+ non-syntactic
* DynamicWithObjects on the dynamic scope chain or a NonSyntacticScopeObject.
*
* See also "Debug scope objects" below.
*/
@ -459,13 +456,113 @@ class StaticEvalObject : public ScopeObject
inline bool isNonGlobal() const;
};
// Static scope objects that stand in for one or more "polluting global"
// scopes on the dynamic scope chain.
//
// There are two flavors of polluting global scopes on the dynamic scope
// chain: either 0+ non-syntactic DynamicWithObjects, or 1
// NonSyntacticVariablesObject, created exclusively in
// js::ExecuteInGlobalAndReturnScope.
/*
* Non-syntactic scope objects
*
* A non-syntactic scope is one that was not created due to source code. On
* the static scope chain, a single StaticNonSyntacticScopeObjects maps to 0+
* non-syntactic dynamic scope objects. This is contrasted with syntactic
* scopes, where each syntactic static scope corresponds to 0 or 1 dynamic
* scope objects.
*
* There are 3 kinds of dynamic non-syntactic scopes:
*
* 1. DynamicWithObject
*
* When the embedding compiles or executes a script, it has the option to
* pass in a vector of objects to be used as the initial scope chain. Each
* of those objects is wrapped by a DynamicWithObject.
*
* The innermost scope passed in by the embedding becomes a qualified
* variables object that captures 'var' bindings. That is, it wraps the
* holder object of 'var' bindings.
*
* Does not hold 'let' or 'const' bindings.
*
* 2. NonSyntacticVariablesObject
*
* When the embedding wants qualified 'var' bindings and unqualified
* bareword assignments to go on a different object than the global
* object. While any object can be made into a qualified variables object,
* only the GlobalObject and NonSyntacticVariablesObject are considered
* unqualified variables objects.
*
* Unlike DynamicWithObjects, this object is itself the holder of 'var'
* bindings.
*
* Does not hold 'let' or 'const' bindings.
*
* 3. ClonedBlockObject
*
* Each non-syntactic object used as a qualified variables object needs to
* enclose a non-syntactic ClonedBlockObject to hold 'let' and 'const'
* bindings. There is a bijection per compartment between the non-syntactic
* variables objects and their non-syntactic ClonedBlockObjects.
*
* Does not hold 'var' bindings.
*
* The embedding (Gecko) uses non-syntactic scopes for various things, some of
* which are detailed below. All scope chain listings below are, from top to
* bottom, outermost to innermost.
*
* A. Component loading
*
* Components may be loaded in "reuse loader global" mode, where to save on
* memory, all JSMs and JS-implemented XPCOM modules are loaded into a single
* global. Each individual JSMs are compiled as functions with their own
* FakeBackstagePass. They have the following dynamic scope chain:
*
* BackstagePass global
* |
* Global lexical scope
* |
* DynamicWithObject wrapping FakeBackstagePass
* |
* Non-syntactic lexical scope
*
* B. Subscript loading
*
* Subscripts may be loaded into a target object. They have the following
* dynamic scope chain:
*
* Loader global
* |
* Global lexical scope
* |
* DynamicWithObject wrapping target
* |
* ClonedBlockObject
*
* C. Frame scripts
*
* XUL frame scripts are always loaded with a NonSyntacticVariablesObject as a
* "polluting global". This is done exclusively in
* js::ExecuteInGlobalAndReturnScope.
*
* Loader global
* |
* Global lexical scope
* |
* NonSyntacticVariablesObject
* |
* ClonedBlockObject
*
* D. XBL
*
* XBL methods are compiled as functions with XUL elements on the scope chain.
* For a chain of elements e0,...,eN:
*
* ...
* |
* DynamicWithObject wrapping eN
* |
* ...
* |
* DynamicWithObject wrapping e0
* |
* ClonedBlockObject
*
*/
class StaticNonSyntacticScopeObjects : public ScopeObject
{
public:
@ -482,7 +579,7 @@ class StaticNonSyntacticScopeObjects : public ScopeObject
// A non-syntactic dynamic scope object that captures non-lexical
// bindings. That is, a scope object that captures both qualified var
// assignments and unqualified bareword assignments. Its parent is always the
// real global.
// global lexical scope.
//
// This is used in ExecuteInGlobalAndReturnScope and sits in front of the
// global scope to capture 'var' and bareword asignments.
@ -492,7 +589,8 @@ class NonSyntacticVariablesObject : public ScopeObject
static const unsigned RESERVED_SLOTS = 1;
static const Class class_;
static NonSyntacticVariablesObject* create(JSContext* cx, Handle<GlobalObject*> global);
static NonSyntacticVariablesObject* create(JSContext* cx,
Handle<ClonedBlockObject*> globalLexical);
};
class NestedScopeObject : public ScopeObject
@ -703,10 +801,14 @@ class StaticBlockObject : public BlockObject
}
// Is this the static global lexical scope?
bool isGlobal() {
bool isGlobal() const {
return !enclosingStaticScope();
}
bool isSyntactic() const {
return !isExtensible() || isGlobal();
}
/* Frontend-only functions ***********************************************/
/* Initialization functions for above fields. */
@ -765,6 +867,9 @@ class ClonedBlockObject : public BlockObject
static ClonedBlockObject* createGlobal(JSContext* cx, Handle<GlobalObject*> global);
static ClonedBlockObject* createNonSyntactic(JSContext* cx, HandleObject enclosingStatic,
HandleObject enclosingScope);
static ClonedBlockObject* createHollowForDebug(JSContext* cx,
Handle<StaticBlockObject*> block);
@ -795,6 +900,10 @@ class ClonedBlockObject : public BlockObject
return enclosingScope().as<GlobalObject>();
}
bool isSyntactic() const {
return !isExtensible() || isGlobal();
}
/* Copy in all the unaliased formals and locals. */
void copyUnaliasedValues(AbstractFramePtr frame);
@ -1179,9 +1288,25 @@ namespace js {
inline bool
IsSyntacticScope(JSObject* scope)
{
return scope->is<ScopeObject>() &&
(!scope->is<DynamicWithObject>() || scope->as<DynamicWithObject>().isSyntactic()) &&
!scope->is<NonSyntacticVariablesObject>();
if (!scope->is<ScopeObject>())
return false;
if (scope->is<DynamicWithObject>())
return scope->as<DynamicWithObject>().isSyntactic();
if (scope->is<ClonedBlockObject>())
return scope->as<ClonedBlockObject>().isSyntactic();
if (scope->is<NonSyntacticVariablesObject>())
return false;
return true;
}
inline bool
IsExtensibleLexicalScope(JSObject* scope)
{
return scope->is<ClonedBlockObject>() && scope->as<ClonedBlockObject>().isExtensible();
}
inline bool
@ -1234,13 +1359,12 @@ inline bool
ScopeIter::hasNonSyntacticScopeObject() const
{
// The case we're worrying about here is a NonSyntactic static scope which
// has 0+ corresponding non-syntactic DynamicWithObject scopes or a
// NonSyntacticVariablesObject.
// has 0+ corresponding non-syntactic DynamicWithObject scopes, a
// NonSyntacticVariablesObject, or a non-syntactic ClonedBlockObject.
if (ssi_.type() == StaticScopeIter<CanGC>::NonSyntactic) {
MOZ_ASSERT_IF(scope_->is<DynamicWithObject>(),
!scope_->as<DynamicWithObject>().isSyntactic());
return scope_->is<DynamicWithObject>() ||
scope_->is<NonSyntacticVariablesObject>();
return scope_->is<ScopeObject>() && !IsSyntacticScope(scope_);
}
return false;
}

View File

@ -62,7 +62,7 @@ InterpreterFrame::global() const
}
inline JSObject&
InterpreterFrame::varObj()
InterpreterFrame::varObj() const
{
JSObject* obj = scopeChain();
while (!obj->isQualifiedVarObj())
@ -70,6 +70,12 @@ InterpreterFrame::varObj()
return *obj;
}
inline ClonedBlockObject&
InterpreterFrame::extensibleLexicalScope() const
{
return NearestEnclosingExtensibleLexicalScope(scopeChain());
}
inline JSCompartment*
InterpreterFrame::compartment() const
{

View File

@ -144,7 +144,11 @@ AssertDynamicScopeMatchesStaticScope(JSContext* cx, JSScript* script, JSObject*
RootedObject enclosingScope(cx, script->enclosingStaticScope());
for (StaticScopeIter<NoGC> i(enclosingScope); !i.done(); i++) {
if (i.type() == StaticScopeIter<NoGC>::NonSyntactic) {
while (scope->is<DynamicWithObject>() || scope->is<NonSyntacticVariablesObject>()) {
while (scope->is<DynamicWithObject>() ||
scope->is<NonSyntacticVariablesObject>() ||
(scope->is<ClonedBlockObject>() &&
!scope->as<ClonedBlockObject>().isSyntactic()))
{
MOZ_ASSERT(!IsSyntacticScope(scope));
scope = &scope->as<ScopeObject>().enclosingScope();
}

View File

@ -45,6 +45,7 @@ class ScriptFrameIter;
class SPSProfiler;
class InterpreterFrame;
class StaticBlockObject;
class ClonedBlockObject;
class ScopeCoordinate;
@ -616,7 +617,8 @@ class InterpreterFrame
inline ScopeObject& aliasedVarScope(ScopeCoordinate sc) const;
inline GlobalObject& global() const;
inline CallObject& callObj() const;
inline JSObject& varObj();
inline JSObject& varObj() const;
inline ClonedBlockObject& extensibleLexicalScope() const;
inline void pushOnScopeChain(ScopeObject& scope);
inline void popOffScopeChain();

View File

@ -2566,9 +2566,11 @@ UpdatePropertyType(ExclusiveContext* cx, HeapTypeSet* types, NativeObject* obj,
* comment).
*
* Also don't add untracked values (initial uninitialized lexical
* magic values and optimized out values) as appearing in CallObjects.
* magic values and optimized out values) as appearing in CallObjects
* and the global lexical scope.
*/
MOZ_ASSERT_IF(TypeSet::IsUntrackedValue(value), obj->is<CallObject>());
MOZ_ASSERT_IF(TypeSet::IsUntrackedValue(value),
obj->is<CallObject>() || IsExtensibleLexicalScope(obj));
if ((indexed || !value.isUndefined() || !CanHaveEmptyPropertyTypesForOwnProperty(obj)) &&
!TypeSet::IsUntrackedValue(value))
{

View File

@ -325,6 +325,25 @@ mozJSComponentLoader::ReallyInit()
return NS_OK;
}
// For terrible compatibility reasons, we need to consider both the global
// lexical scope and the global of modules when searching for exported
// symbols.
static JSObject*
ResolveModuleObjectProperty(JSContext* aCx, HandleObject aModObj, const char* name)
{
if (JS_HasExtensibleLexicalScope(aModObj)) {
RootedObject lexical(aCx, JS_ExtensibleLexicalScope(aModObj));
bool found;
if (!JS_HasOwnProperty(aCx, lexical, name, &found)) {
return nullptr;
}
if (found) {
return lexical;
}
}
return aModObj;
}
const mozilla::Module*
mozJSComponentLoader::LoadModule(FileLocation& aFile)
{
@ -372,9 +391,12 @@ mozJSComponentLoader::LoadModule(FileLocation& aFile)
JSAutoCompartment ac(cx, entry->obj);
RootedObject entryObj(cx, entry->obj);
RootedObject NSGetFactoryHolder(cx, ResolveModuleObjectProperty(cx, entryObj, "NSGetFactory"));
RootedValue NSGetFactory_val(cx);
if (!JS_GetProperty(cx, entryObj, "NSGetFactory", &NSGetFactory_val) ||
NSGetFactory_val.isUndefined()) {
if (!NSGetFactoryHolder ||
!JS_GetProperty(cx, NSGetFactoryHolder, "NSGetFactory", &NSGetFactory_val) ||
NSGetFactory_val.isUndefined())
{
return nullptr;
}
@ -424,7 +446,7 @@ mozJSComponentLoader::FindTargetObject(JSContext* aCx,
if (mReuseLoaderGlobal) {
JSFunction* fun = js::GetOutermostEnclosingFunctionOfScriptedCaller(aCx);
if (fun) {
JSObject* funParent = js::GetObjectEnvironmentObjectForFunction(fun);
JSObject* funParent = js::GetNearestEnclosingWithScopeObjectForFunction(fun);
if (JS_GetClass(funParent) == &kFakeBackstagePassJSClass)
targetObject = funParent;
}
@ -964,6 +986,9 @@ mozJSComponentLoader::UnloadModules()
RootedObject global(cx, mLoaderGlobal->GetJSObject());
if (global) {
JSAutoCompartment ac(cx, global);
if (JS_HasExtensibleLexicalScope(global)) {
JS_SetAllNonReservedSlotsToUndefined(cx, JS_ExtensibleLexicalScope(global));
}
JS_SetAllNonReservedSlotsToUndefined(cx, global);
} else {
NS_WARNING("Going to leak!");
@ -1074,6 +1099,22 @@ mozJSComponentLoader::IsModuleLoaded(const nsACString& aLocation,
return NS_OK;
}
static JSObject*
ResolveModuleObjectPropertyById(JSContext* aCx, HandleObject aModObj, HandleId id)
{
if (JS_HasExtensibleLexicalScope(aModObj)) {
RootedObject lexical(aCx, JS_ExtensibleLexicalScope(aModObj));
bool found;
if (!JS_HasOwnPropertyById(aCx, lexical, id, &found)) {
return nullptr;
}
if (found) {
return lexical;
}
}
return aModObj;
}
nsresult
mozJSComponentLoader::ImportInto(const nsACString& aLocation,
HandleObject targetObj,
@ -1178,8 +1219,10 @@ mozJSComponentLoader::ImportInto(const nsACString& aLocation,
JSAutoCompartment ac(cx, mod->obj);
RootedValue symbols(cx);
RootedObject modObj(cx, mod->obj);
if (!JS_GetProperty(cx, modObj,
RootedObject exportedSymbolsHolder(cx, ResolveModuleObjectProperty(cx, mod->obj,
"EXPORTED_SYMBOLS"));
if (!exportedSymbolsHolder ||
!JS_GetProperty(cx, exportedSymbolsHolder,
"EXPORTED_SYMBOLS", &symbols)) {
return ReportOnCaller(cxhelper, ERROR_NOT_PRESENT,
PromiseFlatCString(aLocation).get());
@ -1210,6 +1253,7 @@ mozJSComponentLoader::ImportInto(const nsACString& aLocation,
RootedValue value(cx);
RootedId symbolId(cx);
RootedObject symbolHolder(cx);
for (uint32_t i = 0; i < symbolCount; ++i) {
if (!JS_GetElement(cx, symbolsObj, i, &value) ||
!value.isString() ||
@ -1218,8 +1262,9 @@ mozJSComponentLoader::ImportInto(const nsACString& aLocation,
PromiseFlatCString(aLocation).get(), i);
}
RootedObject modObj(cx, mod->obj);
if (!JS_GetPropertyById(cx, modObj, symbolId, &value)) {
symbolHolder = ResolveModuleObjectPropertyById(cx, mod->obj, symbolId);
if (!symbolHolder ||
!JS_GetPropertyById(cx, symbolHolder, symbolId, &value)) {
JSAutoByteString bytes(cx, JSID_TO_STRING(symbolId));
if (!bytes)
return NS_ERROR_FAILURE;

View File

@ -112,6 +112,9 @@ class mozJSComponentLoader : public mozilla::ModuleLoader,
mozilla::AutoJSContext cx;
JSAutoCompartment ac(cx, obj);
if (JS_HasExtensibleLexicalScope(obj)) {
JS_SetAllNonReservedSlotsToUndefined(cx, JS_ExtensibleLexicalScope(obj));
}
JS_SetAllNonReservedSlotsToUndefined(cx, obj);
obj = nullptr;
thisObjectKey = nullptr;

View File

@ -1556,6 +1556,7 @@ XRE_XPCShellMain(int argc, char** argv, char** envp)
JS_DropPrincipals(rt, gJSPrincipals);
JS_SetAllNonReservedSlotsToUndefined(cx, glob);
JS_SetAllNonReservedSlotsToUndefined(cx, JS_GlobalLexicalScope(glob));
JS_GC(rt);
}
JS_GC(rt);

View File

@ -333,7 +333,7 @@ function _register_protocol_handlers() {
}
function _register_modules_protocol_handler() {
if (!this._TESTING_MODULES_DIR) {
if (!_TESTING_MODULES_DIR) {
throw new Error("Please define a path where the testing modules can be " +
"found in a variable called '_TESTING_MODULES_DIR' before " +
"head.js is included.");
@ -1227,7 +1227,7 @@ function do_load_child_test_harness()
+ "const _JSDEBUGGER_PORT=0; "
+ "const _XPCSHELL_PROCESS='child';";
if (this._TESTING_MODULES_DIR) {
if (_TESTING_MODULES_DIR) {
command += " const _TESTING_MODULES_DIR=" + uneval(_TESTING_MODULES_DIR) + ";";
}