mirror of
https://github.com/zebrajr/node.git
synced 2025-12-06 12:20:27 +01:00
Original commit message:
[explicit-resource-management] Clear isolate internal exception
This CL clears the isolate internal exception before rejecting
the promise.
Bug: 418103036
Change-Id: If3748f7fc4b79b7b5be5009b9fff0d0267541a6f
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/6594252
Reviewed-by: Shu-yu Guo <syg@chromium.org>
Commit-Queue: Rezvan Mahdavi Hezaveh <rezvan@chromium.org>
Cr-Commit-Position: refs/heads/main@{#100532}
Refs: 1d71595801
PR-URL: https://github.com/nodejs/node/pull/58749
Reviewed-By: Richard Lau <richard.lau@ibm.com>
Reviewed-By: Luigi Pinca <luigipinca@gmail.com>
Reviewed-By: Chengzhong Wu <legendecas@gmail.com>
Reviewed-By: Rafael Gonzaga <rafael.nunu@hotmail.com>
Reviewed-By: James M Snell <jasnell@gmail.com>
408 lines
16 KiB
C++
408 lines
16 KiB
C++
// 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.
|
|
|
|
#include "src/api/api.h"
|
|
#include "src/base/logging.h"
|
|
#include "src/base/macros.h"
|
|
#include "src/builtins/builtins-utils-inl.h"
|
|
#include "src/builtins/builtins.h"
|
|
#include "src/execution/isolate.h"
|
|
#include "src/handles/maybe-handles.h"
|
|
#include "src/objects/heap-object.h"
|
|
#include "src/objects/js-disposable-stack-inl.h"
|
|
#include "src/objects/js-disposable-stack.h"
|
|
#include "src/objects/js-objects.h"
|
|
#include "src/objects/js-promise-inl.h"
|
|
#include "src/objects/js-promise.h"
|
|
#include "src/objects/objects.h"
|
|
#include "src/roots/roots.h"
|
|
|
|
namespace v8 {
|
|
namespace internal {
|
|
|
|
BUILTIN(AsyncDisposableStackOnFulfilled) {
|
|
HandleScope scope(isolate);
|
|
|
|
DirectHandle<JSDisposableStackBase> stack(
|
|
Cast<
|
|
JSDisposableStackBase>(isolate->context()->GetNoCell(static_cast<int>(
|
|
JSDisposableStackBase::AsyncDisposableStackContextSlots::kStack))),
|
|
isolate);
|
|
DirectHandle<JSPromise> promise(
|
|
Cast<JSPromise>(isolate->context()->GetNoCell(static_cast<int>(
|
|
JSDisposableStackBase::AsyncDisposableStackContextSlots::
|
|
kOuterPromise))),
|
|
isolate);
|
|
|
|
MAYBE_RETURN(JSAsyncDisposableStack::NextDisposeAsyncIteration(isolate, stack,
|
|
promise),
|
|
ReadOnlyRoots(isolate).exception());
|
|
return ReadOnlyRoots(isolate).undefined_value();
|
|
}
|
|
|
|
BUILTIN(AsyncDisposableStackOnRejected) {
|
|
HandleScope scope(isolate);
|
|
|
|
DirectHandle<JSDisposableStackBase> stack(
|
|
Cast<
|
|
JSDisposableStackBase>(isolate->context()->GetNoCell(static_cast<int>(
|
|
JSDisposableStackBase::AsyncDisposableStackContextSlots::kStack))),
|
|
isolate);
|
|
DirectHandle<JSPromise> promise(
|
|
Cast<JSPromise>(isolate->context()->GetNoCell(static_cast<int>(
|
|
JSDisposableStackBase::AsyncDisposableStackContextSlots::
|
|
kOuterPromise))),
|
|
isolate);
|
|
|
|
DirectHandle<Object> rejection_error = args.at(1);
|
|
// (TODO:rezvan): Pass the correct pending message.
|
|
DirectHandle<Object> message(isolate->pending_message(), isolate);
|
|
DCHECK(isolate->is_catchable_by_javascript(*rejection_error));
|
|
JSDisposableStackBase::HandleErrorInDisposal(isolate, stack, rejection_error,
|
|
message);
|
|
|
|
MAYBE_RETURN(JSAsyncDisposableStack::NextDisposeAsyncIteration(isolate, stack,
|
|
promise),
|
|
ReadOnlyRoots(isolate).exception());
|
|
return ReadOnlyRoots(isolate).undefined_value();
|
|
}
|
|
|
|
// Part of
|
|
// https://tc39.es/proposal-explicit-resource-management/#sec-getdisposemethod
|
|
BUILTIN(AsyncDisposeFromSyncDispose) {
|
|
HandleScope scope(isolate);
|
|
// 1. If hint is async-dispose
|
|
// b. If GetMethod(V, @@asyncDispose) is undefined,
|
|
// i. If GetMethod(V, @@dispose) is not undefined, then
|
|
// 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%).
|
|
DirectHandle<Object> receiver = args.receiver();
|
|
DirectHandle<JSPromise> promise = isolate->factory()->NewJSPromise();
|
|
|
|
// c. Let result be Completion(Call(method, O)).
|
|
DirectHandle<JSFunction> sync_method(
|
|
Cast<JSFunction>(isolate->context()->GetNoCell(static_cast<int>(
|
|
JSDisposableStackBase::AsyncDisposeFromSyncDisposeContextSlots::
|
|
kMethod))),
|
|
isolate);
|
|
|
|
MaybeDirectHandle<Object> result =
|
|
Execution::Call(isolate, sync_method, receiver, {});
|
|
|
|
if (!result.is_null()) {
|
|
// e. Perform ? Call(promiseCapability.[[Resolve]], undefined, «
|
|
// undefined »).
|
|
JSPromise::Resolve(promise, isolate->factory()->undefined_value())
|
|
.ToHandleChecked();
|
|
} else {
|
|
Tagged<Object> exception = isolate->exception();
|
|
if (!isolate->is_catchable_by_javascript(exception)) {
|
|
return {};
|
|
}
|
|
// d. IfAbruptRejectPromise(result, promiseCapability).
|
|
isolate->clear_internal_exception();
|
|
isolate->clear_pending_message();
|
|
JSPromise::Reject(promise, direct_handle(exception, isolate));
|
|
}
|
|
|
|
// f. Return promiseCapability.[[Promise]].
|
|
return *promise;
|
|
}
|
|
|
|
// https://tc39.es/proposal-explicit-resource-management/#sec-asyncdisposablestack
|
|
BUILTIN(AsyncDisposableStackConstructor) {
|
|
const char kMethodName[] = "AsyncDisposableStack";
|
|
HandleScope scope(isolate);
|
|
isolate->CountUsage(v8::Isolate::kExplicitResourceManagement);
|
|
|
|
// 1. If NewTarget is undefined, throw a TypeError exception.
|
|
if (!IsJSReceiver(*args.new_target(), isolate)) {
|
|
THROW_NEW_ERROR_RETURN_FAILURE(
|
|
isolate, NewTypeError(MessageTemplate::kConstructorNotFunction,
|
|
isolate->factory()->NewStringFromAsciiChecked(
|
|
kMethodName)));
|
|
}
|
|
|
|
// 2. Let asyncDisposableStack be ? OrdinaryCreateFromConstructor(NewTarget,
|
|
// "%AsyncDisposableStack.prototype%", « [[AsyncDisposableState]],
|
|
// [[DisposeCapability]] »).
|
|
DirectHandle<Map> map;
|
|
DirectHandle<JSFunction> target = args.target();
|
|
DirectHandle<JSReceiver> new_target = Cast<JSReceiver>(args.new_target());
|
|
|
|
DCHECK_EQ(*target,
|
|
target->native_context()->js_async_disposable_stack_function());
|
|
|
|
ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
|
|
isolate, map, JSFunction::GetDerivedMap(isolate, target, new_target));
|
|
|
|
DirectHandle<JSAsyncDisposableStack> async_disposable_stack =
|
|
isolate->factory()->NewJSAsyncDisposableStack(map);
|
|
// 3. Set asyncDisposableStack.[[AsyncDisposableState]] to pending.
|
|
// 4. Set asyncDisposableStack.[[DisposeCapability]] to
|
|
// NewDisposeCapability().
|
|
JSDisposableStackBase::InitializeJSDisposableStackBase(
|
|
isolate, async_disposable_stack);
|
|
// 5. Return asyncDisposableStack.
|
|
return *async_disposable_stack;
|
|
}
|
|
|
|
// https://tc39.es/proposal-explicit-resource-management/#sec-asyncdisposablestack.prototype.use
|
|
BUILTIN(AsyncDisposableStackPrototypeUse) {
|
|
const char kMethodName[] = "AsyncDisposableStack.prototype.use";
|
|
HandleScope scope(isolate);
|
|
|
|
// 1. Let asyncDisposableStack be the this value.
|
|
// 2. Perform ? RequireInternalSlot(asyncDisposableStack,
|
|
// [[AsyncDisposableState]]).
|
|
CHECK_RECEIVER(JSAsyncDisposableStack, async_disposable_stack, kMethodName);
|
|
DirectHandle<JSAny> value = args.at<JSAny>(1);
|
|
|
|
// 3. If asyncDisposableStack.[[AsyncDisposableState]] is disposed, throw a
|
|
// ReferenceError exception.
|
|
if (async_disposable_stack->state() == DisposableStackState::kDisposed) {
|
|
THROW_NEW_ERROR_RETURN_FAILURE(
|
|
isolate,
|
|
NewReferenceError(
|
|
MessageTemplate::kDisposableStackIsDisposed,
|
|
isolate->factory()->NewStringFromAsciiChecked(kMethodName)));
|
|
}
|
|
|
|
// 4. Perform ?
|
|
// AddDisposableResource(asyncDisposableStack.[[DisposeCapability]],
|
|
// value, async-dispose).
|
|
DirectHandle<Object> method;
|
|
ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
|
|
isolate, method,
|
|
JSDisposableStackBase::CheckValueAndGetDisposeMethod(
|
|
isolate, value, DisposeMethodHint::kAsyncDispose));
|
|
|
|
JSDisposableStackBase::Add(
|
|
isolate, async_disposable_stack,
|
|
(IsNullOrUndefined(*value) ? isolate->factory()->undefined_value()
|
|
: value),
|
|
method, DisposeMethodCallType::kValueIsReceiver,
|
|
DisposeMethodHint::kAsyncDispose);
|
|
|
|
// 5. Return value.
|
|
return *value;
|
|
}
|
|
|
|
// https://tc39.es/proposal-explicit-resource-management/#sec-asyncdisposablestack.prototype.disposeAsync
|
|
BUILTIN(AsyncDisposableStackPrototypeDisposeAsync) {
|
|
HandleScope scope(isolate);
|
|
|
|
// 1. Let asyncDisposableStack be the this value.
|
|
DirectHandle<Object> receiver = args.receiver();
|
|
|
|
// 2. Let promiseCapability be ! NewPromiseCapability(%Promise%).
|
|
DirectHandle<JSPromise> promise = isolate->factory()->NewJSPromise();
|
|
|
|
// 3. If asyncDisposableStack does not have an [[AsyncDisposableState]]
|
|
// internal slot, then
|
|
if (!IsJSAsyncDisposableStack(*receiver)) {
|
|
// a. Perform ! Call(promiseCapability.[[Reject]], undefined, « a newly
|
|
// created TypeError object »).
|
|
JSPromise::Reject(promise,
|
|
isolate->factory()->NewTypeError(
|
|
MessageTemplate::kNotAnAsyncDisposableStack));
|
|
// b. Return promiseCapability.[[Promise]].
|
|
return *promise;
|
|
}
|
|
|
|
DirectHandle<JSAsyncDisposableStack> async_disposable_stack =
|
|
Cast<JSAsyncDisposableStack>(receiver);
|
|
|
|
// 4. If asyncDisposableStack.[[AsyncDisposableState]] is disposed, then
|
|
if (async_disposable_stack->state() == DisposableStackState::kDisposed) {
|
|
// a. Perform ! Call(promiseCapability.[[Resolve]], undefined, «
|
|
// undefined »).
|
|
JSPromise::Resolve(
|
|
promise,
|
|
direct_handle(ReadOnlyRoots(isolate).undefined_value(), isolate))
|
|
.ToHandleChecked();
|
|
// b. Return promiseCapability.[[Promise]].
|
|
return *promise;
|
|
}
|
|
|
|
// 5. Set asyncDisposableStack.[[AsyncDisposableState]] to disposed.
|
|
async_disposable_stack->set_state(DisposableStackState::kDisposed);
|
|
|
|
// 6. Let result be
|
|
// DisposeResources(asyncDisposableStack.[[DisposeCapability]],
|
|
// NormalCompletion(undefined)).
|
|
// 7. IfAbruptRejectPromise(result, promiseCapability).
|
|
// 8. Perform ! Call(promiseCapability.[[Resolve]], undefined, « result
|
|
// »).
|
|
// 9. Return promiseCapability.[[Promise]].
|
|
MAYBE_RETURN(JSAsyncDisposableStack::NextDisposeAsyncIteration(
|
|
isolate, async_disposable_stack, promise),
|
|
ReadOnlyRoots(isolate).exception());
|
|
return *promise;
|
|
}
|
|
|
|
// https://tc39.es/proposal-explicit-resource-management/#sec-get-asyncdisposablestack.prototype.disposed
|
|
BUILTIN(AsyncDisposableStackPrototypeGetDisposed) {
|
|
const char kMethodName[] = "get AsyncDisposableStack.prototype.disposed";
|
|
HandleScope scope(isolate);
|
|
|
|
// 1. Let AsyncdisposableStack be the this value.
|
|
// 2. Perform ? RequireInternalSlot(asyncDisposableStack,
|
|
// [[AsyncDisposableState]]).
|
|
CHECK_RECEIVER(JSAsyncDisposableStack, async_disposable_stack, kMethodName);
|
|
|
|
// 3. If AsyncdisposableStack.[[AsyncDisposableState]] is disposed, return
|
|
// true.
|
|
// 4. Otherwise, return false.
|
|
return *(isolate->factory()->ToBoolean(async_disposable_stack->state() ==
|
|
DisposableStackState::kDisposed));
|
|
}
|
|
|
|
// https://tc39.es/proposal-explicit-resource-management/#sec-asyncdisposablestack.prototype.adopt
|
|
BUILTIN(AsyncDisposableStackPrototypeAdopt) {
|
|
const char kMethodName[] = "AsyncDisposableStack.prototype.adopt";
|
|
HandleScope scope(isolate);
|
|
DirectHandle<Object> value = args.at(1);
|
|
DirectHandle<Object> on_dispose_async = args.at(2);
|
|
|
|
// 1. Let asyncDisposableStack be the this value.
|
|
// 2. Perform ? RequireInternalSlot(asyncDisposableStack,
|
|
// [[AsyncDisposableState]]).
|
|
CHECK_RECEIVER(JSAsyncDisposableStack, async_disposable_stack, kMethodName);
|
|
|
|
// 3. If asyncDisposableStack.[[AsyncDisposableState]] is disposed, throw a
|
|
// ReferenceError exception.
|
|
if (async_disposable_stack->state() == DisposableStackState::kDisposed) {
|
|
THROW_NEW_ERROR_RETURN_FAILURE(
|
|
isolate,
|
|
NewReferenceError(
|
|
MessageTemplate::kDisposableStackIsDisposed,
|
|
isolate->factory()->NewStringFromAsciiChecked(kMethodName)));
|
|
}
|
|
|
|
// 4. If IsCallable(onDisposeAsync) is false, throw a TypeError exception.
|
|
if (!IsCallable(*on_dispose_async)) {
|
|
THROW_NEW_ERROR_RETURN_FAILURE(
|
|
isolate, NewTypeError(MessageTemplate::kNotCallable, on_dispose_async));
|
|
}
|
|
|
|
// 5. Let closure be a new Abstract Closure with no parameters that captures
|
|
// value and onDisposeAsync and performs the following steps when called:
|
|
// a. Return ? Call(onDisposeAsync, undefined, « value »).
|
|
// 6. Let F be CreateBuiltinFunction(closure, 0, "", « »).
|
|
// 7. Perform ?
|
|
// AddDisposableResource(asyncDisposableStack.[[DisposeCapability]],
|
|
// undefined, async-dispose, F).
|
|
// Instead of creating an abstract closure and a function, we pass
|
|
// DisposeMethodCallType::kArgument so at the time of disposal, the value will
|
|
// be passed as the argument to the method.
|
|
JSDisposableStackBase::Add(isolate, async_disposable_stack, value,
|
|
on_dispose_async,
|
|
DisposeMethodCallType::kValueIsArgument,
|
|
DisposeMethodHint::kAsyncDispose);
|
|
|
|
// 8. Return value.
|
|
return *value;
|
|
}
|
|
|
|
// https://tc39.es/proposal-explicit-resource-management/#sec-asyncdisposablestack.prototype.defer
|
|
BUILTIN(AsyncDisposableStackPrototypeDefer) {
|
|
const char kMethodName[] = "AsyncDisposableStack.prototype.defer";
|
|
HandleScope scope(isolate);
|
|
DirectHandle<Object> on_dispose_async = args.at(1);
|
|
|
|
// 1. Let asyncDisposableStack be the this value.
|
|
// 2. Perform ? RequireInternalSlot(asyncDisposableStack,
|
|
// [[AsyncDisposableState]]).
|
|
CHECK_RECEIVER(JSAsyncDisposableStack, async_disposable_stack, kMethodName);
|
|
|
|
// 3. If asyncDisposableStack.[[AsyncDisposableState]] is disposed, throw a
|
|
// ReferenceError exception.
|
|
if (async_disposable_stack->state() == DisposableStackState::kDisposed) {
|
|
THROW_NEW_ERROR_RETURN_FAILURE(
|
|
isolate,
|
|
NewReferenceError(
|
|
MessageTemplate::kDisposableStackIsDisposed,
|
|
isolate->factory()->NewStringFromAsciiChecked(kMethodName)));
|
|
}
|
|
|
|
// 4. If IsCallable(onDisposeAsync) is false, throw a TypeError exception.
|
|
if (!IsCallable(*on_dispose_async)) {
|
|
THROW_NEW_ERROR_RETURN_FAILURE(
|
|
isolate, NewTypeError(MessageTemplate::kNotCallable, on_dispose_async));
|
|
}
|
|
|
|
// 5. Perform ?
|
|
// AddDisposableResource(asyncDisposableStack.[[DisposeCapability]],
|
|
// undefined, async-dispose, onDisposeAsync).
|
|
JSDisposableStackBase::Add(
|
|
isolate, async_disposable_stack, isolate->factory()->undefined_value(),
|
|
on_dispose_async, DisposeMethodCallType::kValueIsReceiver,
|
|
DisposeMethodHint::kAsyncDispose);
|
|
|
|
// 6. Return undefined.
|
|
return ReadOnlyRoots(isolate).undefined_value();
|
|
}
|
|
|
|
// https://tc39.es/proposal-explicit-resource-management/#sec-asyncdisposablestack.prototype.move
|
|
BUILTIN(AsyncDisposableStackPrototypeMove) {
|
|
const char kMethodName[] = "AsyncDisposableStack.prototype.move";
|
|
HandleScope scope(isolate);
|
|
|
|
// 1. Let asyncDisposableStack be the this value.
|
|
// 2. Perform ? RequireInternalSlot(asyncDisposableStack,
|
|
// [[AsyncDisposableState]]).
|
|
CHECK_RECEIVER(JSAsyncDisposableStack, async_disposable_stack, kMethodName);
|
|
|
|
// 3. If asyncDisposableStack.[[AsyncDisposableState]] is disposed, throw a
|
|
// ReferenceError exception.
|
|
if (async_disposable_stack->state() == DisposableStackState::kDisposed) {
|
|
THROW_NEW_ERROR_RETURN_FAILURE(
|
|
isolate,
|
|
NewReferenceError(
|
|
MessageTemplate::kDisposableStackIsDisposed,
|
|
isolate->factory()->NewStringFromAsciiChecked(kMethodName)));
|
|
}
|
|
|
|
// 4. Let newAsyncDisposableStack be ?
|
|
// OrdinaryCreateFromConstructor(%AsyncDisposableStack%,
|
|
// "%AsyncDisposableStack.prototype%", « [[AsyncDisposableState]],
|
|
// [[DisposeCapability]] »).
|
|
// 5. Set newAsyncDisposableStack.[[AsyncDisposableState]] to pending.
|
|
|
|
Tagged<JSFunction> constructor_function =
|
|
Cast<JSFunction>(isolate->native_context()->GetNoCell(
|
|
Context::JS_ASYNC_DISPOSABLE_STACK_FUNCTION_INDEX));
|
|
DirectHandle<Map> map(constructor_function->initial_map(), isolate);
|
|
|
|
DirectHandle<JSAsyncDisposableStack> new_async_disposable_stack =
|
|
isolate->factory()->NewJSAsyncDisposableStack(map);
|
|
|
|
// 6. Set newAsyncDisposableStack.[[DisposeCapability]] to
|
|
// asyncDisposableStack.[[DisposeCapability]].
|
|
new_async_disposable_stack->set_stack(async_disposable_stack->stack());
|
|
new_async_disposable_stack->set_length(async_disposable_stack->length());
|
|
new_async_disposable_stack->set_state(DisposableStackState::kPending);
|
|
new_async_disposable_stack->set_error(
|
|
*(isolate->factory()->uninitialized_value()));
|
|
|
|
// 7. Set asyncDisposableStack.[[DisposeCapability]] to
|
|
// NewDisposeCapability().
|
|
async_disposable_stack->set_stack(ReadOnlyRoots(isolate).empty_fixed_array());
|
|
async_disposable_stack->set_length(0);
|
|
async_disposable_stack->set_error(
|
|
*(isolate->factory()->uninitialized_value()));
|
|
|
|
// 8. Set disposableStack.[[DisposableState]] to disposed.
|
|
async_disposable_stack->set_state(DisposableStackState::kDisposed);
|
|
|
|
// 9. Return newDisposableStack.
|
|
return *new_async_disposable_stack;
|
|
}
|
|
|
|
} // namespace internal
|
|
} // namespace v8
|