--- a/js/src/frontend/BytecodeEmitter.cpp
+++ b/js/src/frontend/BytecodeEmitter.cpp
@@ -731,16 +731,19 @@ BytecodeEmitter::EmitterScope::searchInE
return NameLocation::Dynamic();
case ScopeKind::Global:
return NameLocation::Global(BindingKind::Var);
case ScopeKind::With:
case ScopeKind::NonSyntactic:
return NameLocation::Dynamic();
+
+ case ScopeKind::WasmFunction:
+ MOZ_CRASH("No direct eval inside wasm functions");
}
if (hasEnv) {
MOZ_ASSERT(hops < ENVCOORD_HOPS_LIMIT - 1);
hops++;
}
}
@@ -1428,16 +1431,19 @@ BytecodeEmitter::EmitterScope::leave(Byt
case ScopeKind::NamedLambda:
case ScopeKind::StrictNamedLambda:
case ScopeKind::Eval:
case ScopeKind::StrictEval:
case ScopeKind::Global:
case ScopeKind::NonSyntactic:
case ScopeKind::Module:
break;
+
+ case ScopeKind::WasmFunction:
+ MOZ_CRASH("No wasm function scopes in JS");
}
// Finish up the scope if we are leaving it in LIFO fashion.
if (!nonLocal) {
// Popping scopes due to non-local jumps generate additional scope
// notes. See NonLocalExitControl::prepareForNonLocalJump.
if (ScopeKindIsInBody(kind)) {
// The extra function var scope is never popped once it's pushed,
--- a/js/src/gc/Marking.cpp
+++ b/js/src/gc/Marking.cpp
@@ -1259,16 +1259,22 @@ EvalScope::Data::trace(JSTracer* trc)
}
void
ModuleScope::Data::trace(JSTracer* trc)
{
TraceNullableEdge(trc, &module, "scope module");
TraceBindingNames(trc, names, length);
}
void
+WasmFunctionScope::Data::trace(JSTracer* trc)
+{
+ TraceNullableEdge(trc, &instance, "wasm function");
+ TraceBindingNames(trc, names, length);
+}
+void
Scope::traceChildren(JSTracer* trc)
{
TraceNullableEdge(trc, &enclosing_, "scope enclosing");
TraceNullableEdge(trc, &environmentShape_, "scope env shape");
switch (kind_) {
case ScopeKind::Function:
reinterpret_cast<FunctionScope::Data*>(data_)->trace(trc);
break;
@@ -1291,16 +1297,19 @@ Scope::traceChildren(JSTracer* trc)
case ScopeKind::StrictEval:
reinterpret_cast<EvalScope::Data*>(data_)->trace(trc);
break;
case ScopeKind::Module:
reinterpret_cast<ModuleScope::Data*>(data_)->trace(trc);
break;
case ScopeKind::With:
break;
+ case ScopeKind::WasmFunction:
+ reinterpret_cast<WasmFunctionScope::Data*>(data_)->trace(trc);
+ break;
}
}
inline void
js::GCMarker::eagerlyMarkChildren(Scope* scope)
{
if (scope->enclosing_)
traverseEdge(scope, static_cast<Scope*>(scope->enclosing_));
if (scope->environmentShape_)
@@ -1356,16 +1365,24 @@ js::GCMarker::eagerlyMarkChildren(Scope*
traverseEdge(scope, static_cast<JSObject*>(data->module));
names = data->names;
length = data->length;
break;
}
case ScopeKind::With:
break;
+
+ case ScopeKind::WasmFunction: {
+ WasmFunctionScope::Data* data = reinterpret_cast<WasmFunctionScope::Data*>(scope->data_);
+ traverseEdge(scope, static_cast<JSObject*>(data->instance));
+ names = data->names;
+ length = data->length;
+ break;
+ }
}
if (scope->kind_ == ScopeKind::Function) {
for (uint32_t i = 0; i < length; i++) {
if (JSAtom* name = names[i].name())
traverseEdge(scope, static_cast<JSString*>(name));
}
} else {
for (uint32_t i = 0; i < length; i++)
--- a/js/src/gc/Policy.h
+++ b/js/src/gc/Policy.h
@@ -47,16 +47,17 @@ class RegExpObject;
class SavedFrame;
class Scope;
class EnvironmentObject;
class ScriptSourceObject;
class Shape;
class SharedArrayBufferObject;
class StructTypeDescr;
class UnownedBaseShape;
+class WasmFunctionScope;
class WasmMemoryObject;
namespace jit {
class JitCode;
} // namespace jit
} // namespace js
// Expand the given macro D for each valid GC reference type.
#define FOR_EACH_INTERNAL_GC_POINTER_TYPE(D) \
@@ -87,16 +88,17 @@ class JitCode;
D(js::RegExpObject*) \
D(js::SavedFrame*) \
D(js::Scope*) \
D(js::ScriptSourceObject*) \
D(js::Shape*) \
D(js::SharedArrayBufferObject*) \
D(js::StructTypeDescr*) \
D(js::UnownedBaseShape*) \
+ D(js::WasmFunctionScope*) \
D(js::WasmInstanceObject*) \
D(js::WasmMemoryObject*) \
D(js::WasmTableObject*) \
D(js::jit::JitCode*)
// Expand the given macro D for each internal tagged GC pointer type.
#define FOR_EACH_INTERNAL_TAGGED_GC_POINTER_TYPE(D) \
D(js::TaggedProto)
--- a/js/src/jsscript.cpp
+++ b/js/src/jsscript.cpp
@@ -747,16 +747,19 @@ js::XDRScript(XDRState<mode>* xdr, Handl
case ScopeKind::Global:
case ScopeKind::NonSyntactic:
if (!GlobalScope::XDR(xdr, scopeKind, &scope))
return false;
break;
case ScopeKind::Module:
MOZ_CRASH("NYI");
break;
+ case ScopeKind::WasmFunction:
+ MOZ_CRASH("wasm functions cannot be nested in JSScripts");
+ break;
}
if (mode == XDR_DECODE)
vector[i].init(scope);
}
}
/*
--- a/js/src/vm/EnvironmentObject.cpp
+++ b/js/src/vm/EnvironmentObject.cpp
@@ -623,16 +623,47 @@ ModuleEnvironmentObject::enumerate(JSCon
properties.infallibleAppend(r.front().propid());
MOZ_ASSERT(properties.length() == count);
return true;
}
/*****************************************************************************/
+const Class WasmFunctionCallObject::class_ = {
+ "WasmCall",
+ JSCLASS_IS_ANONYMOUS | JSCLASS_HAS_RESERVED_SLOTS(WasmFunctionCallObject::RESERVED_SLOTS)
+};
+
+/* static */ WasmFunctionCallObject*
+WasmFunctionCallObject::createHollowForDebug(JSContext* cx, WasmFunctionScope* scope)
+{
+ RootedObjectGroup group(cx, ObjectGroup::defaultNewGroup(cx, &class_, TaggedProto(nullptr)));
+ if (!group)
+ return nullptr;
+
+ RootedShape shape(cx, scope->getEmptyEnvironmentShape(cx));
+ if (!shape)
+ return nullptr;
+
+ gc::AllocKind kind = gc::GetGCObjectKind(shape->numFixedSlots());
+ MOZ_ASSERT(CanBeFinalizedInBackground(kind, &class_));
+ kind = gc::GetBackgroundAllocKind(kind);
+
+ JSObject* obj;
+ JS_TRY_VAR_OR_RETURN_NULL(cx, obj, JSObject::create(cx, kind, gc::DefaultHeap, shape, group));
+
+ Rooted<WasmFunctionCallObject*> callobj(cx, &obj->as<WasmFunctionCallObject>());
+ callobj->initEnclosingEnvironment(&cx->global()->lexicalEnvironment());
+
+ return callobj;
+}
+
+/*****************************************************************************/
+
WithEnvironmentObject*
WithEnvironmentObject::create(JSContext* cx, HandleObject object, HandleObject enclosing,
Handle<WithScope*> scope)
{
Rooted<WithEnvironmentObject*> obj(cx);
obj = NewObjectWithNullTaggedProto<WithEnvironmentObject>(cx, GenericObject,
BaseShape::DELEGATE);
if (!obj)
@@ -2221,16 +2252,17 @@ DebugEnvironmentProxy::initSnapshot(Arra
bool
DebugEnvironmentProxy::isForDeclarative() const
{
EnvironmentObject& e = environment();
return e.is<CallObject>() ||
e.is<VarEnvironmentObject>() ||
e.is<ModuleEnvironmentObject>() ||
+ e.is<WasmFunctionCallObject>() ||
e.is<LexicalEnvironmentObject>();
}
bool
DebugEnvironmentProxy::getMaybeSentinelValue(JSContext* cx, HandleId id, MutableHandleValue vp)
{
Rooted<DebugEnvironmentProxy*> self(cx, this);
return DebugEnvironmentProxyHandler::singleton.getMaybeSentinelValue(cx, self, id, vp);
@@ -2849,16 +2881,17 @@ GetDebugEnvironmentForEnvironmentObject(
}
static DebugEnvironmentProxy*
GetDebugEnvironmentForMissing(JSContext* cx, const EnvironmentIter& ei)
{
MOZ_ASSERT(!ei.hasSyntacticEnvironment() &&
(ei.scope().is<FunctionScope>() ||
ei.scope().is<LexicalScope>() ||
+ ei.scope().is<WasmFunctionScope>() ||
ei.scope().is<VarScope>()));
if (DebugEnvironmentProxy* debugEnv = DebugEnvironments::hasDebugEnvironment(cx, ei))
return debugEnv;
EnvironmentIter copy(cx, ei);
RootedObject enclosingDebug(cx, GetDebugEnvironment(cx, ++copy));
if (!enclosingDebug)
@@ -2891,16 +2924,23 @@ GetDebugEnvironmentForMissing(JSContext*
} else if (ei.scope().is<LexicalScope>()) {
Rooted<LexicalScope*> lexicalScope(cx, &ei.scope().as<LexicalScope>());
Rooted<LexicalEnvironmentObject*> env(cx,
LexicalEnvironmentObject::createHollowForDebug(cx, lexicalScope));
if (!env)
return nullptr;
debugEnv = DebugEnvironmentProxy::create(cx, *env, enclosingDebug);
+ } else if (ei.scope().is<WasmFunctionScope>()) {
+ Rooted<WasmFunctionScope*> wasmFunctionScope(cx, &ei.scope().as<WasmFunctionScope>());
+ Rooted<WasmFunctionCallObject*> callobj(cx, WasmFunctionCallObject::createHollowForDebug(cx, wasmFunctionScope));
+ if (!callobj)
+ return nullptr;
+
+ debugEnv = DebugEnvironmentProxy::create(cx, *callobj, enclosingDebug);
} else {
Rooted<VarScope*> varScope(cx, &ei.scope().as<VarScope>());
Rooted<VarEnvironmentObject*> env(cx,
VarEnvironmentObject::createHollowForDebug(cx, varScope));
if (!env)
return nullptr;
debugEnv = DebugEnvironmentProxy::create(cx, *env, enclosingDebug);
@@ -2935,16 +2975,17 @@ GetDebugEnvironment(JSContext* cx, const
if (ei.done())
return GetDebugEnvironmentForNonEnvironmentObject(ei);
if (ei.hasAnyEnvironmentObject())
return GetDebugEnvironmentForEnvironmentObject(cx, ei);
if (ei.scope().is<FunctionScope>() ||
ei.scope().is<LexicalScope>() ||
+ ei.scope().is<WasmFunctionScope>() ||
ei.scope().is<VarScope>())
{
return GetDebugEnvironmentForMissing(cx, ei);
}
EnvironmentIter copy(cx, ei);
return GetDebugEnvironment(cx, ++copy);
}
--- a/js/src/vm/EnvironmentObject.h
+++ b/js/src/vm/EnvironmentObject.h
@@ -421,16 +421,27 @@ class ModuleEnvironmentObject : public E
static bool enumerate(JSContext* cx, HandleObject obj, AutoIdVector& properties,
bool enumerableOnly);
};
typedef Rooted<ModuleEnvironmentObject*> RootedModuleEnvironmentObject;
typedef Handle<ModuleEnvironmentObject*> HandleModuleEnvironmentObject;
typedef MutableHandle<ModuleEnvironmentObject*> MutableHandleModuleEnvironmentObject;
+class WasmFunctionCallObject : public EnvironmentObject
+{
+ public:
+ static const Class class_;
+
+ static const uint32_t RESERVED_SLOTS = 1;
+
+ static WasmFunctionCallObject* createHollowForDebug(JSContext* cx,
+ WasmFunctionScope* scope);
+};
+
class LexicalEnvironmentObject : public EnvironmentObject
{
// Global and non-syntactic lexical environments need to store a 'this'
// value and all other lexical environments have a fixed shape and store a
// backpointer to the LexicalScope.
//
// Since the two sets are disjoint, we only use one slot to save space.
static const unsigned THIS_VALUE_OR_SCOPE_SLOT = 1;
@@ -979,16 +990,17 @@ class DebugEnvironments
template <>
inline bool
JSObject::is<js::EnvironmentObject>() const
{
return is<js::CallObject>() ||
is<js::VarEnvironmentObject>() ||
is<js::ModuleEnvironmentObject>() ||
+ is<js::WasmFunctionCallObject>() ||
is<js::LexicalEnvironmentObject>() ||
is<js::WithEnvironmentObject>() ||
is<js::NonSyntacticVariablesObject>() ||
is<js::RuntimeLexicalErrorObject>();
}
template<>
bool
--- a/js/src/vm/Interpreter.cpp
+++ b/js/src/vm/Interpreter.cpp
@@ -1015,16 +1015,19 @@ PopEnvironment(JSContext* cx, Environmen
if (ei.scope().hasEnvironment())
ei.initialFrame().popOffEnvironmentChain<VarEnvironmentObject>();
break;
case ScopeKind::Eval:
case ScopeKind::Global:
case ScopeKind::NonSyntactic:
case ScopeKind::Module:
break;
+ case ScopeKind::WasmFunction:
+ MOZ_CRASH("wasm is not interpreted");
+ break;
}
}
// Unwind environment chain and iterator to match the env corresponding to
// the given bytecode position.
void
js::UnwindEnvironment(JSContext* cx, EnvironmentIter& ei, jsbytecode* pc)
{
--- a/js/src/vm/Scope.cpp
+++ b/js/src/vm/Scope.cpp
@@ -70,16 +70,18 @@ js::ScopeKindString(ScopeKind kind)
case ScopeKind::StrictEval:
return "strict eval";
case ScopeKind::Global:
return "global";
case ScopeKind::NonSyntactic:
return "non-syntactic";
case ScopeKind::Module:
return "module";
+ case ScopeKind::WasmFunction:
+ return "wasm function";
}
MOZ_CRASH("Bad ScopeKind");
}
static Shape*
EmptyEnvironmentShape(ExclusiveContext* cx, const Class* cls, uint32_t numSlots,
uint32_t baseShapeFlags)
{
@@ -374,19 +376,24 @@ Scope::clone(JSContext* cx, HandleScope
return create(cx, scope->kind_, enclosing, envShape, Move(dataClone));
}
case ScopeKind::Global:
case ScopeKind::NonSyntactic:
MOZ_CRASH("Use GlobalScope::clone.");
break;
+ case ScopeKind::WasmFunction:
+ MOZ_CRASH("wasm functions are not nested in JSScript");
+ break;
+
case ScopeKind::Module:
MOZ_CRASH("NYI");
break;
+
}
return nullptr;
}
void
Scope::finalize(FreeOp* fop)
{
@@ -458,16 +465,19 @@ LexicalScope::nextFrameSlot(Scope* scope
case ScopeKind::Eval:
case ScopeKind::StrictEval:
return si.scope()->as<EvalScope>().nextFrameSlot();
case ScopeKind::Global:
case ScopeKind::NonSyntactic:
return 0;
case ScopeKind::Module:
return si.scope()->as<ModuleScope>().nextFrameSlot();
+ case ScopeKind::WasmFunction:
+ // TODO return si.scope()->as<WasmFunctionScope>().nextFrameSlot();
+ return 0;
}
}
MOZ_CRASH("Not an enclosing intra-frame Scope");
}
/* static */ LexicalScope*
LexicalScope::create(ExclusiveContext* cx, ScopeKind kind, Handle<Data*> data,
uint32_t firstFrameSlot, HandleScope enclosing)
@@ -1137,16 +1147,58 @@ ModuleScope::getEmptyEnvironmentShape(Ex
}
JSScript*
ModuleScope::script() const
{
return module()->script();
}
+// TODO Check what Debugger behavior should be when it evaluates a
+// var declaration.
+static const uint32_t WasmFunctionEnvShapeFlags =
+ BaseShape::NOT_EXTENSIBLE | BaseShape::DELEGATE;
+
+/* static */ WasmFunctionScope*
+WasmFunctionScope::create(JSContext* cx, WasmInstanceObject* instance, uint32_t funcIndex)
+{
+ // WasmFunctionScope::Data has GCManagedDeletePolicy because it contains a
+ // GCPtr. Destruction of |data| below may trigger calls into the GC.
+ Rooted<WasmFunctionScope*> wasmFunctionScope(cx);
+
+ {
+ // TODO pull the local variable names from the wasm function definition.
+
+ Rooted<UniquePtr<Data>> data(cx, NewEmptyScopeData<WasmFunctionScope>(cx));
+ if (!data)
+ return nullptr;
+
+ Rooted<Scope*> enclosingScope(cx, &cx->global()->emptyGlobalScope());
+
+ data->instance.init(instance);
+ data->funcIndex = funcIndex;
+
+ Scope* scope = Scope::create(cx, ScopeKind::WasmFunction, enclosingScope, /* envShape = */ nullptr);
+ if (!scope)
+ return nullptr;
+
+ wasmFunctionScope = &scope->as<WasmFunctionScope>();
+ wasmFunctionScope->initData(Move(data.get()));
+ }
+
+ return wasmFunctionScope;
+}
+
+/* static */ Shape*
+WasmFunctionScope::getEmptyEnvironmentShape(ExclusiveContext* cx)
+{
+ const Class* cls = &WasmFunctionCallObject::class_;
+ return EmptyEnvironmentShape(cx, cls, JSSLOT_FREE(cls), WasmFunctionEnvShapeFlags);
+}
+
ScopeIter::ScopeIter(JSScript* script)
: scope_(script->bodyScope())
{ }
bool
ScopeIter::hasSyntacticEnvironment() const
{
return scope()->hasEnvironment() && scope()->kind() != ScopeKind::NonSyntactic;
@@ -1188,16 +1240,19 @@ BindingIter::BindingIter(Scope* scope)
break;
case ScopeKind::Global:
case ScopeKind::NonSyntactic:
init(scope->as<GlobalScope>().data());
break;
case ScopeKind::Module:
init(scope->as<ModuleScope>().data());
break;
+ case ScopeKind::WasmFunction:
+ init(scope->as<WasmFunctionScope>().data());
+ break;
}
}
BindingIter::BindingIter(JSScript* script)
: BindingIter(script->bodyScope())
{ }
void
@@ -1318,16 +1373,32 @@ BindingIter::init(ModuleScope::Data& dat
// lets - [data.letStart, data.constStart)
// consts - [data.constStart, data.length)
init(data.varStart, data.varStart, data.varStart, data.varStart, data.letStart, data.constStart,
CanHaveFrameSlots | CanHaveEnvironmentSlots,
0, JSSLOT_FREE(&ModuleEnvironmentObject::class_),
data.names, data.length);
}
+void
+BindingIter::init(WasmFunctionScope::Data& data)
+{
+ // imports - [0, 0)
+ // positional formals - [0, 0)
+ // other formals - [0, 0)
+ // top-level funcs - [0, 0)
+ // vars - [0, 0)
+ // lets - [0, 0)
+ // consts - [0, 0)
+ init(0, 0, 0, 0, 0, 0,
+ CanHaveFrameSlots | CanHaveEnvironmentSlots,
+ UINT32_MAX, UINT32_MAX,
+ data.names, data.length);
+}
+
PositionalFormalParameterIter::PositionalFormalParameterIter(JSScript* script)
: BindingIter(script)
{
// Reinit with flags = 0, i.e., iterate over all positional parameters.
if (script->bodyScope()->is<FunctionScope>())
init(script->bodyScope()->as<FunctionScope>().data(), /* flags = */ 0);
settle();
}
--- a/js/src/vm/Scope.h
+++ b/js/src/vm/Scope.h
@@ -65,17 +65,20 @@ enum class ScopeKind : uint8_t
Eval,
StrictEval,
// GlobalScope
Global,
NonSyntactic,
// ModuleScope
- Module
+ Module,
+
+ // WasmFunctionScope
+ WasmFunction
};
static inline bool
ScopeKindIsCatch(ScopeKind kind)
{
return kind == ScopeKind::SimpleCatch || kind == ScopeKind::Catch;
}
@@ -872,16 +875,67 @@ class ModuleScope : public Scope
return data().module;
}
JSScript* script() const;
static Shape* getEmptyEnvironmentShape(ExclusiveContext* cx);
};
+// Scope corresponding to the wasm function. A WasmFunctionScope is used by
+// Debugger only, and not for wasm execution.
+//
+class WasmFunctionScope : public Scope
+{
+ friend class BindingIter;
+ friend class Scope;
+ static const ScopeKind classScopeKind_ = ScopeKind::WasmFunction;
+
+ public:
+ struct Data
+ {
+ uint32_t length;
+ uint32_t nextFrameSlot;
+ uint32_t funcIndex;
+
+ // The wasm instance of the scope.
+ GCPtr<WasmInstanceObject*> instance;
+
+ BindingName names[1];
+
+ void trace(JSTracer* trc);
+ };
+
+ static WasmFunctionScope* create(JSContext* cx, WasmInstanceObject* instance, uint32_t funcIndex);
+
+ static size_t sizeOfData(uint32_t length) {
+ return sizeof(Data) + (length ? length - 1 : 0) * sizeof(BindingName);
+ }
+
+ private:
+ Data& data() {
+ return *reinterpret_cast<Data*>(data_);
+ }
+
+ const Data& data() const {
+ return *reinterpret_cast<Data*>(data_);
+ }
+
+ public:
+ WasmInstanceObject* instance() const {
+ return data().instance;
+ }
+
+ uint32_t funcIndex() const {
+ return data().funcIndex;
+ }
+
+ static Shape* getEmptyEnvironmentShape(ExclusiveContext* cx);
+};
+
//
// An iterator for a Scope's bindings. This is the source of truth for frame
// and environment object layout.
//
// It may be placed in GC containers; for example:
//
// for (Rooted<BindingIter> bi(cx, BindingIter(scope)); bi; bi++) {
// use(bi);
@@ -981,16 +1035,17 @@ class BindingIter
}
void init(LexicalScope::Data& data, uint32_t firstFrameSlot, uint8_t flags);
void init(FunctionScope::Data& data, uint8_t flags);
void init(VarScope::Data& data, uint32_t firstFrameSlot);
void init(GlobalScope::Data& data);
void init(EvalScope::Data& data, bool strict);
void init(ModuleScope::Data& data);
+ void init(WasmFunctionScope::Data& data);
bool hasFormalParameterExprs() const {
return flags_ & HasFormalParameterExprs;
}
bool ignoreDestructuredFormalParameters() const {
return flags_ & IgnoreDestructuredFormalParameters;
}
@@ -1053,16 +1108,20 @@ class BindingIter
explicit BindingIter(GlobalScope::Data& data) {
init(data);
}
explicit BindingIter(ModuleScope::Data& data) {
init(data);
}
+ explicit BindingIter(WasmFunctionScope::Data& data) {
+ init(data);
+ }
+
BindingIter(EvalScope::Data& data, bool strict) {
init(data, strict);
}
explicit BindingIter(const BindingIter& bi) = default;
bool done() const {
return index_ == length_;
@@ -1409,16 +1468,17 @@ struct ScopeDataGCPolicy
{ }
DEFINE_SCOPE_DATA_GCPOLICY(js::LexicalScope::Data);
DEFINE_SCOPE_DATA_GCPOLICY(js::FunctionScope::Data);
DEFINE_SCOPE_DATA_GCPOLICY(js::VarScope::Data);
DEFINE_SCOPE_DATA_GCPOLICY(js::GlobalScope::Data);
DEFINE_SCOPE_DATA_GCPOLICY(js::EvalScope::Data);
DEFINE_SCOPE_DATA_GCPOLICY(js::ModuleScope::Data);
+DEFINE_SCOPE_DATA_GCPOLICY(js::WasmFunctionScope::Data);
#undef DEFINE_SCOPE_DATA_GCPOLICY
namespace ubi {
template <>
class Concrete<js::Scope> : TracerConcrete<js::Scope>
{
--- a/js/src/vm/Stack.cpp
+++ b/js/src/vm/Stack.cpp
@@ -151,16 +151,20 @@ AssertScopeMatchesEnvironment(Scope* sco
MOZ_CRASH("NonSyntactic should not have a syntactic environment");
break;
case ScopeKind::Module:
MOZ_ASSERT(env->as<ModuleEnvironmentObject>().module().script() ==
si.scope()->as<ModuleScope>().script());
env = &env->as<ModuleEnvironmentObject>().enclosingEnvironment();
break;
+
+ case ScopeKind::WasmFunction:
+ env = &env->as<WasmFunctionCallObject>().enclosingEnvironment();
+ break;
}
}
}
// In the case of a non-syntactic env chain, the immediate parent of the
// outermost non-syntactic env may be the global lexical env, or, if
// called from Debugger, a DebugEnvironmentProxy.
//
--- a/js/src/wasm/WasmJS.cpp
+++ b/js/src/wasm/WasmJS.cpp
@@ -903,16 +903,17 @@ WasmInstanceObject::isNewborn() const
MOZ_ASSERT(is<WasmInstanceObject>());
return getReservedSlot(INSTANCE_SLOT).isUndefined();
}
/* static */ void
WasmInstanceObject::finalize(FreeOp* fop, JSObject* obj)
{
fop->delete_(&obj->as<WasmInstanceObject>().exports());
+ fop->delete_(&obj->as<WasmInstanceObject>().scopes());
if (!obj->as<WasmInstanceObject>().isNewborn())
fop->delete_(&obj->as<WasmInstanceObject>().instance());
}
/* static */ void
WasmInstanceObject::trace(JSTracer* trc, JSObject* obj)
{
WasmInstanceObject& instanceObj = obj->as<WasmInstanceObject>();
@@ -931,22 +932,29 @@ WasmInstanceObject::create(JSContext* cx
HandleObject proto)
{
UniquePtr<ExportMap> exports = js::MakeUnique<ExportMap>();
if (!exports || !exports->init()) {
ReportOutOfMemory(cx);
return nullptr;
}
+ UniquePtr<WeakScopeMap> scopes = js::MakeUnique<WeakScopeMap>(cx->zone(), ScopeMap());
+ if (!scopes || !scopes->init()) {
+ ReportOutOfMemory(cx);
+ return nullptr;
+ }
+
AutoSetNewObjectMetadata metadata(cx);
RootedWasmInstanceObject obj(cx, NewObjectWithGivenProto<WasmInstanceObject>(cx, proto));
if (!obj)
return nullptr;
obj->setReservedSlot(EXPORTS_SLOT, PrivateValue(exports.release()));
+ obj->setReservedSlot(SCOPES_SLOT, PrivateValue(scopes.release()));
MOZ_ASSERT(obj->isNewborn());
MOZ_ASSERT(obj->isTenured(), "assumed by WasmTableObject write barriers");
// Root the Instance via WasmInstanceObject before any possible GC.
auto* instance = cx->new_<Instance>(cx,
obj,
Move(code),
@@ -1030,16 +1038,22 @@ WasmInstanceObject::instance() const
}
WasmInstanceObject::ExportMap&
WasmInstanceObject::exports() const
{
return *(ExportMap*)getReservedSlot(EXPORTS_SLOT).toPrivate();
}
+WasmInstanceObject::WeakScopeMap&
+WasmInstanceObject::scopes() const
+{
+ return *(WeakScopeMap*)getReservedSlot(SCOPES_SLOT).toPrivate();
+}
+
static bool
WasmCall(JSContext* cx, unsigned argc, Value* vp)
{
CallArgs args = CallArgsFromVp(argc, vp);
RootedFunction callee(cx, &args.callee().as<JSFunction>());
Instance& instance = ExportedFunctionToInstance(callee);
uint32_t funcIndex = ExportedFunctionToFuncIndex(callee);
@@ -1094,16 +1108,35 @@ const CodeRange&
WasmInstanceObject::getExportedFunctionCodeRange(HandleFunction fun)
{
uint32_t funcIndex = ExportedFunctionToFuncIndex(fun);
MOZ_ASSERT(exports().lookup(funcIndex)->value() == fun);
const Metadata& metadata = instance().metadata();
return metadata.codeRanges[metadata.lookupFuncExport(funcIndex).codeRangeIndex()];
}
+/* static */ WasmFunctionScope*
+WasmInstanceObject::getFunctionScope(JSContext* cx, HandleWasmInstanceObject instanceObj,
+ uint32_t funcIndex)
+{
+ if (ScopeMap::Ptr p = instanceObj->scopes().lookup(funcIndex))
+ return p->value();
+
+ Rooted<WasmFunctionScope*> funcScope(cx, WasmFunctionScope::create(cx, instanceObj, funcIndex));
+ if (!funcScope)
+ return nullptr;
+
+ if (!instanceObj->scopes().putNew(funcIndex, funcScope)) {
+ ReportOutOfMemory(cx);
+ return nullptr;
+ }
+
+ return funcScope;
+}
+
bool
wasm::IsExportedFunction(JSFunction* fun)
{
return fun->maybeNative() == WasmCall;
}
bool
wasm::IsExportedWasmFunction(JSFunction* fun)
--- a/js/src/wasm/WasmJS.h
+++ b/js/src/wasm/WasmJS.h
@@ -21,16 +21,17 @@
#include "gc/Policy.h"
#include "vm/NativeObject.h"
#include "wasm/WasmTypes.h"
namespace js {
class TypedArrayObject;
+class WasmFunctionScope;
namespace wasm {
// Creates a testing-only NaN JS object with fields as described above, for
// T=float or T=double.
template<typename T>
JSObject*
@@ -143,33 +144,44 @@ class WasmModuleObject : public NativeOb
// The class of WebAssembly.Instance. Each WasmInstanceObject owns a
// wasm::Instance. These objects are used both as content-facing JS objects and
// as internal implementation details of asm.js.
class WasmInstanceObject : public NativeObject
{
static const unsigned INSTANCE_SLOT = 0;
static const unsigned EXPORTS_SLOT = 1;
+ static const unsigned SCOPES_SLOT = 2;
static const ClassOps classOps_;
bool isNewborn() const;
static void finalize(FreeOp* fop, JSObject* obj);
static void trace(JSTracer* trc, JSObject* obj);
- // ExportMap maps from function definition index to exported function
- // object. This allows the instance to lazily create exported function
+ // ExportMap maps from function index to exported function object.
+ // This allows the instance to lazily create exported function
// objects on demand (instead up-front for all table elements) while
// correctly preserving observable function object identity.
using ExportMap = GCHashMap<uint32_t,
HeapPtr<JSFunction*>,
DefaultHasher<uint32_t>,
SystemAllocPolicy>;
ExportMap& exports() const;
+ // WeakScopeMap maps from function index to js::Scope. This maps is weak
+ // to avoid holding scope objects alive. The scopes are normally created
+ // during debugging.
+ using ScopeMap = GCHashMap<uint32_t,
+ ReadBarriered<WasmFunctionScope*>,
+ DefaultHasher<uint32_t>,
+ SystemAllocPolicy>;
+ using WeakScopeMap = JS::WeakCache<ScopeMap>;
+ WeakScopeMap& scopes() const;
+
public:
- static const unsigned RESERVED_SLOTS = 2;
+ static const unsigned RESERVED_SLOTS = 3;
static const Class class_;
static const JSPropertySpec properties[];
static const JSFunctionSpec methods[];
static const JSFunctionSpec static_methods[];
static bool construct(JSContext*, unsigned, Value*);
static WasmInstanceObject* create(JSContext* cx,
UniquePtr<wasm::Code> code,
@@ -181,16 +193,20 @@ class WasmInstanceObject : public Native
wasm::Instance& instance() const;
static bool getExportedFunction(JSContext* cx,
HandleWasmInstanceObject instanceObj,
uint32_t funcIndex,
MutableHandleFunction fun);
const wasm::CodeRange& getExportedFunctionCodeRange(HandleFunction fun);
+
+ static WasmFunctionScope* getFunctionScope(JSContext* cx,
+ HandleWasmInstanceObject instanceObj,
+ uint32_t funcIndex);
};
// The class of WebAssembly.Memory. A WasmMemoryObject references an ArrayBuffer
// or SharedArrayBuffer object which owns the actual memory.
class WasmMemoryObject : public NativeObject
{
static const unsigned BUFFER_SLOT = 0;