Bug 1900276 - Implement bytecode to handle using syntax in module context. r=arai

Differential Revision: https://phabricator.services.mozilla.com/D212400
This commit is contained in:
Debadree Chatterjee 2024-06-04 18:29:21 +00:00
parent de089f1a73
commit 175cb7f2fa
7 changed files with 143 additions and 20 deletions

View file

@ -2467,6 +2467,14 @@ bool BytecodeEmitter::emitScript(ParseNode* body) {
} }
} }
#ifdef ENABLE_EXPLICIT_RESOURCE_MANAGEMENT
if (emitterScope.hasDisposables()) {
if (!emit1(JSOp::DisposeDisposables)) {
return false;
}
}
#endif
if (!markSimpleBreakpoint()) { if (!markSimpleBreakpoint()) {
return false; return false;
} }

View file

@ -1216,8 +1216,12 @@ static Maybe<ModuleScope::ParserData*> NewModuleScopeData(
} }
ModuleScope::ParserData* bindings = nullptr; ModuleScope::ParserData* bindings = nullptr;
uint32_t numBindings = uint32_t numBindings = imports.length() + vars.length() + lets.length() +
imports.length() + vars.length() + lets.length() + consts.length(); consts.length()
#ifdef ENABLE_EXPLICIT_RESOURCE_MANAGEMENT
+ usings.length()
#endif
;
if (numBindings > 0) { if (numBindings > 0) {
bindings = NewEmptyBindingData<ModuleScope>(fc, alloc, numBindings); bindings = NewEmptyBindingData<ModuleScope>(fc, alloc, numBindings);
@ -1229,7 +1233,12 @@ static Maybe<ModuleScope::ParserData*> NewModuleScopeData(
InitializeBindingData(bindings, numBindings, imports, InitializeBindingData(bindings, numBindings, imports,
&ParserModuleScopeSlotInfo::varStart, vars, &ParserModuleScopeSlotInfo::varStart, vars,
&ParserModuleScopeSlotInfo::letStart, lets, &ParserModuleScopeSlotInfo::letStart, lets,
&ParserModuleScopeSlotInfo::constStart, consts); &ParserModuleScopeSlotInfo::constStart, consts
#ifdef ENABLE_EXPLICIT_RESOURCE_MANAGEMENT
,
&ParserModuleScopeSlotInfo::usingStart, usings
#endif
);
} }
return Some(bindings); return Some(bindings);

View file

@ -0,0 +1,24 @@
// |jit-test| skip-if: !getBuildConfiguration("explicit-resource-management")
load(libdir + "asserts.js");
globalThis.callOrder = [];
const m = parseModule(`
using x = {
[Symbol.dispose]() {
globalThis.callOrder.push("x");
}
}
using y = {
[Symbol.dispose]() {
globalThis.callOrder.push("y");
}
}
`);
moduleLink(m);
moduleEvaluate(m);
assertArrayEq(globalThis.callOrder, ["y", "x"]);

View file

@ -0,0 +1,16 @@
// |jit-test| skip-if: !getBuildConfiguration("explicit-resource-management")
globalThis.called = false;
const m = parseModule(`
using x = {
[Symbol.dispose]() {
globalThis.called = true;
}
}
`);
moduleLink(m);
moduleEvaluate(m);
assertEq(globalThis.called, true);

View file

@ -414,9 +414,50 @@ ModuleEnvironmentObject* ModuleEnvironmentObject::create(
MOZ_ASSERT(!env->inDictionaryMode()); MOZ_ASSERT(!env->inDictionaryMode());
#endif #endif
#ifdef ENABLE_EXPLICIT_RESOURCE_MANAGEMENT
env->initSlot(ModuleEnvironmentObject::DISPOSABLE_OBJECTS_SLOT,
UndefinedValue());
#endif
return env; return env;
} }
#ifdef ENABLE_EXPLICIT_RESOURCE_MANAGEMENT
// TODO: at the time of unflagging ENABLE_EXPLICIT_RESOURCE_MANAGEMENT
// consider having a common base class for LexicalEnvironmentObject and
// ModuleEnvironmentObject containing all the common code.
static bool addDisposableObjectHelper(JS::Handle<EnvironmentObject*> env,
uint32_t slot, JSContext* cx,
JS::Handle<JS::Value> val) {
Value slotData = env->getReservedSlot(slot);
ListObject* disposablesList = nullptr;
if (slotData.isUndefined()) {
disposablesList = ListObject::create(cx);
if (!disposablesList) {
return false;
}
env->setReservedSlot(slot, ObjectValue(*disposablesList));
} else {
disposablesList = &slotData.toObject().as<ListObject>();
}
return disposablesList->append(cx, val);
}
bool ModuleEnvironmentObject::addDisposableObject(JSContext* cx,
JS::Handle<JS::Value> val) {
Rooted<ModuleEnvironmentObject*> env(cx, this);
return addDisposableObjectHelper(env, DISPOSABLE_OBJECTS_SLOT, cx, val);
}
Value ModuleEnvironmentObject::getDisposables() {
return getReservedSlot(DISPOSABLE_OBJECTS_SLOT);
}
void ModuleEnvironmentObject::clearDisposables() {
setReservedSlot(DISPOSABLE_OBJECTS_SLOT, UndefinedValue());
}
#endif
/* static */ /* static */
ModuleEnvironmentObject* ModuleEnvironmentObject::createSynthetic( ModuleEnvironmentObject* ModuleEnvironmentObject::createSynthetic(
JSContext* cx, Handle<ModuleObject*> module) { JSContext* cx, Handle<ModuleObject*> module) {
@ -957,18 +998,8 @@ bool LexicalEnvironmentObject::isExtensible() const {
#ifdef ENABLE_EXPLICIT_RESOURCE_MANAGEMENT #ifdef ENABLE_EXPLICIT_RESOURCE_MANAGEMENT
bool LexicalEnvironmentObject::addDisposableObject(JSContext* cx, bool LexicalEnvironmentObject::addDisposableObject(JSContext* cx,
JS::Handle<JS::Value> val) { JS::Handle<JS::Value> val) {
Value slotData = getReservedSlot(DISPOSABLE_OBJECTS_SLOT); Rooted<LexicalEnvironmentObject*> env(cx, this);
ListObject* disposablesList = nullptr; return addDisposableObjectHelper(env, DISPOSABLE_OBJECTS_SLOT, cx, val);
if (slotData.isUndefined()) {
disposablesList = ListObject::create(cx);
if (!disposablesList) {
return false;
}
setReservedSlot(DISPOSABLE_OBJECTS_SLOT, ObjectValue(*disposablesList));
} else {
disposablesList = &slotData.toObject().as<ListObject>();
}
return disposablesList->append(cx, val);
} }
Value LexicalEnvironmentObject::getDisposables() { Value LexicalEnvironmentObject::getDisposables() {

View file

@ -618,6 +618,10 @@ class VarEnvironmentObject : public EnvironmentObject {
class ModuleEnvironmentObject : public EnvironmentObject { class ModuleEnvironmentObject : public EnvironmentObject {
static constexpr uint32_t MODULE_SLOT = 1; static constexpr uint32_t MODULE_SLOT = 1;
#ifdef ENABLE_EXPLICIT_RESOURCE_MANAGEMENT
static constexpr uint32_t DISPOSABLE_OBJECTS_SLOT = 2;
#endif
static const ObjectOps objectOps_; static const ObjectOps objectOps_;
static const JSClassOps classOps_; static const JSClassOps classOps_;
@ -626,7 +630,12 @@ class ModuleEnvironmentObject : public EnvironmentObject {
static const JSClass class_; static const JSClass class_;
#ifdef ENABLE_EXPLICIT_RESOURCE_MANAGEMENT
static constexpr uint32_t RESERVED_SLOTS = 3;
#else
static constexpr uint32_t RESERVED_SLOTS = 2; static constexpr uint32_t RESERVED_SLOTS = 2;
#endif
static constexpr ObjectFlags OBJECT_FLAGS = {ObjectFlag::NotExtensible, static constexpr ObjectFlags OBJECT_FLAGS = {ObjectFlag::NotExtensible,
ObjectFlag::QualifiedVarObj}; ObjectFlag::QualifiedVarObj};
@ -655,6 +664,18 @@ class ModuleEnvironmentObject : public EnvironmentObject {
uint32_t firstSyntheticValueSlot() { return RESERVED_SLOTS; } uint32_t firstSyntheticValueSlot() { return RESERVED_SLOTS; }
#ifdef ENABLE_EXPLICIT_RESOURCE_MANAGEMENT
bool addDisposableObject(JSContext* cx, JS::Handle<JS::Value> val);
// Used to get the Disposable objects within the
// lexical scope, it returns a ListObject* if there
// is a non empty list of Disposables, else
// UndefinedValue.
Value getDisposables();
void clearDisposables();
#endif
private: private:
static bool lookupProperty(JSContext* cx, HandleObject obj, HandleId id, static bool lookupProperty(JSContext* cx, HandleObject obj, HandleId id,
MutableHandleObject objp, PropertyResult* propp); MutableHandleObject objp, PropertyResult* propp);

View file

@ -1639,11 +1639,15 @@ void js::ReportInNotObjectError(JSContext* cx, HandleValue lref,
#ifdef ENABLE_EXPLICIT_RESOURCE_MANAGEMENT #ifdef ENABLE_EXPLICIT_RESOURCE_MANAGEMENT
bool js::DisposeDisposablesOnScopeLeave(JSContext* cx, bool js::DisposeDisposablesOnScopeLeave(JSContext* cx,
JS::Handle<JSObject*> env) { JS::Handle<JSObject*> env) {
if (!env->is<LexicalEnvironmentObject>()) { if (!env->is<LexicalEnvironmentObject>() &&
!env->is<ModuleEnvironmentObject>()) {
return true; return true;
} }
Value maybeDisposables = env->as<LexicalEnvironmentObject>().getDisposables(); Value maybeDisposables =
env->is<LexicalEnvironmentObject>()
? env->as<LexicalEnvironmentObject>().getDisposables()
: env->as<ModuleEnvironmentObject>().getDisposables();
MOZ_ASSERT(maybeDisposables.isObject() || maybeDisposables.isUndefined()); MOZ_ASSERT(maybeDisposables.isObject() || maybeDisposables.isUndefined());
@ -1698,7 +1702,11 @@ bool js::DisposeDisposablesOnScopeLeave(JSContext* cx,
// Step 3. Set disposeCapability.[[DisposableResourceStack]] to // Step 3. Set disposeCapability.[[DisposableResourceStack]] to
// a new empty List. // a new empty List.
env->as<LexicalEnvironmentObject>().clearDisposables(); if (env->is<LexicalEnvironmentObject>()) {
env->as<LexicalEnvironmentObject>().clearDisposables();
} else {
env->as<ModuleEnvironmentObject>().clearDisposables();
}
// 4. Return ? completion. // 4. Return ? completion.
if (hadError) { if (hadError) {
@ -2069,8 +2077,14 @@ bool MOZ_NEVER_INLINE JS_HAZ_JSNATIVE_CALLER js::Interpret(JSContext* cx,
? UndefinedValue() ? UndefinedValue()
: REGS.sp[-1]); : REGS.sp[-1]);
if (!env->as<LexicalEnvironmentObject>().addDisposableObject(cx, val)) { if (env->is<LexicalEnvironmentObject>()) {
goto error; if (!env->as<LexicalEnvironmentObject>().addDisposableObject(cx, val)) {
goto error;
}
} else if (env->is<ModuleEnvironmentObject>()) {
if (!env->as<ModuleEnvironmentObject>().addDisposableObject(cx, val)) {
goto error;
}
} }
} }
END_CASE(AddDisposable) END_CASE(AddDisposable)