Bug 930414 - Add module scopes, using ModuleObject as the static scope and ModuleEnvironementObject as the dynamic scope r=shu

This commit is contained in:
Jon Coppeard 2015-08-24 15:58:36 +01:00
parent a920d4b2e6
commit 381c692f63
11 changed files with 177 additions and 70 deletions

View File

@ -413,6 +413,8 @@ bool
ParseContext<ParseHandler>::generateBindings(ExclusiveContext* cx, TokenStream& ts, LifoAlloc& alloc,
MutableHandle<Bindings> bindings) const
{
MOZ_ASSERT_IF(sc->isFunctionBox(), args_.length() < ARGNO_LIMIT);
MOZ_ASSERT_IF(sc->isModuleBox(), args_.length() == 0);
MOZ_ASSERT(vars_.length() + bodyLevelLexicals_.length() < LOCALNO_LIMIT);
/*
@ -448,29 +450,7 @@ ParseContext<ParseHandler>::generateBindings(ExclusiveContext* cx, TokenStream&
return Bindings::initWithTemporaryStorage(cx, bindings, args_.length(), vars_.length(),
bodyLevelLexicals_.length(), blockScopeDepth,
numUnaliasedVars, numUnaliasedBodyLevelLexicals,
packedBindings);
}
template <typename ParseHandler>
bool
ParseContext<ParseHandler>::generateFunctionBindings(ExclusiveContext* cx, TokenStream& ts,
LifoAlloc& alloc,
MutableHandle<Bindings> bindings) const
{
MOZ_ASSERT(sc->isFunctionBox());
MOZ_ASSERT(args_.length() < ARGNO_LIMIT);
return generateBindings(cx, ts, alloc, bindings);
}
template <typename ParseHandler>
bool
ParseContext<ParseHandler>::generateModuleBindings(ExclusiveContext* cx, TokenStream& ts,
LifoAlloc& alloc,
MutableHandle<Bindings> bindings) const
{
MOZ_ASSERT(sc->isModuleBox());
MOZ_ASSERT(args_.length() == 0);
return generateBindings(cx, ts, alloc, bindings);
packedBindings, sc->isModuleBox());
}
template <typename ParseHandler>
@ -883,7 +863,7 @@ Parser<ParseHandler>::standaloneModule(HandleModuleObject module)
return null();
Rooted<Bindings> bindings(context, modulebox->bindings);
if (!modulepc.generateModuleBindings(context, tokenStream, alloc, &bindings))
if (!modulepc.generateBindings(context, tokenStream, alloc, &bindings))
return null();
modulebox->bindings = bindings;
@ -973,7 +953,7 @@ Parser<FullParseHandler>::standaloneFunctionBody(HandleFunction fun,
}
Rooted<Bindings> bindings(context, funbox->bindings);
if (!funpc.generateFunctionBindings(context, tokenStream, alloc, &bindings))
if (!funpc.generateBindings(context, tokenStream, alloc, &bindings))
return null();
funbox->bindings = bindings;
@ -1529,7 +1509,7 @@ ConvertDefinitionToNamedLambdaUse(TokenStream& ts, ParseContext<FullParseHandler
* Since 'dn' is a placeholder, it has not been defined in the
* ParseContext and hence we must manually flag a closed-over
* callee name as needing a dynamic scope (this is done for all
* definitions in the ParseContext by generateFunctionBindings).
* definitions in the ParseContext by generateBindings).
*
* If 'dn' has been assigned to, then we also flag the function
* scope has needing a dynamic scope so that dynamic scope
@ -1690,7 +1670,7 @@ Parser<FullParseHandler>::leaveFunction(ParseNode* fn, ParseContext<FullParseHan
}
Rooted<Bindings> bindings(context, funbox->bindings);
if (!pc->generateFunctionBindings(context, tokenStream, alloc, &bindings))
if (!pc->generateBindings(context, tokenStream, alloc, &bindings))
return false;
funbox->bindings = bindings;
@ -2716,7 +2696,7 @@ Parser<FullParseHandler>::standaloneLazyFunction(HandleFunction fun, bool strict
}
Rooted<Bindings> bindings(context, funbox->bindings);
if (!pc->generateFunctionBindings(context, tokenStream, alloc, &bindings))
if (!pc->generateBindings(context, tokenStream, alloc, &bindings))
return null();
funbox->bindings = bindings;
@ -3231,7 +3211,7 @@ Parser<FullParseHandler>::bindLexical(BindData<FullParseHandler>* data,
// script->nfixed and body-level lets.
//
// For body-level lets, the index is bogus at this point and is adjusted
// when creating Bindings. See ParseContext::generateFunctionBindings and
// when creating Bindings. See ParseContext::generateBindings and
// AppendPackedBindings.
if (!pn->pn_scopecoord.setSlot(parser->tokenStream, index))
return false;

View File

@ -212,19 +212,9 @@ struct MOZ_STACK_CLASS ParseContext : public GenericParseContext
* - Sometimes a script's bindings are accessed at runtime to retrieve the
* contents of the lexical scope (e.g., from the debugger).
*/
private:
bool generateBindings(ExclusiveContext* cx, TokenStream& ts, LifoAlloc& alloc,
MutableHandle<Bindings> bindings) const;
public:
bool generateFunctionBindings(ExclusiveContext* cx, TokenStream& ts,
LifoAlloc& alloc,
MutableHandle<Bindings> bindings) const;
bool generateModuleBindings(ExclusiveContext* cx, TokenStream& ts,
LifoAlloc& alloc,
MutableHandle<Bindings> bindings) const;
private:
ParseContext** parserPC; /* this points to the Parser's active pc
and holds either |this| or one of

View File

@ -230,6 +230,7 @@ JSObject::isQualifiedVarObj() const
MOZ_ASSERT_IF(rv,
is<js::GlobalObject>() ||
is<js::CallObject>() ||
is<js::ModuleEnvironmentObject>() ||
is<js::NonSyntacticVariablesObject>() ||
(is<js::DynamicWithObject>() && !as<js::DynamicWithObject>().isSyntactic()));
return rv;

View File

@ -75,7 +75,7 @@ Bindings::initWithTemporaryStorage(ExclusiveContext* cx, MutableHandle<Bindings>
uint32_t numArgs, uint32_t numVars,
uint32_t numBodyLevelLexicals, uint32_t numBlockScoped,
uint32_t numUnaliasedVars, uint32_t numUnaliasedBodyLevelLexicals,
const Binding* bindingArray)
const Binding* bindingArray, bool isModule /* = false */)
{
MOZ_ASSERT(!self.callObjShape());
MOZ_ASSERT(self.bindingArrayUsingTemporaryStorage());
@ -141,9 +141,10 @@ Bindings::initWithTemporaryStorage(ExclusiveContext* cx, MutableHandle<Bindings>
uint32_t nfixed = gc::GetGCKindSlots(gc::GetGCObjectKind(nslots));
// Start with the empty shape and then append one shape per aliased binding.
const Class* cls = isModule ? &ModuleEnvironmentObject::class_ : &CallObject::class_;
RootedShape shape(cx,
EmptyShape::getInitialShape(cx, &CallObject::class_, TaggedProto(nullptr),
nfixed, BaseShape::QUALIFIED_VAROBJ | BaseShape::DELEGATE));
EmptyShape::getInitialShape(cx, cls, TaggedProto(nullptr), nfixed,
BaseShape::QUALIFIED_VAROBJ | BaseShape::DELEGATE));
if (!shape)
return false;
@ -169,9 +170,7 @@ Bindings::initWithTemporaryStorage(ExclusiveContext* cx, MutableHandle<Bindings>
}
#endif
StackBaseShape stackBase(cx, &CallObject::class_,
BaseShape::QUALIFIED_VAROBJ | BaseShape::DELEGATE);
StackBaseShape stackBase(cx, cls, BaseShape::QUALIFIED_VAROBJ | BaseShape::DELEGATE);
UnownedBaseShape* base = BaseShape::getUnowned(cx, stackBase);
if (!base)
return false;

View File

@ -274,7 +274,8 @@ class Bindings : public JS::Traceable
uint32_t numBlockScoped,
uint32_t numUnaliasedVars,
uint32_t numUnaliasedBodyLevelLexicals,
const Binding* bindingArray);
const Binding* bindingArray,
bool isModule = false);
// Initialize a trivial Bindings with no slots and an empty callObjShape.
bool initTrivial(ExclusiveContext* cx);

View File

@ -4245,18 +4245,28 @@ DumpStaticScopeChain(JSContext* cx, unsigned argc, Value* vp)
return false;
}
if (!args[0].isObject() || !args[0].toObject().is<JSFunction>()) {
ReportUsageError(cx, callee, "Argument must be an interpreted function");
if (!args[0].isObject() ||
!(args[0].toObject().is<JSFunction>() || args[0].toObject().is<ModuleObject>()))
{
ReportUsageError(cx, callee, "Argument must be an interpreted function or a module");
return false;
}
RootedFunction fun(cx, &args[0].toObject().as<JSFunction>());
if (!fun->isInterpreted()) {
ReportUsageError(cx, callee, "Argument must be an interpreted function");
return false;
RootedObject obj(cx, &args[0].toObject());
RootedScript script(cx);
if (obj->is<JSFunction>()) {
RootedFunction fun(cx, &obj->as<JSFunction>());
if (!fun->isInterpreted()) {
ReportUsageError(cx, callee, "Argument must be an interpreted function");
return false;
}
script = fun->getOrCreateScript(cx);
} else {
script = obj->as<ModuleObject>().script();
}
js::DumpStaticScopeChain(fun->getOrCreateScript(cx));
js::DumpStaticScopeChain(script);
args.rval().setUndefined();
return true;
@ -4910,8 +4920,8 @@ static const JSFunctionSpecWithHelp fuzzing_unsafe_functions[] = {
#ifdef DEBUG
JS_FN_HELP("dumpStaticScopeChain", DumpStaticScopeChain, 1, 0,
"dumpStaticScopeChain(fun)",
" Prints the static scope chain of an interpreted function fun."),
"dumpStaticScopeChain(obj)",
" Prints the static scope chain of an interpreted function or a module."),
#endif
JS_FS_HELP_END

View File

@ -1259,6 +1259,7 @@ PopScope(JSContext* cx, ScopeIter& si)
case ScopeIter::With:
si.initialFrame().popWith(cx);
break;
case ScopeIter::Module:
case ScopeIter::Call:
case ScopeIter::Eval:
case ScopeIter::NonSyntactic:

View File

@ -80,6 +80,8 @@ StaticScopeIter<allowGC>::operator++(int)
obj = obj->template as<StaticEvalObject>().enclosingScopeForStaticScopeIter();
} else if (obj->template is<StaticNonSyntacticScopeObjects>()) {
obj = obj->template as<StaticNonSyntacticScopeObjects>().enclosingScopeForStaticScopeIter();
} else if (obj->template is<ModuleObject>()) {
obj = obj->template as<ModuleObject>().script()->enclosingStaticScope();
} else if (onNamedLambda || !obj->template as<JSFunction>().isNamedLambda()) {
onNamedLambda = false;
JSFunction& fun = obj->template as<JSFunction>();
@ -104,6 +106,8 @@ StaticScopeIter<allowGC>::hasSyntacticDynamicScopeObject() const
return fun.functionBox()->isHeavyweight();
return fun.isHeavyweight();
}
if (obj->template is<ModuleObject>())
return true;
if (obj->template is<StaticBlockObject>())
return obj->template as<StaticBlockObject>().needsClone();
if (obj->template is<StaticWithObject>())
@ -122,6 +126,8 @@ StaticScopeIter<allowGC>::scopeShape() const
MOZ_ASSERT(type() != NamedLambda && type() != Eval);
if (type() == Block)
return block().lastProperty();
if (type() == Module)
return moduleScript()->callObjShape();
return funScript()->callObjShape();
}
@ -131,15 +137,17 @@ StaticScopeIter<allowGC>::type() const
{
if (onNamedLambda)
return NamedLambda;
return obj->template is<StaticBlockObject>()
? Block
: (obj->template is<StaticWithObject>()
? With
: (obj->template is<StaticEvalObject>()
? Eval
: (obj->template is<StaticNonSyntacticScopeObjects>())
? NonSyntactic
: Function));
if (obj->template is<StaticBlockObject>())
return Block;
if (obj->template is<StaticWithObject>())
return With;
if (obj->template is<StaticEvalObject>())
return Eval;
if (obj->template is<StaticNonSyntacticScopeObjects>())
return NonSyntactic;
if (obj->template is<ModuleObject>())
return Module;
return Function;
}
template <AllowGC allowGC>
@ -200,6 +208,22 @@ StaticScopeIter<allowGC>::maybeFunctionBox() const
return nullptr;
}
template <AllowGC allowGC>
inline JSScript*
StaticScopeIter<allowGC>::moduleScript() const
{
MOZ_ASSERT(type() == Module);
return obj->template as<ModuleObject>().script();
}
template <AllowGC allowGC>
inline ModuleObject&
StaticScopeIter<allowGC>::module() const
{
MOZ_ASSERT(type() == Module);
return obj->template as<ModuleObject>();
}
} /* namespace js */
inline JSObject*

View File

@ -191,8 +191,8 @@ CallObject::createTemplateObject(JSContext* cx, HandleScript script, gc::Initial
if (!obj)
return nullptr;
// Set uninitialized lexicals even on template objects, as Ion will use
// copy over the template object's slot values in the fast path.
// Set uninitialized lexicals even on template objects, as Ion will copy
// over the template object's slot values in the fast path.
obj->as<CallObject>().initAliasedLexicalsToThrowOnTouch(script);
return &obj->as<CallObject>();
@ -311,6 +311,59 @@ const Class CallObject::class_ = {
JSCLASS_IS_ANONYMOUS | JSCLASS_HAS_RESERVED_SLOTS(CallObject::RESERVED_SLOTS)
};
/*****************************************************************************/
const Class ModuleEnvironmentObject::class_ = {
"ModuleEnvironmentObject",
JSCLASS_IMPLEMENTS_BARRIERS |
JSCLASS_HAS_RESERVED_SLOTS(ModuleEnvironmentObject::RESERVED_SLOTS) |
JSCLASS_IS_ANONYMOUS
};
/* static */ ModuleEnvironmentObject*
ModuleEnvironmentObject::create(ExclusiveContext* cx, HandleModuleObject module)
{
RootedScript script(cx, module->script());
RootedShape shape(cx, script->bindings.callObjShape());
MOZ_ASSERT(shape->getObjectClass() == &class_);
RootedObjectGroup group(cx, ObjectGroup::defaultNewGroup(cx, &class_, TaggedProto(nullptr)));
if (!group)
return nullptr;
gc::AllocKind kind = gc::GetGCObjectKind(shape->numFixedSlots());
MOZ_ASSERT(CanBeFinalizedInBackground(kind, &class_));
kind = gc::GetBackgroundAllocKind(kind);
JSObject* obj = JSObject::create(cx, kind, TenuredHeap, shape, group);
if (!obj)
return nullptr;
Rooted<ModuleEnvironmentObject*> scope(cx, &obj->as<ModuleEnvironmentObject>());
// Set uninitialized lexicals even on template objects, as Ion will use
// copy over the template object's slot values in the fast path.
scope->initAliasedLexicalsToThrowOnTouch(script);
scope->initFixedSlot(MODULE_SLOT, ObjectValue(*module));
if (!JSObject::setSingleton(cx, scope))
return nullptr;
// Initialize this early so that we can manipulate the scope object without
// causing assertions.
scope->setEnclosingScope(cx->global());
return scope;
}
ModuleObject&
ModuleEnvironmentObject::module() const
{
return getReservedSlot(MODULE_SLOT).toObject().as<ModuleObject>();
}
/*****************************************************************************/
const Class DeclEnvObject::class_ = {
js_Object_str,
JSCLASS_HAS_RESERVED_SLOTS(DeclEnvObject::RESERVED_SLOTS) |
@ -1075,6 +1128,9 @@ ScopeIter::settle()
#ifdef DEBUG
if (!ssi_.done() && hasAnyScopeObject()) {
switch (ssi_.type()) {
case StaticScopeIter<CanGC>::Module:
MOZ_ASSERT(scope_->as<ModuleEnvironmentObject>().module() == ssi_.module());
break;
case StaticScopeIter<CanGC>::Function:
MOZ_ASSERT(scope_->as<CallObject>().callee().nonLazyScript() == ssi_.funScript());
break;
@ -1118,6 +1174,8 @@ ScopeIter::type() const
MOZ_ASSERT(!done());
switch (ssi_.type()) {
case StaticScopeIter<CanGC>::Module:
return Module;
case StaticScopeIter<CanGC>::Function:
return Call;
case StaticScopeIter<CanGC>::Block:
@ -1151,6 +1209,8 @@ ScopeIter::maybeStaticScope() const
switch (ssi_.type()) {
case StaticScopeIter<CanGC>::Function:
return &fun();
case StaticScopeIter<CanGC>::Module:
return &module();
case StaticScopeIter<CanGC>::Block:
return &staticBlock();
case StaticScopeIter<CanGC>::With:
@ -2387,6 +2447,10 @@ GetDebugScopeForMissing(JSContext* cx, const ScopeIter& si)
*/
DebugScopeObject* debugScope = nullptr;
switch (si.type()) {
case ScopeIter::Module:
MOZ_CRASH(); // TODO: Implement debug scopes for modules.
break;
case ScopeIter::Call: {
RootedFunction callee(cx, &si.fun());
// Generators should always reify their scopes.
@ -2592,6 +2656,9 @@ js::DumpStaticScopeChain(JSObject* staticScope)
{
for (StaticScopeIter<NoGC> ssi(staticScope); !ssi.done(); ssi++) {
switch (ssi.type()) {
case StaticScopeIter<NoGC>::Module:
fprintf(stdout, "module [%p]", &ssi.module());
break;
case StaticScopeIter<NoGC>::Function:
if (ssi.fun().isBeingParsed())
fprintf(stdout, "funbox [%p fun=%p]", ssi.maybeFunctionBox(), &ssi.fun());

View File

@ -130,7 +130,7 @@ class StaticScopeIter
bool hasSyntacticDynamicScopeObject() const;
Shape* scopeShape() const;
enum Type { Function, Block, With, NamedLambda, Eval, NonSyntactic };
enum Type { Module, Function, Block, With, NamedLambda, Eval, NonSyntactic };
Type type() const;
StaticBlockObject& block() const;
@ -140,6 +140,8 @@ class StaticScopeIter
JSScript* funScript() const;
JSFunction& fun() const;
frontend::FunctionBox* maybeFunctionBox() const;
JSScript* moduleScript() const;
ModuleObject& module() const;
};
/*****************************************************************************/
@ -218,6 +220,8 @@ ScopeCoordinateFunctionScript(JSScript* script, jsbytecode* pc);
* | | DeclEnvObject Holds name of recursive/heavyweight named lambda
* | |
* | CallObject Scope of entire function or strict eval
* | |
* | ModuleEnvironmentObject Module top-level scope on run-time scope chain
* |
* NestedScopeObject Statement scopes; don't cross script boundaries
* | | |
@ -286,6 +290,7 @@ class ScopeObject : public NativeObject
class CallObject : public ScopeObject
{
protected:
static const uint32_t CALLEE_SLOT = 1;
static CallObject*
@ -371,6 +376,21 @@ class CallObject : public ScopeObject
}
};
class ModuleEnvironmentObject : public CallObject
{
static const uint32_t MODULE_SLOT = CallObject::CALLEE_SLOT;
public:
static const Class class_;
static ModuleEnvironmentObject* create(ExclusiveContext* cx, HandleModuleObject module);
ModuleObject& module() const;
};
typedef Rooted<ModuleEnvironmentObject*> RootedModuleEnvironmentObject;
typedef Handle<ModuleEnvironmentObject*> HandleModuleEnvironmentObject;
typedef MutableHandle<ModuleEnvironmentObject*> MutableHandleModuleEnvironmentObject;
class DeclEnvObject : public ScopeObject
{
// Pre-allocated slot for the named lambda.
@ -830,7 +850,7 @@ class ScopeIter
inline JSObject& enclosingScope() const;
// If !done():
enum Type { Call, Block, With, Eval, NonSyntactic };
enum Type { Module, Call, Block, With, Eval, NonSyntactic };
Type type() const;
inline bool hasNonSyntacticScopeObject() const;
@ -845,6 +865,7 @@ class ScopeIter
StaticEvalObject& staticEval() const { return ssi_.eval(); }
StaticNonSyntacticScopeObjects& staticNonSyntactic() const { return ssi_.nonSyntactic(); }
JSFunction& fun() const { return ssi_.fun(); }
ModuleObject& module() const { return ssi_.module(); }
bool withinInitialFrame() const { return !!frame_; }
AbstractFramePtr initialFrame() const { MOZ_ASSERT(withinInitialFrame()); return frame_; }
@ -1076,6 +1097,14 @@ JSObject::is<js::NestedScopeObject>() const
is<js::DynamicWithObject>();
}
template<>
inline bool
JSObject::is<js::CallObject>() const
{
return getClass() == &js::CallObject::class_ ||
is<js::ModuleEnvironmentObject>();
}
template<>
inline bool
JSObject::is<js::ScopeObject>() const
@ -1174,15 +1203,16 @@ ScopeIter::canHaveSyntacticScopeObject() const
return false;
switch (type()) {
case Module:
case Call:
return true;
case Block:
return true;
case With:
return true;
case Eval:
// Only strict eval scopes can have dynamic scope objects.
return staticEval().isStrict();
case NonSyntactic:
return false;
}

View File

@ -149,6 +149,10 @@ AssertDynamicScopeMatchesStaticScope(JSContext* cx, JSScript* script, JSObject*
}
} else if (i.hasSyntacticDynamicScopeObject()) {
switch (i.type()) {
case StaticScopeIter<NoGC>::Module:
MOZ_ASSERT(scope->as<ModuleEnvironmentObject>().module().script() == i.moduleScript());
scope = &scope->as<ModuleEnvironmentObject>().enclosingScope();
break;
case StaticScopeIter<NoGC>::Function:
MOZ_ASSERT(scope->as<CallObject>().callee().nonLazyScript() == i.funScript());
scope = &scope->as<CallObject>().enclosingScope();