mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2025-07-30 04:39:06 +00:00
LibJS: Bring the explicit resource management implementation up to date
While we don't yet have a working `using` implementation with our byte code, we can still keep our DisposableStack implementation up to date. The changes brought in here are all editorial, and set us up to start an AsyncDisposableStack implementation.
This commit is contained in:
parent
f82b1c5a2d
commit
5ea0aa5f08
Notes:
github-actions[bot]
2025-01-17 19:47:35 +00:00
Author: https://github.com/trflynn89
Commit: 5ea0aa5f08
Pull-request: https://github.com/LadybirdBrowser/ladybird/pull/3290
12 changed files with 375 additions and 258 deletions
|
@ -122,6 +122,7 @@
|
||||||
JS_ENUMERATE_TYPED_ARRAYS
|
JS_ENUMERATE_TYPED_ARRAYS
|
||||||
|
|
||||||
#define JS_ENUMERATE_WELL_KNOWN_SYMBOLS \
|
#define JS_ENUMERATE_WELL_KNOWN_SYMBOLS \
|
||||||
|
__JS_ENUMERATE(asyncDispose, async_dispose) \
|
||||||
__JS_ENUMERATE(asyncIterator, async_iterator) \
|
__JS_ENUMERATE(asyncIterator, async_iterator) \
|
||||||
__JS_ENUMERATE(dispose, dispose) \
|
__JS_ENUMERATE(dispose, dispose) \
|
||||||
__JS_ENUMERATE(hasInstance, has_instance) \
|
__JS_ENUMERATE(hasInstance, has_instance) \
|
||||||
|
@ -163,6 +164,8 @@ class Completion;
|
||||||
class Console;
|
class Console;
|
||||||
class CyclicModule;
|
class CyclicModule;
|
||||||
class DeclarativeEnvironment;
|
class DeclarativeEnvironment;
|
||||||
|
struct DisposeCapability;
|
||||||
|
struct DisposableResource;
|
||||||
class ECMAScriptFunctionObject;
|
class ECMAScriptFunctionObject;
|
||||||
class Environment;
|
class Environment;
|
||||||
class Error;
|
class Error;
|
||||||
|
|
|
@ -387,13 +387,15 @@ ThrowCompletionOr<Object*> get_prototype_from_constructor(VM& vm, FunctionObject
|
||||||
}
|
}
|
||||||
|
|
||||||
// 9.1.2.2 NewDeclarativeEnvironment ( E ), https://tc39.es/ecma262/#sec-newdeclarativeenvironment
|
// 9.1.2.2 NewDeclarativeEnvironment ( E ), https://tc39.es/ecma262/#sec-newdeclarativeenvironment
|
||||||
|
// 4.1.2.1 NewDeclarativeEnvironment ( E ), https://tc39.es/proposal-explicit-resource-management/#sec-declarative-environment-records-initializebinding-n-v
|
||||||
GC::Ref<DeclarativeEnvironment> new_declarative_environment(Environment& environment)
|
GC::Ref<DeclarativeEnvironment> new_declarative_environment(Environment& environment)
|
||||||
{
|
{
|
||||||
auto& heap = environment.heap();
|
auto& heap = environment.heap();
|
||||||
|
|
||||||
// 1. Let env be a new Declarative Environment Record containing no bindings.
|
// 1. Let env be a new Declarative Environment Record containing no bindings.
|
||||||
// 2. Set env.[[OuterEnv]] to E.
|
// 2. Set env.[[OuterEnv]] to E.
|
||||||
// 3. Return env.
|
// 3. Set env.[[DisposeCapability]] to NewDisposeCapability().
|
||||||
|
// 4. Return env.
|
||||||
return heap.allocate<DeclarativeEnvironment>(&environment);
|
return heap.allocate<DeclarativeEnvironment>(&environment);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -411,6 +413,7 @@ GC::Ref<ObjectEnvironment> new_object_environment(Object& object, bool is_with_e
|
||||||
}
|
}
|
||||||
|
|
||||||
// 9.1.2.4 NewFunctionEnvironment ( F, newTarget ), https://tc39.es/ecma262/#sec-newfunctionenvironment
|
// 9.1.2.4 NewFunctionEnvironment ( F, newTarget ), https://tc39.es/ecma262/#sec-newfunctionenvironment
|
||||||
|
// 4.1.2.2 NewFunctionEnvironment ( F, newTarget ), https://tc39.es/proposal-explicit-resource-management/#sec-newfunctionenvironment
|
||||||
GC::Ref<FunctionEnvironment> new_function_environment(ECMAScriptFunctionObject& function, Object* new_target)
|
GC::Ref<FunctionEnvironment> new_function_environment(ECMAScriptFunctionObject& function, Object* new_target)
|
||||||
{
|
{
|
||||||
auto& heap = function.heap();
|
auto& heap = function.heap();
|
||||||
|
@ -432,9 +435,10 @@ GC::Ref<FunctionEnvironment> new_function_environment(ECMAScriptFunctionObject&
|
||||||
env->set_new_target(new_target ?: js_undefined());
|
env->set_new_target(new_target ?: js_undefined());
|
||||||
|
|
||||||
// 6. Set env.[[OuterEnv]] to F.[[Environment]].
|
// 6. Set env.[[OuterEnv]] to F.[[Environment]].
|
||||||
|
// 7. Set env.[[DisposeCapability]] to NewDisposeCapability().
|
||||||
// NOTE: Done in step 1 via the FunctionEnvironment constructor.
|
// NOTE: Done in step 1 via the FunctionEnvironment constructor.
|
||||||
|
|
||||||
// 7. Return env.
|
// 8. Return env.
|
||||||
return env;
|
return env;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1403,124 +1407,228 @@ ThrowCompletionOr<String> get_substitution(VM& vm, Utf16View const& matched, Utf
|
||||||
return MUST(Utf16View { result }.to_utf8(Utf16View::AllowInvalidCodeUnits::Yes));
|
return MUST(Utf16View { result }.to_utf8(Utf16View::AllowInvalidCodeUnits::Yes));
|
||||||
}
|
}
|
||||||
|
|
||||||
// 2.1.2 AddDisposableResource ( disposable, V, hint [ , method ] ), https://tc39.es/proposal-explicit-resource-management/#sec-adddisposableresource-disposable-v-hint-disposemethod
|
void DisposeCapability::visit_edges(GC::Cell::Visitor& visitor) const
|
||||||
ThrowCompletionOr<void> add_disposable_resource(VM& vm, Vector<DisposableResource>& disposable, Value value, Environment::InitializeBindingHint hint, FunctionObject* method)
|
|
||||||
{
|
{
|
||||||
// NOTE: For now only sync is a valid hint
|
for (auto const& disposable_resource : disposable_resource_stack)
|
||||||
VERIFY(hint == Environment::InitializeBindingHint::SyncDispose);
|
disposable_resource.visit_edges(visitor);
|
||||||
|
}
|
||||||
|
|
||||||
|
void DisposableResource::visit_edges(GC::Cell::Visitor& visitor) const
|
||||||
|
{
|
||||||
|
visitor.visit(resource_value);
|
||||||
|
visitor.visit(dispose_method);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2.1.3 NewDisposeCapability ( ), https://tc39.es/proposal-explicit-resource-management/#sec-newdisposecapability
|
||||||
|
DisposeCapability new_dispose_capability()
|
||||||
|
{
|
||||||
|
// 1. Let stack be a new empty List.
|
||||||
|
// 2. Return the DisposeCapability Record { [[DisposableResourceStack]]: stack }.
|
||||||
|
return DisposeCapability {};
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2.1.4 AddDisposableResource ( disposeCapability, V, hint [ , method ] ), https://tc39.es/proposal-explicit-resource-management/#sec-adddisposableresource-disposable-v-hint-disposemethod
|
||||||
|
ThrowCompletionOr<void> add_disposable_resource(VM& vm, DisposeCapability& dispose_capability, Value value, Environment::InitializeBindingHint hint, GC::Ptr<FunctionObject> method)
|
||||||
|
{
|
||||||
Optional<DisposableResource> resource;
|
Optional<DisposableResource> resource;
|
||||||
|
|
||||||
// 1. If method is not present then,
|
// 1. If method is not present then,
|
||||||
if (!method) {
|
if (!method) {
|
||||||
// a. If V is null or undefined, return NormalCompletion(empty).
|
// a. If V is either null or undefined and hint is sync-dispose, then
|
||||||
if (value.is_nullish())
|
if (value.is_nullish() && hint == Environment::InitializeBindingHint::SyncDispose) {
|
||||||
|
// i. Return unused.
|
||||||
return {};
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
// b. If Type(V) is not Object, throw a TypeError exception.
|
// b. NOTE: When V is either null or undefined and hint is async-dispose, we record that the resource was evaluated
|
||||||
if (!value.is_object())
|
// to ensure we will still perform an Await when resources are later disposed.
|
||||||
return vm.throw_completion<TypeError>(ErrorType::NotAnObject, value.to_string_without_side_effects());
|
|
||||||
|
|
||||||
// c. Let resource be ? CreateDisposableResource(V, hint).
|
// c. Let resource be ? CreateDisposableResource(V, hint).
|
||||||
resource = TRY(create_disposable_resource(vm, value, hint));
|
resource = TRY(create_disposable_resource(vm, value, hint));
|
||||||
}
|
}
|
||||||
// 2. Else,
|
// 2. Else,
|
||||||
else {
|
else {
|
||||||
// a. If V is null or undefined, then
|
// a. Assert: V is undefined.
|
||||||
if (value.is_nullish()) {
|
VERIFY(value.is_undefined());
|
||||||
// i. Let resource be ? CreateDisposableResource(undefined, hint, method).
|
|
||||||
|
// b. Let resource be ? CreateDisposableResource(undefined, hint, method).
|
||||||
resource = TRY(create_disposable_resource(vm, js_undefined(), hint, method));
|
resource = TRY(create_disposable_resource(vm, js_undefined(), hint, method));
|
||||||
}
|
}
|
||||||
// b. Else,
|
|
||||||
else {
|
|
||||||
// i. If Type(V) is not Object, throw a TypeError exception.
|
|
||||||
if (!value.is_object())
|
|
||||||
return vm.throw_completion<TypeError>(ErrorType::NotAnObject, value.to_string_without_side_effects());
|
|
||||||
|
|
||||||
// ii. Let resource be ? CreateDisposableResource(V, hint, method).
|
// 3. Append resource to disposeCapability.[[DisposableResourceStack]].
|
||||||
resource = TRY(create_disposable_resource(vm, value, hint, method));
|
dispose_capability.disposable_resource_stack.append(resource.release_value());
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 3. Append resource to disposable.[[DisposableResourceStack]].
|
// 4. Return unused.
|
||||||
VERIFY(resource.has_value());
|
|
||||||
disposable.append(resource.release_value());
|
|
||||||
|
|
||||||
// 4. Return NormalCompletion(empty).
|
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
// 2.1.3 CreateDisposableResource ( V, hint [ , method ] ), https://tc39.es/proposal-explicit-resource-management/#sec-createdisposableresource
|
// 2.1.5 CreateDisposableResource ( V, hint [ , method ] ), https://tc39.es/proposal-explicit-resource-management/#sec-createdisposableresource
|
||||||
ThrowCompletionOr<DisposableResource> create_disposable_resource(VM& vm, Value value, Environment::InitializeBindingHint hint, FunctionObject* method)
|
ThrowCompletionOr<DisposableResource> create_disposable_resource(VM& vm, Value value, Environment::InitializeBindingHint hint, GC::Ptr<FunctionObject> method)
|
||||||
{
|
{
|
||||||
// 1. If method is not present, then
|
// 1. If method is not present, then
|
||||||
if (!method) {
|
if (!method) {
|
||||||
// a. If V is undefined, throw a TypeError exception.
|
// a. If V is either null or undefined, then
|
||||||
if (value.is_undefined())
|
if (value.is_nullish()) {
|
||||||
return vm.throw_completion<TypeError>(ErrorType::IsUndefined, "value");
|
// i. Set V to undefined.
|
||||||
|
// ii. Set method to undefined.
|
||||||
|
}
|
||||||
|
// b. Else,
|
||||||
|
else {
|
||||||
|
// i. If V is not an Object, throw a TypeError exception.
|
||||||
|
if (!value.is_object())
|
||||||
|
return vm.throw_completion<TypeError>(ErrorType::NotAnObject, value);
|
||||||
|
|
||||||
// b. Set method to ? GetDisposeMethod(V, hint).
|
// ii. Set method to ? GetDisposeMethod(V, hint).
|
||||||
method = TRY(get_dispose_method(vm, value, hint));
|
method = TRY(get_dispose_method(vm, value, hint));
|
||||||
|
|
||||||
// c. If method is undefined, throw a TypeError exception.
|
// iii. If method is undefined, throw a TypeError exception.
|
||||||
if (!method)
|
if (!method)
|
||||||
return vm.throw_completion<TypeError>(ErrorType::NoDisposeMethod, value.to_string_without_side_effects());
|
return vm.throw_completion<TypeError>(ErrorType::NoDisposeMethod, value);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
// 2. Else,
|
// 2. Else,
|
||||||
|
else {
|
||||||
// a. If IsCallable(method) is false, throw a TypeError exception.
|
// a. If IsCallable(method) is false, throw a TypeError exception.
|
||||||
// NOTE: This is guaranteed to never occur from the type.
|
// NOTE: This is guaranteed to never occur due to its type.
|
||||||
VERIFY(method);
|
}
|
||||||
|
|
||||||
// 3. Return the DisposableResource Record { [[ResourceValue]]: V, [[Hint]]: hint, [[DisposeMethod]]: method }.
|
// 3. Return the DisposableResource Record { [[ResourceValue]]: V, [[Hint]]: hint, [[DisposeMethod]]: method }.
|
||||||
// NOTE: Since we only support sync dispose we don't store the hint for now.
|
|
||||||
VERIFY(hint == Environment::InitializeBindingHint::SyncDispose);
|
|
||||||
return DisposableResource {
|
return DisposableResource {
|
||||||
value,
|
.resource_value = value.is_object() ? GC::Ptr { value.as_object() } : nullptr,
|
||||||
*method
|
.hint = hint,
|
||||||
|
.dispose_method = method,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
// 2.1.4 GetDisposeMethod ( V, hint ), https://tc39.es/proposal-explicit-resource-management/#sec-getdisposemethod
|
// 2.1.6 GetDisposeMethod ( V, hint ), https://tc39.es/proposal-explicit-resource-management/#sec-getdisposemethod
|
||||||
ThrowCompletionOr<GC::Ptr<FunctionObject>> get_dispose_method(VM& vm, Value value, Environment::InitializeBindingHint hint)
|
ThrowCompletionOr<GC::Ptr<FunctionObject>> get_dispose_method(VM& vm, Value value, Environment::InitializeBindingHint hint)
|
||||||
{
|
{
|
||||||
// NOTE: We only have sync dispose for now which means we ignore step 1.
|
GC::Ptr<FunctionObject> method;
|
||||||
VERIFY(hint == Environment::InitializeBindingHint::SyncDispose);
|
|
||||||
|
|
||||||
|
// 1. If hint is async-dispose, then
|
||||||
|
if (hint == Environment::InitializeBindingHint::AsyncDispose) {
|
||||||
|
// a. Let method be ? GetMethod(V, @@asyncDispose).
|
||||||
|
method = TRY(value.get_method(vm, vm.well_known_symbol_async_dispose()));
|
||||||
|
|
||||||
|
// b. If method is undefined, then
|
||||||
|
if (!method) {
|
||||||
|
// i. Set method to ? GetMethod(V, @@dispose).
|
||||||
|
method = TRY(value.get_method(vm, vm.well_known_symbol_dispose()));
|
||||||
|
|
||||||
|
// ii. If method is not undefined, then
|
||||||
|
if (method) {
|
||||||
|
auto& realm = *vm.current_realm();
|
||||||
|
|
||||||
|
// 1. Let closure be a new Abstract Closure with no parameters that captures method and performs the following steps when called:
|
||||||
|
auto closure = [&realm, method](VM& vm) -> ThrowCompletionOr<Value> {
|
||||||
|
// a. Let O be the this value.
|
||||||
|
auto object = vm.this_value();
|
||||||
|
|
||||||
|
// b. Let promiseCapability be ! NewPromiseCapability(%Promise%).
|
||||||
|
auto promise_capability = MUST(new_promise_capability(vm, realm.intrinsics().promise_constructor()));
|
||||||
|
|
||||||
|
// c. Let result be Completion(Call(method, O)).
|
||||||
|
// d. IfAbruptRejectPromise(result, promiseCapability).
|
||||||
|
TRY_OR_REJECT(vm, promise_capability, call(vm, method, object));
|
||||||
|
|
||||||
|
// e. Perform ? Call(promiseCapability.[[Resolve]], undefined, « undefined »).
|
||||||
|
TRY(call(vm, *promise_capability->resolve(), js_undefined(), js_undefined()));
|
||||||
|
|
||||||
|
// f. Return promiseCapability.[[Promise]].
|
||||||
|
return promise_capability->promise();
|
||||||
|
};
|
||||||
|
|
||||||
|
// 2. NOTE: This function is not observable to user code. It is used to ensure that a Promise returned
|
||||||
|
// from a synchronous @@dispose method will not be awaited and that any exception thrown will not be
|
||||||
|
// thrown synchronously.
|
||||||
|
|
||||||
|
// 3. Return CreateBuiltinFunction(closure, 0, "", « »).
|
||||||
|
return NativeFunction::create(realm, move(closure), 0, "");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
// 2. Else,
|
// 2. Else,
|
||||||
|
else {
|
||||||
// a. Let method be ? GetMethod(V, @@dispose).
|
// a. Let method be ? GetMethod(V, @@dispose).
|
||||||
return TRY(value.get_method(vm, vm.well_known_symbol_dispose()));
|
method = TRY(value.get_method(vm, vm.well_known_symbol_dispose()));
|
||||||
}
|
}
|
||||||
|
|
||||||
// 2.1.5 Dispose ( V, hint, method ), https://tc39.es/proposal-explicit-resource-management/#sec-dispose
|
// 3. Return method.
|
||||||
Completion dispose(VM& vm, Value value, GC::Ref<FunctionObject> method)
|
return method;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2.1.7 Dispose ( V, hint, method ), https://tc39.es/proposal-explicit-resource-management/#sec-dispose
|
||||||
|
Completion dispose(VM& vm, Value value, Environment::InitializeBindingHint hint, GC::Ptr<FunctionObject> method)
|
||||||
{
|
{
|
||||||
// 1. Let result be ? Call(method, V).
|
Value result;
|
||||||
[[maybe_unused]] auto result = TRY(call(vm, *method, value));
|
|
||||||
|
|
||||||
// NOTE: Hint can only be sync-dispose so we ignore step 2.
|
// 1. If method is undefined, let result be undefined.
|
||||||
// 2. If hint is async-dispose and result is not undefined, then
|
if (!method) {
|
||||||
|
result = js_undefined();
|
||||||
|
}
|
||||||
|
// 2. Else, let result be ? Call(method, V).
|
||||||
|
else {
|
||||||
|
result = TRY(call(vm, *method, value));
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3. If hint is async-dispose, then
|
||||||
|
if (hint == Environment::InitializeBindingHint::AsyncDispose) {
|
||||||
// a. Perform ? Await(result).
|
// a. Perform ? Await(result).
|
||||||
|
TRY(await(vm, result));
|
||||||
|
}
|
||||||
|
|
||||||
// 3. Return undefined.
|
// 4. Return undefined.
|
||||||
return js_undefined();
|
return js_undefined();
|
||||||
}
|
}
|
||||||
|
|
||||||
// 2.1.6 DisposeResources ( disposable, completion ), https://tc39.es/proposal-explicit-resource-management/#sec-disposeresources-disposable-completion-errors
|
// 2.1.8 DisposeResources ( disposeCapability, completion ), https://tc39.es/proposal-explicit-resource-management/#sec-disposeresources
|
||||||
Completion dispose_resources(VM& vm, Vector<DisposableResource> const& disposable, Completion completion)
|
Completion dispose_resources(VM& vm, DisposeCapability& dispose_capability, Completion completion)
|
||||||
{
|
{
|
||||||
// 1. If disposable is not undefined, then
|
// 1. Let needsAwait be false.
|
||||||
// NOTE: At this point disposable is always defined.
|
bool needs_await = false;
|
||||||
|
|
||||||
// a. For each resource of disposable.[[DisposableResourceStack]], in reverse list order, do
|
// 2. Let hasAwaited be false.
|
||||||
for (auto const& resource : disposable.in_reverse()) {
|
bool has_awaited = false;
|
||||||
// i. Let result be Dispose(resource.[[ResourceValue]], resource.[[Hint]], resource.[[DisposeMethod]]).
|
|
||||||
auto result = dispose(vm, resource.resource_value, resource.dispose_method);
|
|
||||||
|
|
||||||
// ii. If result.[[Type]] is throw, then
|
// 3. For each element resource of disposeCapability.[[DisposableResourceStack]], in reverse list order, do
|
||||||
if (result.is_error()) {
|
for (auto const& resource : dispose_capability.disposable_resource_stack.in_reverse()) {
|
||||||
// 1. If completion.[[Type]] is throw, then
|
// a. Let value be resource.[[ResourceValue]].
|
||||||
if (completion.is_error()) {
|
auto value = resource.resource_value;
|
||||||
|
|
||||||
|
// b. Let hint be resource.[[Hint]].
|
||||||
|
auto hint = resource.hint;
|
||||||
|
|
||||||
|
// c. Let method be resource.[[DisposeMethod]].
|
||||||
|
auto method = resource.dispose_method;
|
||||||
|
|
||||||
|
// d. If hint is sync-dispose and needsAwait is true and hasAwaited is false, then
|
||||||
|
if (hint == Environment::InitializeBindingHint::SyncDispose && needs_await && !has_awaited) {
|
||||||
|
// i. Perform ! Await(undefined).
|
||||||
|
MUST(await(vm, js_undefined()));
|
||||||
|
|
||||||
|
// ii. Set needsAwait to false.
|
||||||
|
needs_await = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// e. If method is not undefined, then
|
||||||
|
if (method) {
|
||||||
|
// i. Let result be Completion(Call(method, value)).
|
||||||
|
auto result = call(vm, *method, value);
|
||||||
|
|
||||||
|
// ii. If result is a normal completion and hint is async-dispose, then
|
||||||
|
if (!result.is_throw_completion() && hint == Environment::InitializeBindingHint::AsyncDispose) {
|
||||||
|
// 1. Set result to Completion(Await(result.[[Value]])).
|
||||||
|
result = await(vm, result.value());
|
||||||
|
|
||||||
|
// 2. Set hasAwaited to true.
|
||||||
|
has_awaited = true;
|
||||||
|
}
|
||||||
|
// iii. If result is a throw completion, then
|
||||||
|
else if (result.is_throw_completion()) {
|
||||||
|
// 1. If completion is a throw completion, then
|
||||||
|
if (completion.type() == Completion::Type::Throw) {
|
||||||
// a. Set result to result.[[Value]].
|
// a. Set result to result.[[Value]].
|
||||||
|
auto result_value = result.error().value().value();
|
||||||
|
|
||||||
// b. Let suppressed be completion.[[Value]].
|
// b. Let suppressed be completion.[[Value]].
|
||||||
auto suppressed = completion.value().value();
|
auto suppressed = completion.value().value();
|
||||||
|
@ -1528,11 +1636,11 @@ Completion dispose_resources(VM& vm, Vector<DisposableResource> const& disposabl
|
||||||
// c. Let error be a newly created SuppressedError object.
|
// c. Let error be a newly created SuppressedError object.
|
||||||
auto error = SuppressedError::create(*vm.current_realm());
|
auto error = SuppressedError::create(*vm.current_realm());
|
||||||
|
|
||||||
// d. Perform ! DefinePropertyOrThrow(error, "error", PropertyDescriptor { [[Configurable]]: true, [[Enumerable]]: false, [[Writable]]: true, [[Value]]: result }).
|
// d. Perform CreateNonEnumerableDataPropertyOrThrow(error, "error", result).
|
||||||
MUST(error->define_property_or_throw(vm.names.error, { .value = result.value(), .writable = true, .enumerable = true, .configurable = true }));
|
error->create_non_enumerable_data_property_or_throw(vm.names.error, result_value);
|
||||||
|
|
||||||
// e. Perform ! DefinePropertyOrThrow(error, "suppressed", PropertyDescriptor { [[Configurable]]: true, [[Enumerable]]: false, [[Writable]]: true, [[Value]]: suppressed }).
|
// e. Perform CreateNonEnumerableDataPropertyOrThrow(error, "suppressed", suppressed).
|
||||||
MUST(error->define_property_or_throw(vm.names.suppressed, { .value = suppressed, .writable = true, .enumerable = false, .configurable = true }));
|
error->create_non_enumerable_data_property_or_throw(vm.names.suppressed, suppressed);
|
||||||
|
|
||||||
// f. Set completion to ThrowCompletion(error).
|
// f. Set completion to ThrowCompletion(error).
|
||||||
completion = throw_completion(error);
|
completion = throw_completion(error);
|
||||||
|
@ -1540,22 +1648,37 @@ Completion dispose_resources(VM& vm, Vector<DisposableResource> const& disposabl
|
||||||
// 2. Else,
|
// 2. Else,
|
||||||
else {
|
else {
|
||||||
// a. Set completion to result.
|
// a. Set completion to result.
|
||||||
completion = result;
|
completion = result.release_error();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// f. Else,
|
||||||
|
else {
|
||||||
|
// i. Assert: hint is async-dispose.
|
||||||
|
VERIFY(hint == Environment::InitializeBindingHint::AsyncDispose);
|
||||||
|
|
||||||
|
// ii. Set needsAwait to true.
|
||||||
|
needs_await = true;
|
||||||
|
|
||||||
|
// iii. NOTE: This can only indicate a case where either null or undefined was the initialized value of an
|
||||||
|
// await using declaration.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// 2. Return completion.
|
// 4. If needsAwait is true and hasAwaited is false, then
|
||||||
return completion;
|
if (needs_await && !has_awaited) {
|
||||||
|
// a. Perform ! Await(undefined).
|
||||||
|
MUST(await(vm, js_undefined()));
|
||||||
}
|
}
|
||||||
|
|
||||||
Completion dispose_resources(VM& vm, GC::Ptr<DeclarativeEnvironment> disposable, Completion completion)
|
// 5. NOTE: After disposeCapability has been disposed, it will never be used again. The contents of
|
||||||
{
|
// disposeCapability.[[DisposableResourceStack]] can be discarded in implementations, such as by garbage
|
||||||
// 1. If disposable is not undefined, then
|
// collection, at this point.
|
||||||
if (disposable)
|
|
||||||
return dispose_resources(vm, disposable->disposable_resource_stack(), completion);
|
|
||||||
|
|
||||||
// 2. Return completion.
|
// 6. Set disposeCapability.[[DisposableResourceStack]] to a new empty List.
|
||||||
|
dispose_capability.disposable_resource_stack.clear();
|
||||||
|
|
||||||
|
// 7. Return completion.
|
||||||
return completion;
|
return completion;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -12,6 +12,7 @@
|
||||||
#include <LibGC/RootVector.h>
|
#include <LibGC/RootVector.h>
|
||||||
#include <LibJS/Forward.h>
|
#include <LibJS/Forward.h>
|
||||||
#include <LibJS/Runtime/CanonicalIndex.h>
|
#include <LibJS/Runtime/CanonicalIndex.h>
|
||||||
|
#include <LibJS/Runtime/Environment.h>
|
||||||
#include <LibJS/Runtime/FunctionObject.h>
|
#include <LibJS/Runtime/FunctionObject.h>
|
||||||
#include <LibJS/Runtime/GlobalObject.h>
|
#include <LibJS/Runtime/GlobalObject.h>
|
||||||
#include <LibJS/Runtime/Iterator.h>
|
#include <LibJS/Runtime/Iterator.h>
|
||||||
|
@ -44,16 +45,28 @@ ThrowCompletionOr<Object*> get_prototype_from_constructor(VM&, FunctionObject co
|
||||||
Object* create_unmapped_arguments_object(VM&, ReadonlySpan<Value> arguments);
|
Object* create_unmapped_arguments_object(VM&, ReadonlySpan<Value> arguments);
|
||||||
Object* create_mapped_arguments_object(VM&, FunctionObject&, Vector<FunctionParameter> const&, ReadonlySpan<Value> arguments, Environment&);
|
Object* create_mapped_arguments_object(VM&, FunctionObject&, Vector<FunctionParameter> const&, ReadonlySpan<Value> arguments, Environment&);
|
||||||
|
|
||||||
struct DisposableResource {
|
// 2.1.1 DisposeCapability Records, https://tc39.es/proposal-explicit-resource-management/#sec-disposecapability-records
|
||||||
Value resource_value;
|
struct DisposeCapability {
|
||||||
GC::Ref<FunctionObject> dispose_method;
|
void visit_edges(GC::Cell::Visitor&) const;
|
||||||
|
|
||||||
|
Vector<DisposableResource> disposable_resource_stack; // [[DisposableResourceStack]]
|
||||||
};
|
};
|
||||||
ThrowCompletionOr<void> add_disposable_resource(VM&, Vector<DisposableResource>& disposable, Value, Environment::InitializeBindingHint, FunctionObject* = nullptr);
|
|
||||||
ThrowCompletionOr<DisposableResource> create_disposable_resource(VM&, Value, Environment::InitializeBindingHint, FunctionObject* method = nullptr);
|
// 2.1.2 DisposableResource Records, https://tc39.es/proposal-explicit-resource-management/#sec-disposableresource-records
|
||||||
|
struct DisposableResource {
|
||||||
|
void visit_edges(GC::Cell::Visitor&) const;
|
||||||
|
|
||||||
|
GC::Ptr<Object> resource_value; // [[ResourceValue]]
|
||||||
|
Environment::InitializeBindingHint hint; // [[Hint]]
|
||||||
|
GC::Ptr<FunctionObject> dispose_method; // [[DisposeMethod]]
|
||||||
|
};
|
||||||
|
|
||||||
|
DisposeCapability new_dispose_capability();
|
||||||
|
ThrowCompletionOr<void> add_disposable_resource(VM&, DisposeCapability&, Value, Environment::InitializeBindingHint, GC::Ptr<FunctionObject> = {});
|
||||||
|
ThrowCompletionOr<DisposableResource> create_disposable_resource(VM&, Value, Environment::InitializeBindingHint, GC::Ptr<FunctionObject> = {});
|
||||||
ThrowCompletionOr<GC::Ptr<FunctionObject>> get_dispose_method(VM&, Value, Environment::InitializeBindingHint);
|
ThrowCompletionOr<GC::Ptr<FunctionObject>> get_dispose_method(VM&, Value, Environment::InitializeBindingHint);
|
||||||
Completion dispose(VM& vm, Value, GC::Ref<FunctionObject> method);
|
Completion dispose(VM&, Value, Environment::InitializeBindingHint, GC::Ptr<FunctionObject> method);
|
||||||
Completion dispose_resources(VM& vm, Vector<DisposableResource> const& disposable, Completion completion);
|
Completion dispose_resources(VM&, DisposeCapability&, Completion);
|
||||||
Completion dispose_resources(VM& vm, GC::Ptr<DeclarativeEnvironment> disposable, Completion completion);
|
|
||||||
|
|
||||||
ThrowCompletionOr<Value> perform_import_call(VM&, Value specifier, Value options_value);
|
ThrowCompletionOr<Value> perform_import_call(VM&, Value specifier, Value options_value);
|
||||||
|
|
||||||
|
|
|
@ -25,30 +25,30 @@ DeclarativeEnvironment* DeclarativeEnvironment::create_for_per_iteration_binding
|
||||||
|
|
||||||
DeclarativeEnvironment::DeclarativeEnvironment()
|
DeclarativeEnvironment::DeclarativeEnvironment()
|
||||||
: Environment(nullptr, IsDeclarative::Yes)
|
: Environment(nullptr, IsDeclarative::Yes)
|
||||||
|
, m_dispose_capability(new_dispose_capability())
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
DeclarativeEnvironment::DeclarativeEnvironment(Environment* parent_environment)
|
DeclarativeEnvironment::DeclarativeEnvironment(Environment* parent_environment)
|
||||||
: Environment(parent_environment, IsDeclarative::Yes)
|
: Environment(parent_environment, IsDeclarative::Yes)
|
||||||
|
, m_dispose_capability(new_dispose_capability())
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
DeclarativeEnvironment::DeclarativeEnvironment(Environment* parent_environment, ReadonlySpan<Binding> bindings)
|
DeclarativeEnvironment::DeclarativeEnvironment(Environment* parent_environment, ReadonlySpan<Binding> bindings)
|
||||||
: Environment(parent_environment, IsDeclarative::Yes)
|
: Environment(parent_environment, IsDeclarative::Yes)
|
||||||
, m_bindings(bindings)
|
, m_bindings(bindings)
|
||||||
|
, m_dispose_capability(new_dispose_capability())
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
void DeclarativeEnvironment::visit_edges(Visitor& visitor)
|
void DeclarativeEnvironment::visit_edges(Visitor& visitor)
|
||||||
{
|
{
|
||||||
Base::visit_edges(visitor);
|
Base::visit_edges(visitor);
|
||||||
|
m_dispose_capability.visit_edges(visitor);
|
||||||
|
|
||||||
for (auto& binding : m_bindings)
|
for (auto& binding : m_bindings)
|
||||||
visitor.visit(binding.value);
|
visitor.visit(binding.value);
|
||||||
|
|
||||||
for (auto& disposable : m_disposable_resource_stack) {
|
|
||||||
visitor.visit(disposable.resource_value);
|
|
||||||
visitor.visit(disposable.dispose_method);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 9.1.1.1.1 HasBinding ( N ), https://tc39.es/ecma262/#sec-declarative-environment-records-hasbinding-n
|
// 9.1.1.1.1 HasBinding ( N ), https://tc39.es/ecma262/#sec-declarative-environment-records-hasbinding-n
|
||||||
|
@ -122,9 +122,9 @@ ThrowCompletionOr<void> DeclarativeEnvironment::initialize_binding_direct(VM& vm
|
||||||
// 1. Assert: envRec must have an uninitialized binding for N.
|
// 1. Assert: envRec must have an uninitialized binding for N.
|
||||||
VERIFY(binding.initialized == false);
|
VERIFY(binding.initialized == false);
|
||||||
|
|
||||||
// 2. If hint is not normal, perform ? AddDisposableResource(envRec, V, hint).
|
// 2. If hint is not normal, perform ? AddDisposableResource(envRec.[[DisposeCapability]], V, hint).
|
||||||
if (hint != Environment::InitializeBindingHint::Normal)
|
if (hint != Environment::InitializeBindingHint::Normal)
|
||||||
TRY(add_disposable_resource(vm, m_disposable_resource_stack, value, hint));
|
TRY(add_disposable_resource(vm, m_dispose_capability, value, hint));
|
||||||
|
|
||||||
// 3. Set the bound value for N in envRec to V.
|
// 3. Set the bound value for N in envRec to V.
|
||||||
binding.value = value;
|
binding.value = value;
|
||||||
|
|
|
@ -69,13 +69,13 @@ public:
|
||||||
|
|
||||||
[[nodiscard]] u64 environment_serial_number() const { return m_environment_serial_number; }
|
[[nodiscard]] u64 environment_serial_number() const { return m_environment_serial_number; }
|
||||||
|
|
||||||
|
DisposeCapability const& dispose_capability() const { return m_dispose_capability; }
|
||||||
|
DisposeCapability& dispose_capability() { return m_dispose_capability; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
ThrowCompletionOr<Value> get_binding_value_direct(VM&, Binding const&) const;
|
ThrowCompletionOr<Value> get_binding_value_direct(VM&, Binding const&) const;
|
||||||
ThrowCompletionOr<void> set_mutable_binding_direct(VM&, Binding&, Value, bool strict);
|
ThrowCompletionOr<void> set_mutable_binding_direct(VM&, Binding&, Value, bool strict);
|
||||||
|
|
||||||
friend Completion dispose_resources(VM&, GC::Ptr<DeclarativeEnvironment>, Completion);
|
|
||||||
Vector<DisposableResource> const& disposable_resource_stack() const { return m_disposable_resource_stack; }
|
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
DeclarativeEnvironment();
|
DeclarativeEnvironment();
|
||||||
explicit DeclarativeEnvironment(Environment* parent_environment);
|
explicit DeclarativeEnvironment(Environment* parent_environment);
|
||||||
|
@ -125,7 +125,7 @@ protected:
|
||||||
private:
|
private:
|
||||||
Vector<Binding> m_bindings;
|
Vector<Binding> m_bindings;
|
||||||
HashMap<DeprecatedFlyString, size_t> m_bindings_assoc;
|
HashMap<DeprecatedFlyString, size_t> m_bindings_assoc;
|
||||||
Vector<DisposableResource> m_disposable_resource_stack;
|
DisposeCapability m_dispose_capability;
|
||||||
|
|
||||||
u64 m_environment_serial_number { 0 };
|
u64 m_environment_serial_number { 0 };
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2022, David Tuin <davidot@serenityos.org>
|
* Copyright (c) 2022, David Tuin <davidot@serenityos.org>
|
||||||
|
* Copyright (c) 2025, Tim Flynn <trflynn89@ladybird.org>
|
||||||
*
|
*
|
||||||
* SPDX-License-Identifier: BSD-2-Clause
|
* SPDX-License-Identifier: BSD-2-Clause
|
||||||
*/
|
*/
|
||||||
|
@ -10,19 +11,16 @@ namespace JS {
|
||||||
|
|
||||||
GC_DEFINE_ALLOCATOR(DisposableStack);
|
GC_DEFINE_ALLOCATOR(DisposableStack);
|
||||||
|
|
||||||
DisposableStack::DisposableStack(Vector<DisposableResource> stack, Object& prototype)
|
DisposableStack::DisposableStack(DisposeCapability dispose_capability, Object& prototype)
|
||||||
: Object(ConstructWithPrototypeTag::Tag, prototype)
|
: Object(ConstructWithPrototypeTag::Tag, prototype)
|
||||||
, m_disposable_resource_stack(move(stack))
|
, m_dispose_capability(move(dispose_capability))
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
void DisposableStack::visit_edges(Cell::Visitor& visitor)
|
void DisposableStack::visit_edges(Cell::Visitor& visitor)
|
||||||
{
|
{
|
||||||
Base::visit_edges(visitor);
|
Base::visit_edges(visitor);
|
||||||
for (auto& resource : m_disposable_resource_stack) {
|
m_dispose_capability.visit_edges(visitor);
|
||||||
visitor.visit(resource.resource_value);
|
|
||||||
visitor.visit(resource.dispose_method);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2022, David Tuin <davidot@serenityos.org>
|
* Copyright (c) 2022, David Tuin <davidot@serenityos.org>
|
||||||
|
* Copyright (c) 2025, Tim Flynn <trflynn89@ladybird.org>
|
||||||
*
|
*
|
||||||
* SPDX-License-Identifier: BSD-2-Clause
|
* SPDX-License-Identifier: BSD-2-Clause
|
||||||
*/
|
*/
|
||||||
|
@ -23,19 +24,19 @@ public:
|
||||||
Disposed
|
Disposed
|
||||||
};
|
};
|
||||||
|
|
||||||
[[nodiscard]] DisposableState disposable_state() const { return m_state; }
|
[[nodiscard]] DisposableState disposable_state() const { return m_disposable_state; }
|
||||||
[[nodiscard]] Vector<DisposableResource> const& disposable_resource_stack() const { return m_disposable_resource_stack; }
|
void set_disposed() { m_disposable_state = DisposableState::Disposed; }
|
||||||
[[nodiscard]] Vector<DisposableResource>& disposable_resource_stack() { return m_disposable_resource_stack; }
|
|
||||||
|
|
||||||
void set_disposed() { m_state = DisposableState::Disposed; }
|
[[nodiscard]] DisposeCapability const& dispose_capability() const { return m_dispose_capability; }
|
||||||
|
[[nodiscard]] DisposeCapability& dispose_capability() { return m_dispose_capability; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
DisposableStack(Vector<DisposableResource> stack, Object& prototype);
|
DisposableStack(DisposeCapability, Object& prototype);
|
||||||
|
|
||||||
virtual void visit_edges(Visitor& visitor) override;
|
virtual void visit_edges(Visitor& visitor) override;
|
||||||
|
|
||||||
Vector<DisposableResource> m_disposable_resource_stack;
|
DisposableState m_disposable_state { DisposableState::Pending };
|
||||||
DisposableState m_state { DisposableState::Pending };
|
DisposeCapability m_dispose_capability;
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2022, David Tuin <davidot@serenityos.org>
|
* Copyright (c) 2022, David Tuin <davidot@serenityos.org>
|
||||||
|
* Copyright (c) 2025, Tim Flynn <trflynn89@ladybird.org>
|
||||||
*
|
*
|
||||||
* SPDX-License-Identifier: BSD-2-Clause
|
* SPDX-License-Identifier: BSD-2-Clause
|
||||||
*/
|
*/
|
||||||
|
@ -22,13 +23,13 @@ void DisposableStackConstructor::initialize(Realm& realm)
|
||||||
auto& vm = this->vm();
|
auto& vm = this->vm();
|
||||||
Base::initialize(realm);
|
Base::initialize(realm);
|
||||||
|
|
||||||
// 26.2.2.1 DisposableStack.prototype, https://tc39.es/ecma262/#sec-finalization-registry.prototype
|
// 12.3.2.1 DisposableStack.prototype, https://tc39.es/proposal-explicit-resource-management/#sec-disposablestack.prototype
|
||||||
define_direct_property(vm.names.prototype, realm.intrinsics().disposable_stack_prototype(), 0);
|
define_direct_property(vm.names.prototype, realm.intrinsics().disposable_stack_prototype(), 0);
|
||||||
|
|
||||||
define_direct_property(vm.names.length, Value(0), Attribute::Configurable);
|
define_direct_property(vm.names.length, Value(0), Attribute::Configurable);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 11.3.1.1 DisposableStack ( ), https://tc39.es/proposal-explicit-resource-management/#sec-disposablestack
|
// 12.3.1.1 DisposableStack ( ), https://tc39.es/proposal-explicit-resource-management/#sec-disposablestack
|
||||||
ThrowCompletionOr<Value> DisposableStackConstructor::call()
|
ThrowCompletionOr<Value> DisposableStackConstructor::call()
|
||||||
{
|
{
|
||||||
auto& vm = this->vm();
|
auto& vm = this->vm();
|
||||||
|
@ -37,16 +38,16 @@ ThrowCompletionOr<Value> DisposableStackConstructor::call()
|
||||||
return vm.throw_completion<TypeError>(ErrorType::ConstructorWithoutNew, vm.names.DisposableStack);
|
return vm.throw_completion<TypeError>(ErrorType::ConstructorWithoutNew, vm.names.DisposableStack);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 11.3.1.1 DisposableStack ( ), https://tc39.es/proposal-explicit-resource-management/#sec-disposablestack
|
// 12.3.1.1 DisposableStack ( ), https://tc39.es/proposal-explicit-resource-management/#sec-disposablestack
|
||||||
ThrowCompletionOr<GC::Ref<Object>> DisposableStackConstructor::construct(FunctionObject& new_target)
|
ThrowCompletionOr<GC::Ref<Object>> DisposableStackConstructor::construct(FunctionObject& new_target)
|
||||||
{
|
{
|
||||||
auto& vm = this->vm();
|
auto& vm = this->vm();
|
||||||
|
|
||||||
// 2. Let disposableStack be ? OrdinaryCreateFromConstructor(NewTarget, "%DisposableStack.prototype%", « [[DisposableState]], [[DisposableResourceStack]] »).
|
// 2. Let disposableStack be ? OrdinaryCreateFromConstructor(NewTarget, "%DisposableStack.prototype%", « [[DisposableState]], [[DisposeCapability]] »).
|
||||||
// 3. Set disposableStack.[[DisposableState]] to pending.
|
// 3. Set disposableStack.[[DisposableState]] to pending.
|
||||||
// 4. Set disposableStack.[[DisposableResourceStack]] to a new empty List.
|
// 4. Set disposableStack.[[DisposeCapability]] to NewDisposeCapability().
|
||||||
// 5. Return disposableStack.
|
// 5. Return disposableStack.
|
||||||
return TRY(ordinary_create_from_constructor<DisposableStack>(vm, new_target, &Intrinsics::disposable_stack_prototype, Vector<DisposableResource> {}));
|
return TRY(ordinary_create_from_constructor<DisposableStack>(vm, new_target, &Intrinsics::disposable_stack_prototype, new_dispose_capability()));
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,11 +1,11 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2022, David Tuin <davidot@serenityos.org>
|
* Copyright (c) 2022, David Tuin <davidot@serenityos.org>
|
||||||
|
* Copyright (c) 2025, Tim Flynn <trflynn89@ladybird.org>
|
||||||
*
|
*
|
||||||
* SPDX-License-Identifier: BSD-2-Clause
|
* SPDX-License-Identifier: BSD-2-Clause
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <LibJS/Runtime/AbstractOperations.h>
|
#include <LibJS/Runtime/AbstractOperations.h>
|
||||||
#include <LibJS/Runtime/DisposableStack.h>
|
|
||||||
#include <LibJS/Runtime/DisposableStackConstructor.h>
|
#include <LibJS/Runtime/DisposableStackConstructor.h>
|
||||||
#include <LibJS/Runtime/DisposableStackPrototype.h>
|
#include <LibJS/Runtime/DisposableStackPrototype.h>
|
||||||
#include <LibJS/Runtime/NativeFunction.h>
|
#include <LibJS/Runtime/NativeFunction.h>
|
||||||
|
@ -21,96 +21,26 @@ DisposableStackPrototype::DisposableStackPrototype(Realm& realm)
|
||||||
|
|
||||||
void DisposableStackPrototype::initialize(Realm& realm)
|
void DisposableStackPrototype::initialize(Realm& realm)
|
||||||
{
|
{
|
||||||
auto& vm = this->vm();
|
|
||||||
Base::initialize(realm);
|
Base::initialize(realm);
|
||||||
u8 attr = Attribute::Writable | Attribute::Configurable;
|
|
||||||
|
|
||||||
define_native_accessor(realm, vm.names.disposed, disposed_getter, {}, attr);
|
auto& vm = this->vm();
|
||||||
define_native_function(realm, vm.names.dispose, dispose, 0, attr);
|
|
||||||
define_native_function(realm, vm.names.use, use, 1, attr);
|
u8 attr = Attribute::Writable | Attribute::Configurable;
|
||||||
define_native_function(realm, vm.names.adopt, adopt, 2, attr);
|
define_native_function(realm, vm.names.adopt, adopt, 2, attr);
|
||||||
define_native_function(realm, vm.names.defer, defer, 1, attr);
|
define_native_function(realm, vm.names.defer, defer, 1, attr);
|
||||||
|
define_native_function(realm, vm.names.dispose, dispose, 0, attr);
|
||||||
|
define_native_accessor(realm, vm.names.disposed, disposed_getter, {}, attr);
|
||||||
define_native_function(realm, vm.names.move, move_, 0, attr);
|
define_native_function(realm, vm.names.move, move_, 0, attr);
|
||||||
|
define_native_function(realm, vm.names.use, use, 1, attr);
|
||||||
|
|
||||||
// 11.3.3.7 DisposableStack.prototype [ @@dispose ] (), https://tc39.es/proposal-explicit-resource-management/#sec-disposablestack.prototype-@@dispose
|
// 12.3.3.7 DisposableStack.prototype [ @@dispose ] (), https://tc39.es/proposal-explicit-resource-management/#sec-disposablestack.prototype-@@dispose
|
||||||
define_direct_property(vm.well_known_symbol_dispose(), get_without_side_effects(vm.names.dispose), attr);
|
define_direct_property(vm.well_known_symbol_dispose(), get_without_side_effects(vm.names.dispose), attr);
|
||||||
|
|
||||||
// 11.3.3.8 DisposableStack.prototype [ @@toStringTag ], https://tc39.es/proposal-explicit-resource-management/#sec-disposablestack.prototype-@@toStringTag
|
// 12.3.3.8 DisposableStack.prototype [ @@toStringTag ], https://tc39.es/proposal-explicit-resource-management/#sec-disposablestack.prototype-@@toStringTag
|
||||||
define_direct_property(vm.well_known_symbol_to_string_tag(), PrimitiveString::create(vm, vm.names.DisposableStack.as_string()), Attribute::Configurable);
|
define_direct_property(vm.well_known_symbol_to_string_tag(), PrimitiveString::create(vm, vm.names.DisposableStack.as_string()), Attribute::Configurable);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 11.3.3.1 get DisposableStack.prototype.disposed, https://tc39.es/proposal-explicit-resource-management/#sec-get-disposablestack.prototype.disposed
|
// 12.3.3.1 DisposableStack.prototype.adopt( value, onDispose ), https://tc39.es/proposal-explicit-resource-management/#sec-disposablestack.prototype.adopt
|
||||||
JS_DEFINE_NATIVE_FUNCTION(DisposableStackPrototype::disposed_getter)
|
|
||||||
{
|
|
||||||
// 1. Let disposableStack be the this value.
|
|
||||||
// 2. Perform ? RequireInternalSlot(disposableStack, [[DisposableState]]).
|
|
||||||
auto disposable_stack = TRY(typed_this_object(vm));
|
|
||||||
|
|
||||||
// 3. If disposableStack.[[DisposableState]] is disposed, return true.
|
|
||||||
if (disposable_stack->disposable_state() == DisposableStack::DisposableState::Disposed)
|
|
||||||
return Value(true);
|
|
||||||
|
|
||||||
// 4. Otherwise, return false.
|
|
||||||
return Value(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 11.3.3.2 DisposableStack.prototype.dispose (), https://tc39.es/proposal-explicit-resource-management/#sec-disposablestack.prototype.dispose
|
|
||||||
JS_DEFINE_NATIVE_FUNCTION(DisposableStackPrototype::dispose)
|
|
||||||
{
|
|
||||||
// 1. Let disposableStack be the this value.
|
|
||||||
// 2. Perform ? RequireInternalSlot(disposableStack, [[DisposableState]]).
|
|
||||||
auto disposable_stack = TRY(typed_this_object(vm));
|
|
||||||
|
|
||||||
// 3. If disposableStack.[[DisposableState]] is disposed, return undefined.
|
|
||||||
if (disposable_stack->disposable_state() == DisposableStack::DisposableState::Disposed)
|
|
||||||
return js_undefined();
|
|
||||||
|
|
||||||
// 4. Set disposableStack.[[DisposableState]] to disposed.
|
|
||||||
disposable_stack->set_disposed();
|
|
||||||
|
|
||||||
// 5. Return DisposeResources(disposableStack, NormalCompletion(undefined)).
|
|
||||||
return *TRY(dispose_resources(vm, disposable_stack->disposable_resource_stack(), Completion { js_undefined() }));
|
|
||||||
}
|
|
||||||
|
|
||||||
// 11.3.3.3 DisposableStack.prototype.use( value ), https://tc39.es/proposal-explicit-resource-management/#sec-disposablestack.prototype.use
|
|
||||||
JS_DEFINE_NATIVE_FUNCTION(DisposableStackPrototype::use)
|
|
||||||
{
|
|
||||||
auto value = vm.argument(0);
|
|
||||||
|
|
||||||
// 1. Let disposableStack be the this value.
|
|
||||||
// 2. Perform ? RequireInternalSlot(disposableStack, [[DisposableState]]).
|
|
||||||
auto disposable_stack = TRY(typed_this_object(vm));
|
|
||||||
|
|
||||||
// 3. If disposableStack.[[DisposableState]] is disposed, throw a ReferenceError exception.
|
|
||||||
if (disposable_stack->disposable_state() == DisposableStack::DisposableState::Disposed)
|
|
||||||
return vm.throw_completion<ReferenceError>(ErrorType::DisposableStackAlreadyDisposed);
|
|
||||||
|
|
||||||
// 4. If value is neither null nor undefined, then
|
|
||||||
if (!value.is_nullish()) {
|
|
||||||
// a. If Type(value) is not Object, throw a TypeError exception.
|
|
||||||
if (!value.is_object())
|
|
||||||
return vm.throw_completion<TypeError>(ErrorType::NotAnObject, value.to_string_without_side_effects());
|
|
||||||
|
|
||||||
// FIXME: This should be TRY in the spec
|
|
||||||
// b. Let method be GetDisposeMethod(value, sync-dispose).
|
|
||||||
auto method = TRY(get_dispose_method(vm, value, Environment::InitializeBindingHint::SyncDispose));
|
|
||||||
|
|
||||||
// c. If method is undefined, then
|
|
||||||
if (!method.ptr()) {
|
|
||||||
// i. Throw a TypeError exception.
|
|
||||||
return vm.throw_completion<TypeError>(ErrorType::NoDisposeMethod, value.to_string_without_side_effects());
|
|
||||||
}
|
|
||||||
// d. Else,
|
|
||||||
// i. Perform ? AddDisposableResource(disposableStack, value, sync-dispose, method).
|
|
||||||
// FIXME: Fairly sure this can't fail, see https://github.com/tc39/proposal-explicit-resource-management/pull/142
|
|
||||||
MUST(add_disposable_resource(vm, disposable_stack->disposable_resource_stack(), value, Environment::InitializeBindingHint::SyncDispose, method));
|
|
||||||
}
|
|
||||||
|
|
||||||
// 5. Return value.
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 11.3.3.4 DisposableStack.prototype.adopt( value, onDispose ), https://tc39.es/proposal-explicit-resource-management/#sec-disposablestack.prototype.adopt
|
|
||||||
JS_DEFINE_NATIVE_FUNCTION(DisposableStackPrototype::adopt)
|
JS_DEFINE_NATIVE_FUNCTION(DisposableStackPrototype::adopt)
|
||||||
{
|
{
|
||||||
auto& realm = *vm.current_realm();
|
auto& realm = *vm.current_realm();
|
||||||
|
@ -128,33 +58,25 @@ JS_DEFINE_NATIVE_FUNCTION(DisposableStackPrototype::adopt)
|
||||||
|
|
||||||
// 4. If IsCallable(onDispose) is false, throw a TypeError exception.
|
// 4. If IsCallable(onDispose) is false, throw a TypeError exception.
|
||||||
if (!on_dispose.is_function())
|
if (!on_dispose.is_function())
|
||||||
return vm.throw_completion<TypeError>(ErrorType::NotAFunction, on_dispose.to_string_without_side_effects());
|
return vm.throw_completion<TypeError>(ErrorType::NotAFunction, on_dispose);
|
||||||
|
|
||||||
// 5. Let F be a new built-in function object as defined in 11.3.3.4.1.
|
// 5. Let closure be a new Abstract Closure with no parameters that captures value and onDispose and performs the following steps when called:
|
||||||
// 6. Set F.[[Argument]] to value.
|
auto closure = [value, on_dispose](VM& vm) mutable -> ThrowCompletionOr<Value> {
|
||||||
// 7. Set F.[[OnDisposeCallback]] to onDispose.
|
// a. Return ? Call(onDispose, undefined, « value »).
|
||||||
// 11.3.3.4.1 DisposableStack Adopt Callback Functions, https://tc39.es/proposal-explicit-resource-management/#sec-disposablestack-adopt-callback-functions
|
return TRY(call(vm, on_dispose.as_function(), js_undefined(), value));
|
||||||
// A DisposableStack adopt callback function is an anonymous built-in function object that has [[Argument]] and [[OnDisposeCallback]] internal slots.
|
};
|
||||||
auto function = NativeFunction::create(
|
|
||||||
realm, [argument = make_root(value), callback = make_root(on_dispose)](VM& vm) {
|
|
||||||
// When a DisposableStack adopt callback function is called, the following steps are taken:
|
|
||||||
// 1. Let F be the active function object.
|
|
||||||
// 2. Assert: IsCallable(F.[[OnDisposeCallback]]) is true.
|
|
||||||
VERIFY(callback.value().is_function());
|
|
||||||
|
|
||||||
// 3. Return Call(F.[[OnDisposeCallback]], undefined, « F.[[Argument]] »).
|
// 6. Let F be CreateBuiltinFunction(closure, 0, "", « »).
|
||||||
return call(vm, callback.value(), js_undefined(), argument.value());
|
auto function = NativeFunction::create(realm, move(closure), 0, "");
|
||||||
},
|
|
||||||
0, "");
|
|
||||||
|
|
||||||
// 8. Perform ? AddDisposableResource(disposableStack, undefined, sync-dispose, F).
|
// 7. Perform ? AddDisposableResource(disposableStack.[[DisposeCapability]], undefined, sync-dispose, F).
|
||||||
TRY(add_disposable_resource(vm, disposable_stack->disposable_resource_stack(), js_undefined(), Environment::InitializeBindingHint::SyncDispose, function));
|
TRY(add_disposable_resource(vm, disposable_stack->dispose_capability(), js_undefined(), Environment::InitializeBindingHint::SyncDispose, function));
|
||||||
|
|
||||||
// 9. Return value.
|
// 8. Return value.
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 11.3.3.5 DisposableStack.prototype.defer( onDispose ), https://tc39.es/proposal-explicit-resource-management/#sec-disposablestack.prototype.defer
|
// 12.3.3.2 DisposableStack.prototype.defer( onDispose ), https://tc39.es/proposal-explicit-resource-management/#sec-disposablestack.prototype.defer
|
||||||
JS_DEFINE_NATIVE_FUNCTION(DisposableStackPrototype::defer)
|
JS_DEFINE_NATIVE_FUNCTION(DisposableStackPrototype::defer)
|
||||||
{
|
{
|
||||||
auto on_dispose = vm.argument(0);
|
auto on_dispose = vm.argument(0);
|
||||||
|
@ -169,18 +91,53 @@ JS_DEFINE_NATIVE_FUNCTION(DisposableStackPrototype::defer)
|
||||||
|
|
||||||
// 4. If IsCallable(onDispose) is false, throw a TypeError exception.
|
// 4. If IsCallable(onDispose) is false, throw a TypeError exception.
|
||||||
if (!on_dispose.is_function())
|
if (!on_dispose.is_function())
|
||||||
return vm.throw_completion<TypeError>(ErrorType::NotAFunction, on_dispose.to_string_without_side_effects());
|
return vm.throw_completion<TypeError>(ErrorType::NotAFunction, on_dispose);
|
||||||
|
|
||||||
// 5. Perform ? AddDisposableResource(disposableStack, undefined, sync-dispose, onDispose).
|
// 5. Perform ? AddDisposableResource(disposableStack.[[DisposeCapability]], undefined, sync-dispose, onDispose).
|
||||||
TRY(add_disposable_resource(vm, disposable_stack->disposable_resource_stack(), js_undefined(), Environment::InitializeBindingHint::SyncDispose, &on_dispose.as_function()));
|
TRY(add_disposable_resource(vm, disposable_stack->dispose_capability(), js_undefined(), Environment::InitializeBindingHint::SyncDispose, on_dispose.as_function()));
|
||||||
|
|
||||||
// 6. Return undefined.
|
// 6. Return undefined.
|
||||||
return js_undefined();
|
return js_undefined();
|
||||||
}
|
}
|
||||||
|
|
||||||
// 11.3.3.6 DisposableStack.prototype.move(), https://tc39.es/proposal-explicit-resource-management/#sec-disposablestack.prototype.move
|
// 12.3.3.3 DisposableStack.prototype.dispose (), https://tc39.es/proposal-explicit-resource-management/#sec-disposablestack.prototype.dispose
|
||||||
|
JS_DEFINE_NATIVE_FUNCTION(DisposableStackPrototype::dispose)
|
||||||
|
{
|
||||||
|
// 1. Let disposableStack be the this value.
|
||||||
|
// 2. Perform ? RequireInternalSlot(disposableStack, [[DisposableState]]).
|
||||||
|
auto disposable_stack = TRY(typed_this_object(vm));
|
||||||
|
|
||||||
|
// 3. If disposableStack.[[DisposableState]] is disposed, return undefined.
|
||||||
|
if (disposable_stack->disposable_state() == DisposableStack::DisposableState::Disposed)
|
||||||
|
return js_undefined();
|
||||||
|
|
||||||
|
// 4. Set disposableStack.[[DisposableState]] to disposed.
|
||||||
|
disposable_stack->set_disposed();
|
||||||
|
|
||||||
|
// 5. Return DisposeResources(disposableStack.[[DisposeCapability]], NormalCompletion(undefined)).
|
||||||
|
return *TRY(dispose_resources(vm, disposable_stack->dispose_capability(), normal_completion(js_undefined())));
|
||||||
|
}
|
||||||
|
|
||||||
|
// 12.3.3.4 get DisposableStack.prototype.disposed, https://tc39.es/proposal-explicit-resource-management/#sec-get-disposablestack.prototype.disposed
|
||||||
|
JS_DEFINE_NATIVE_FUNCTION(DisposableStackPrototype::disposed_getter)
|
||||||
|
{
|
||||||
|
// 1. Let disposableStack be the this value.
|
||||||
|
// 2. Perform ? RequireInternalSlot(disposableStack, [[DisposableState]]).
|
||||||
|
auto disposable_stack = TRY(typed_this_object(vm));
|
||||||
|
|
||||||
|
// 3. If disposableStack.[[DisposableState]] is disposed, return true.
|
||||||
|
if (disposable_stack->disposable_state() == DisposableStack::DisposableState::Disposed)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
// 4. Otherwise, return false.
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 12.3.3.5 DisposableStack.prototype.move(), https://tc39.es/proposal-explicit-resource-management/#sec-disposablestack.prototype.move
|
||||||
JS_DEFINE_NATIVE_FUNCTION(DisposableStackPrototype::move_)
|
JS_DEFINE_NATIVE_FUNCTION(DisposableStackPrototype::move_)
|
||||||
{
|
{
|
||||||
|
auto& realm = *vm.current_realm();
|
||||||
|
|
||||||
// 1. Let disposableStack be the this value.
|
// 1. Let disposableStack be the this value.
|
||||||
// 2. Perform ? RequireInternalSlot(disposableStack, [[DisposableState]]).
|
// 2. Perform ? RequireInternalSlot(disposableStack, [[DisposableState]]).
|
||||||
auto disposable_stack = TRY(typed_this_object(vm));
|
auto disposable_stack = TRY(typed_this_object(vm));
|
||||||
|
@ -189,15 +146,13 @@ JS_DEFINE_NATIVE_FUNCTION(DisposableStackPrototype::move_)
|
||||||
if (disposable_stack->disposable_state() == DisposableStack::DisposableState::Disposed)
|
if (disposable_stack->disposable_state() == DisposableStack::DisposableState::Disposed)
|
||||||
return vm.throw_completion<ReferenceError>(ErrorType::DisposableStackAlreadyDisposed);
|
return vm.throw_completion<ReferenceError>(ErrorType::DisposableStackAlreadyDisposed);
|
||||||
|
|
||||||
// 4. Let newDisposableStack be ? OrdinaryCreateFromConstructor(%DisposableStack%, "%DisposableStack.prototype%", « [[DisposableState]], [[DisposableResourceStack]] »).
|
// 4. Let newDisposableStack be ? OrdinaryCreateFromConstructor(%DisposableStack%, "%DisposableStack.prototype%", « [[DisposableState]], [[DisposeCapability]] »).
|
||||||
auto new_disposable_stack = TRY(ordinary_create_from_constructor<DisposableStack>(vm, *vm.current_realm()->intrinsics().disposable_stack_constructor(), &Intrinsics::disposable_stack_prototype, disposable_stack->disposable_resource_stack()));
|
|
||||||
|
|
||||||
// 5. Set newDisposableStack.[[DisposableState]] to pending.
|
// 5. Set newDisposableStack.[[DisposableState]] to pending.
|
||||||
// 6. Set newDisposableStack.[[DisposableResourceStack]] to disposableStack.[[DisposableResourceStack]].
|
// 6. Set newDisposableStack.[[DisposeCapability]] to disposableStack.[[DisposeCapability]].
|
||||||
// NOTE: Already done in the constructor
|
auto new_disposable_stack = TRY(ordinary_create_from_constructor<DisposableStack>(vm, realm.intrinsics().disposable_stack_constructor(), &Intrinsics::disposable_stack_prototype, move(disposable_stack->dispose_capability())));
|
||||||
|
|
||||||
// 7. Set disposableStack.[[DisposableResourceStack]] to a new empty List.
|
// 7. Set disposableStack.[[DisposeCapability]] to NewDisposeCapability().
|
||||||
disposable_stack->disposable_resource_stack().clear();
|
disposable_stack->dispose_capability() = new_dispose_capability();
|
||||||
|
|
||||||
// 8. Set disposableStack.[[DisposableState]] to disposed.
|
// 8. Set disposableStack.[[DisposableState]] to disposed.
|
||||||
disposable_stack->set_disposed();
|
disposable_stack->set_disposed();
|
||||||
|
@ -206,4 +161,24 @@ JS_DEFINE_NATIVE_FUNCTION(DisposableStackPrototype::move_)
|
||||||
return new_disposable_stack;
|
return new_disposable_stack;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 12.3.3.6 DisposableStack.prototype.use( value ), https://tc39.es/proposal-explicit-resource-management/#sec-disposablestack.prototype.use
|
||||||
|
JS_DEFINE_NATIVE_FUNCTION(DisposableStackPrototype::use)
|
||||||
|
{
|
||||||
|
auto value = vm.argument(0);
|
||||||
|
|
||||||
|
// 1. Let disposableStack be the this value.
|
||||||
|
// 2. Perform ? RequireInternalSlot(disposableStack, [[DisposableState]]).
|
||||||
|
auto disposable_stack = TRY(typed_this_object(vm));
|
||||||
|
|
||||||
|
// 3. If disposableStack.[[DisposableState]] is disposed, throw a ReferenceError exception.
|
||||||
|
if (disposable_stack->disposable_state() == DisposableStack::DisposableState::Disposed)
|
||||||
|
return vm.throw_completion<ReferenceError>(ErrorType::DisposableStackAlreadyDisposed);
|
||||||
|
|
||||||
|
// 4. Perform ? AddDisposableResource(disposableStack.[[DisposeCapability]], value, sync-dispose).
|
||||||
|
TRY(add_disposable_resource(vm, disposable_stack->dispose_capability(), value, Environment::InitializeBindingHint::SyncDispose));
|
||||||
|
|
||||||
|
// 5. Return value.
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,12 +1,13 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2022, David Tuin <davidot@serenityos.org>
|
* Copyright (c) 2022, David Tuin <davidot@serenityos.org>
|
||||||
|
* Copyright (c) 2025, Tim Flynn <trflynn89@ladybird.org>
|
||||||
*
|
*
|
||||||
* SPDX-License-Identifier: BSD-2-Clause
|
* SPDX-License-Identifier: BSD-2-Clause
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <LibJS/Runtime/FinalizationRegistry.h>
|
#include <LibJS/Runtime/DisposableStack.h>
|
||||||
#include <LibJS/Runtime/PrototypeObject.h>
|
#include <LibJS/Runtime/PrototypeObject.h>
|
||||||
|
|
||||||
namespace JS {
|
namespace JS {
|
||||||
|
@ -22,12 +23,12 @@ public:
|
||||||
private:
|
private:
|
||||||
explicit DisposableStackPrototype(Realm&);
|
explicit DisposableStackPrototype(Realm&);
|
||||||
|
|
||||||
JS_DECLARE_NATIVE_FUNCTION(disposed_getter);
|
|
||||||
JS_DECLARE_NATIVE_FUNCTION(dispose);
|
|
||||||
JS_DECLARE_NATIVE_FUNCTION(use);
|
|
||||||
JS_DECLARE_NATIVE_FUNCTION(adopt);
|
JS_DECLARE_NATIVE_FUNCTION(adopt);
|
||||||
JS_DECLARE_NATIVE_FUNCTION(defer);
|
JS_DECLARE_NATIVE_FUNCTION(defer);
|
||||||
|
JS_DECLARE_NATIVE_FUNCTION(dispose);
|
||||||
|
JS_DECLARE_NATIVE_FUNCTION(disposed_getter);
|
||||||
JS_DECLARE_NATIVE_FUNCTION(move_);
|
JS_DECLARE_NATIVE_FUNCTION(move_);
|
||||||
|
JS_DECLARE_NATIVE_FUNCTION(use);
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -26,6 +26,7 @@ public:
|
||||||
enum class InitializeBindingHint {
|
enum class InitializeBindingHint {
|
||||||
Normal,
|
Normal,
|
||||||
SyncDispose,
|
SyncDispose,
|
||||||
|
AsyncDispose,
|
||||||
};
|
};
|
||||||
|
|
||||||
virtual bool has_this_binding() const { return false; }
|
virtual bool has_this_binding() const { return false; }
|
||||||
|
|
|
@ -671,6 +671,7 @@ ThrowCompletionOr<ResolvedBinding> SourceTextModule::resolve_export(VM& vm, Depr
|
||||||
}
|
}
|
||||||
|
|
||||||
// 16.2.1.6.5 ExecuteModule ( [ capability ] ), https://tc39.es/ecma262/#sec-source-text-module-record-execute-module
|
// 16.2.1.6.5 ExecuteModule ( [ capability ] ), https://tc39.es/ecma262/#sec-source-text-module-record-execute-module
|
||||||
|
// 9.1.1.1.2 ExecuteModule ( [ capability ] ), https://tc39.es/proposal-explicit-resource-management/#sec-source-text-module-record-execute-module
|
||||||
ThrowCompletionOr<void> SourceTextModule::execute_module(VM& vm, GC::Ptr<PromiseCapability> capability)
|
ThrowCompletionOr<void> SourceTextModule::execute_module(VM& vm, GC::Ptr<PromiseCapability> capability)
|
||||||
{
|
{
|
||||||
dbgln_if(JS_MODULE_DEBUG, "[JS MODULE] SourceTextModule::execute_module({}, PromiseCapability @ {})", filename(), capability.ptr());
|
dbgln_if(JS_MODULE_DEBUG, "[JS MODULE] SourceTextModule::execute_module({}, PromiseCapability @ {})", filename(), capability.ptr());
|
||||||
|
@ -708,6 +709,7 @@ ThrowCompletionOr<void> SourceTextModule::execute_module(VM& vm, GC::Ptr<Promise
|
||||||
if (!m_has_top_level_await) {
|
if (!m_has_top_level_await) {
|
||||||
// a. Assert: capability is not present.
|
// a. Assert: capability is not present.
|
||||||
VERIFY(capability == nullptr);
|
VERIFY(capability == nullptr);
|
||||||
|
|
||||||
// b. Push moduleContext onto the execution context stack; moduleContext is now the running execution context.
|
// b. Push moduleContext onto the execution context stack; moduleContext is now the running execution context.
|
||||||
TRY(vm.push_execution_context(*module_context, {}));
|
TRY(vm.push_execution_context(*module_context, {}));
|
||||||
|
|
||||||
|
@ -730,11 +732,10 @@ ThrowCompletionOr<void> SourceTextModule::execute_module(VM& vm, GC::Ptr<Promise
|
||||||
}
|
}
|
||||||
|
|
||||||
// d. Let env be moduleContext's LexicalEnvironment.
|
// d. Let env be moduleContext's LexicalEnvironment.
|
||||||
auto env = module_context->lexical_environment;
|
auto& env = verify_cast<DeclarativeEnvironment>(*module_context->lexical_environment);
|
||||||
VERIFY(is<DeclarativeEnvironment>(*env));
|
|
||||||
|
|
||||||
// e. Set result to DisposeResources(env, result).
|
// e. Set result to Completion(DisposeResources(env.[[DisposeCapability]], result)).
|
||||||
result = dispose_resources(vm, static_cast<DeclarativeEnvironment*>(env.ptr()), result);
|
result = dispose_resources(vm, env.dispose_capability(), result);
|
||||||
|
|
||||||
// f. Suspend moduleContext and remove it from the execution context stack.
|
// f. Suspend moduleContext and remove it from the execution context stack.
|
||||||
vm.pop_execution_context();
|
vm.pop_execution_context();
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue