| // Copyright 2024 the V8 project authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #ifndef V8_OBJECTS_JS_DISPOSABLE_STACK_INL_H_ |
| #define V8_OBJECTS_JS_DISPOSABLE_STACK_INL_H_ |
| |
| #include "src/execution/isolate.h" |
| #include "src/handles/handles.h" |
| #include "src/handles/maybe-handles.h" |
| #include "src/heap/factory.h" |
| #include "src/objects/fixed-array-inl.h" |
| #include "src/objects/heap-object.h" |
| #include "src/objects/js-disposable-stack.h" |
| #include "src/objects/objects-inl.h" |
| #include "src/objects/objects.h" |
| |
| // Has to be the last include (doesn't have include guards): |
| #include "src/objects/object-macros.h" |
| |
| namespace v8 { |
| namespace internal { |
| |
| #include "torque-generated/src/objects/js-disposable-stack-tq-inl.inc" |
| |
| TQ_OBJECT_CONSTRUCTORS_IMPL(JSDisposableStackBase) |
| TQ_OBJECT_CONSTRUCTORS_IMPL(JSSyncDisposableStack) |
| TQ_OBJECT_CONSTRUCTORS_IMPL(JSAsyncDisposableStack) |
| |
| BIT_FIELD_ACCESSORS(JSDisposableStackBase, status, state, |
| JSDisposableStackBase::StateBit) |
| BIT_FIELD_ACCESSORS(JSDisposableStackBase, status, needs_await, |
| JSDisposableStackBase::NeedsAwaitBit) |
| BIT_FIELD_ACCESSORS(JSDisposableStackBase, status, has_awaited, |
| JSDisposableStackBase::HasAwaitedBit) |
| BIT_FIELD_ACCESSORS(JSDisposableStackBase, status, suppressed_error_created, |
| JSDisposableStackBase::SuppressedErrorCreatedBit) |
| BIT_FIELD_ACCESSORS(JSDisposableStackBase, status, length, |
| JSDisposableStackBase::LengthBits) |
| |
| inline void JSDisposableStackBase::Add( |
| Isolate* isolate, DirectHandle<JSDisposableStackBase> disposable_stack, |
| DirectHandle<Object> value, DirectHandle<Object> method, |
| DisposeMethodCallType type, DisposeMethodHint hint) { |
| DCHECK(!IsUndefined(disposable_stack->stack())); |
| int length = disposable_stack->length(); |
| int stack_type = |
| DisposeCallTypeBit::encode(type) | DisposeHintBit::encode(hint); |
| DirectHandle<Smi> stack_type_handle(Smi::FromInt(stack_type), isolate); |
| |
| Handle<FixedArray> array(disposable_stack->stack(), isolate); |
| array = FixedArray::SetAndGrow(isolate, array, length++, value); |
| array = FixedArray::SetAndGrow(isolate, array, length++, method); |
| array = FixedArray::SetAndGrow(isolate, array, length++, stack_type_handle); |
| |
| disposable_stack->set_length(length); |
| disposable_stack->set_stack(*array); |
| } |
| |
| // part of |
| // https://arai-a.github.io/ecma262-compare/?pr=3000&id=sec-createdisposableresource |
| inline MaybeDirectHandle<Object> |
| JSDisposableStackBase::CheckValueAndGetDisposeMethod(Isolate* isolate, |
| DirectHandle<JSAny> value, |
| DisposeMethodHint hint) { |
| DirectHandle<Object> method; |
| if (hint == DisposeMethodHint::kSyncDispose) { |
| // 1. If method is not present, then |
| // a. If V is either null or undefined, then |
| // i. Set V to undefined. |
| // ii. Set method to undefined. |
| // We has already returned from the caller if V is null or undefined, when |
| // hint is `kSyncDispose`. |
| DCHECK(!IsNullOrUndefined(*value)); |
| |
| // b. Else, |
| // i. If V is not an Object, throw a TypeError exception. |
| if (!IsJSReceiver(*value)) { |
| THROW_NEW_ERROR(isolate, |
| NewTypeError(MessageTemplate::kExpectAnObjectWithUsing)); |
| } |
| |
| // ii. Set method to ? GetDisposeMethod(V, hint). |
| ASSIGN_RETURN_ON_EXCEPTION( |
| isolate, method, |
| Object::GetProperty(isolate, value, |
| isolate->factory()->dispose_symbol())); |
| // (GetMethod)3. If IsCallable(func) is false, throw a TypeError |
| // exception. |
| if (!IsJSFunction(*method)) { |
| THROW_NEW_ERROR(isolate, |
| NewTypeError(MessageTemplate::kNotCallable, |
| isolate->factory()->dispose_symbol())); |
| } |
| |
| // iii. If method is undefined, throw a TypeError exception. |
| // It is already checked in step ii. |
| |
| } else if (hint == DisposeMethodHint::kAsyncDispose) { |
| // 1. If method is not present, then |
| // a. If V is either null or undefined, then |
| // i. Set V to undefined. |
| // ii. Set method to undefined. |
| if (IsNullOrUndefined(*value)) { |
| return isolate->factory()->undefined_value(); |
| } |
| |
| // b. Else, |
| // i. If V is not an Object, throw a TypeError exception. |
| if (!IsJSReceiver(*value)) { |
| THROW_NEW_ERROR(isolate, |
| NewTypeError(MessageTemplate::kExpectAnObjectWithUsing)); |
| } |
| // https://tc39.es/proposal-explicit-resource-management/#sec-getdisposemethod |
| // 1. If hint is async-dispose, then |
| // a. Let method be ? GetMethod(V, @@asyncDispose). |
| ASSIGN_RETURN_ON_EXCEPTION( |
| isolate, method, |
| Object::GetProperty(isolate, value, |
| isolate->factory()->async_dispose_symbol())); |
| // b. If method is undefined, then |
| if (IsUndefined(*method)) { |
| // i. Set method to ? GetMethod(V, @@dispose). |
| ASSIGN_RETURN_ON_EXCEPTION( |
| isolate, method, |
| Object::GetProperty(isolate, value, |
| isolate->factory()->dispose_symbol())); |
| // (GetMethod)3. If IsCallable(func) is false, throw a TypeError |
| // exception. |
| if (!IsJSFunction(*method)) { |
| THROW_NEW_ERROR(isolate, |
| NewTypeError(MessageTemplate::kNotCallable, |
| isolate->factory()->dispose_symbol())); |
| } |
| // ii. If method is not undefined, then |
| if (!IsUndefined(*method)) { |
| // 1. Let closure be a new Abstract Closure with no parameters that |
| // captures method and performs the following steps when called: |
| // a. Let O be the this value. |
| // b. Let promiseCapability be ! NewPromiseCapability(%Promise%). |
| // c. Let result be Completion(Call(method, O)). |
| // d. IfAbruptRejectPromise(result, promiseCapability). |
| // e. Perform ? Call(promiseCapability.[[Resolve]], undefined, « |
| // undefined »). |
| // f. Return promiseCapability.[[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, "", « »). |
| |
| // (TODO:rezvan): Add `kAsyncFromSyncDispose` to the `DisposeMethodHint` |
| // enum and remove the following allocation of adapter closure. |
| DirectHandle<Context> async_dispose_from_sync_dispose_context = |
| isolate->factory()->NewBuiltinContext( |
| isolate->native_context(), |
| static_cast<int>( |
| AsyncDisposeFromSyncDisposeContextSlots::kLength)); |
| async_dispose_from_sync_dispose_context->set( |
| static_cast<int>(AsyncDisposeFromSyncDisposeContextSlots::kMethod), |
| *method); |
| |
| method = |
| Factory::JSFunctionBuilder{ |
| isolate, |
| isolate->factory() |
| ->async_dispose_from_sync_dispose_shared_fun(), |
| async_dispose_from_sync_dispose_context} |
| .Build(); |
| } |
| } |
| // (GetMethod)3. If IsCallable(func) is false, throw a TypeError |
| // exception. |
| if (!IsJSFunction(*method)) { |
| THROW_NEW_ERROR(isolate, |
| NewTypeError(MessageTemplate::kNotCallable, |
| isolate->factory()->async_dispose_symbol())); |
| } |
| } |
| return method; |
| } |
| |
| inline void JSDisposableStackBase::HandleErrorInDisposal( |
| Isolate* isolate, DirectHandle<JSDisposableStackBase> disposable_stack, |
| Handle<Object> current_error, DirectHandle<Object> current_error_message) { |
| DCHECK(isolate->is_catchable_by_javascript(*current_error)); |
| |
| Handle<Object> maybe_error(disposable_stack->error(), isolate); |
| |
| // i. If completion is a throw completion, then |
| if (!IsUninitialized(*maybe_error)) { |
| // 1. Set result to result.[[Value]]. |
| // 2. Let suppressed be completion.[[Value]]. |
| // 3. Let error be a newly created SuppressedError object. |
| // 4. Perform CreateNonEnumerableDataPropertyOrThrow(error, "error", |
| // result). |
| // 5. Perform CreateNonEnumerableDataPropertyOrThrow(error, |
| // "suppressed", suppressed). |
| // 6. Set completion to ThrowCompletion(error). |
| maybe_error = isolate->factory()->NewSuppressedErrorAtDisposal( |
| isolate, current_error, maybe_error); |
| disposable_stack->set_suppressed_error_created(true); |
| |
| } else { |
| // ii. Else, |
| // 1. Set completion to result. |
| maybe_error = current_error; |
| } |
| |
| disposable_stack->set_error(*maybe_error); |
| disposable_stack->set_error_message(*current_error_message); |
| } |
| |
| } // namespace internal |
| } // namespace v8 |
| |
| #include "src/objects/object-macros-undef.h" |
| |
| #endif // V8_OBJECTS_JS_DISPOSABLE_STACK_INL_H_ |