blob: 72a99b5229c24e7402673be8c27cff36d76e2dec [file] [log] [blame]
// 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/builtins/builtins-utils-inl.h"
#include "src/builtins/builtins.h"
#include "src/common/globals.h"
#include "src/handles/maybe-handles.h"
#include "src/objects/casting.h"
#include "src/objects/contexts.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-function.h"
namespace v8 {
namespace internal {
// https://arai-a.github.io/ecma262-compare/?pr=3000&id=sec-disposablestack
BUILTIN(DisposableStackConstructor) {
const char* const kMethodName = "DisposableStack";
HandleScope scope(isolate);
isolate->CountUsage(v8::Isolate::kExplicitResourceManagement);
// 1. If NewTarget is undefined, throw a TypeError exception.
if (IsUndefined(*args.new_target(), isolate)) {
THROW_NEW_ERROR_RETURN_FAILURE(
isolate, NewTypeError(MessageTemplate::kConstructorNotFunction,
isolate->factory()->NewStringFromAsciiChecked(
kMethodName)));
}
// 2. Let disposableStack be ? OrdinaryCreateFromConstructor(NewTarget,
// "%DisposableStack.prototype%", « [[DisposableState]],
// [[DisposeCapability]] »).
DirectHandle<Map> map;
DirectHandle<JSFunction> target = args.target();
DirectHandle<JSReceiver> new_target = Cast<JSReceiver>(args.new_target());
ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
isolate, map, JSFunction::GetDerivedMap(isolate, target, new_target));
DirectHandle<JSSyncDisposableStack> disposable_stack =
isolate->factory()->NewJSSyncDisposableStack(map);
// 3. Set disposableStack.[[DisposableState]] to pending.
// 4. Set disposableStack.[[DisposeCapability]] to NewDisposeCapability().
JSDisposableStackBase::InitializeJSDisposableStackBase(isolate,
disposable_stack);
// 5. Return disposableStack.
return *disposable_stack;
}
// https://arai-a.github.io/ecma262-compare/?pr=3000&id=sec-disposablestack.prototype.use
BUILTIN(DisposableStackPrototypeUse) {
const char* const kMethodName = "DisposableStack.prototype.use";
HandleScope scope(isolate);
// 1. Let disposableStack be the this value.
// 2. Perform ? RequireInternalSlot(disposableStack, [[DisposableState]]).
CHECK_RECEIVER(JSSyncDisposableStack, disposable_stack, kMethodName);
DirectHandle<JSAny> value = args.at<JSAny>(1);
// 3. If disposableStack.[[DisposableState]] is disposed, throw a
// ReferenceError exception.
if (disposable_stack->state() == DisposableStackState::kDisposed) {
THROW_NEW_ERROR_RETURN_FAILURE(
isolate,
NewReferenceError(
MessageTemplate::kDisposableStackIsDisposed,
isolate->factory()->NewStringFromAsciiChecked(kMethodName)));
}
// 4. Perform ? AddDisposableResource(disposableStack.[[DisposeCapability]],
// value, sync-dispose).
// (a. If V is either null or undefined and hint is sync-dispose, then
// i. Return unused.)
if (IsNullOrUndefined(*value)) {
return *value;
}
DirectHandle<Object> method;
ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
isolate, method,
JSDisposableStackBase::CheckValueAndGetDisposeMethod(
isolate, value, DisposeMethodHint::kSyncDispose));
JSDisposableStackBase::Add(isolate, disposable_stack, value, method,
DisposeMethodCallType::kValueIsReceiver,
DisposeMethodHint::kSyncDispose);
// 5. Return value.
return *value;
}
BUILTIN(DisposableStackPrototypeDispose) {
const char* const kMethodName = "DisposableStack.prototype.dispose";
HandleScope scope(isolate);
// 1. Let disposableStack be the this value.
// 2. Perform ? RequireInternalSlot(disposableStack, [[DisposableState]]).
CHECK_RECEIVER(JSSyncDisposableStack, disposable_stack, kMethodName);
// 3. If disposableStack.[[DisposableState]] is disposed, return undefined.
if (disposable_stack->state() == DisposableStackState::kDisposed) {
return ReadOnlyRoots(isolate).undefined_value();
}
// 4. Set disposableStack.[[DisposableState]] to disposed.
disposable_stack->set_state(DisposableStackState::kDisposed);
// 5. Return ? DisposeResources(disposableStack.[[DisposeCapability]],
// NormalCompletion(undefined)).
DirectHandle<Object> result;
ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
isolate, result,
JSDisposableStackBase::DisposeResources(
isolate, disposable_stack, DisposableStackResourcesType::kAllSync));
return ReadOnlyRoots(isolate).undefined_value();
}
BUILTIN(DisposableStackPrototypeGetDisposed) {
const char* const kMethodName = "get DisposableStack.prototype.disposed";
HandleScope scope(isolate);
// 1. Let disposableStack be the this value.
// 2. Perform ? RequireInternalSlot(disposableStack, [[DisposableState]]).
CHECK_RECEIVER(JSSyncDisposableStack, disposable_stack, kMethodName);
// 3. If disposableStack.[[DisposableState]] is disposed, return true.
if (disposable_stack->state() == DisposableStackState::kDisposed) {
return ReadOnlyRoots(isolate).true_value();
}
// 4. Otherwise, return false.
return ReadOnlyRoots(isolate).false_value();
}
// https://arai-a.github.io/ecma262-compare/?pr=3000&id=sec-disposablestack.prototype.adopt
BUILTIN(DisposableStackPrototypeAdopt) {
const char* const kMethodName = "DisposableStack.prototype.adopt";
HandleScope scope(isolate);
DirectHandle<Object> value = args.at(1);
DirectHandle<Object> on_dispose = args.at(2);
// 1. Let disposableStack be the this value.
// 2. Perform ? RequireInternalSlot(disposableStack, [[DisposableState]]).
CHECK_RECEIVER(JSSyncDisposableStack, disposable_stack, kMethodName);
// 3. If disposableStack.[[DisposableState]] is disposed, throw a
// ReferenceError exception.
if (disposable_stack->state() == DisposableStackState::kDisposed) {
THROW_NEW_ERROR_RETURN_FAILURE(
isolate,
NewReferenceError(
MessageTemplate::kDisposableStackIsDisposed,
isolate->factory()->NewStringFromAsciiChecked(kMethodName)));
}
// 4. If IsCallable(onDispose) is false, throw a TypeError exception.
if (!IsCallable(*on_dispose)) {
THROW_NEW_ERROR_RETURN_FAILURE(
isolate, NewTypeError(MessageTemplate::kNotCallable, on_dispose));
}
// 5. Let closure be a new Abstract Closure with no parameters that captures
// value and onDispose and performs the following steps when called:
// a. Return ? Call(onDispose, undefined, « value »).
// 6. Let F be CreateBuiltinFunction(closure, 0, "", « »).
// 7. Perform ? AddDisposableResource(disposableStack.[[DisposeCapability]],
// undefined, sync-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, disposable_stack, value, on_dispose,
DisposeMethodCallType::kValueIsArgument,
DisposeMethodHint::kSyncDispose);
// 8. Return value.
return *value;
}
// https://arai-a.github.io/ecma262-compare/?pr=3000&id=sec-disposablestack.prototype.defer
BUILTIN(DisposableStackPrototypeDefer) {
const char* const kMethodName = "DisposableStack.prototype.defer";
HandleScope scope(isolate);
DirectHandle<Object> on_dispose = args.at(1);
// 1. Let disposableStack be the this value.
// 2. Perform ? RequireInternalSlot(disposableStack, [[DisposableState]]).
CHECK_RECEIVER(JSSyncDisposableStack, disposable_stack, kMethodName);
// 3. If disposableStack.[[DisposableState]] is disposed, throw a
// ReferenceError exception.
if (disposable_stack->state() == DisposableStackState::kDisposed) {
THROW_NEW_ERROR_RETURN_FAILURE(
isolate,
NewReferenceError(
MessageTemplate::kDisposableStackIsDisposed,
isolate->factory()->NewStringFromAsciiChecked(kMethodName)));
}
// 4. If IsCallable(onDispose) is false, throw a TypeError exception.
if (!IsCallable(*on_dispose)) {
THROW_NEW_ERROR_RETURN_FAILURE(
isolate, NewTypeError(MessageTemplate::kNotCallable, on_dispose));
}
// 5. Perform ? AddDisposableResource(disposableStack.[[DisposeCapability]],
// undefined, sync-dispose, onDispose).
JSDisposableStackBase::Add(isolate, disposable_stack,
isolate->factory()->undefined_value(), on_dispose,
DisposeMethodCallType::kValueIsReceiver,
DisposeMethodHint::kSyncDispose);
// 6. Return undefined.
return ReadOnlyRoots(isolate).undefined_value();
}
BUILTIN(DisposableStackPrototypeMove) {
const char* const kMethodName = "DisposableStack.prototype.move";
HandleScope scope(isolate);
// 1. Let disposableStack be the this value.
// 2. Perform ? RequireInternalSlot(disposableStack, [[DisposableState]]).
CHECK_RECEIVER(JSSyncDisposableStack, disposable_stack, kMethodName);
// 3. If disposableStack.[[DisposableState]] is disposed, throw a
// ReferenceError exception.
if (disposable_stack->state() == DisposableStackState::kDisposed) {
THROW_NEW_ERROR_RETURN_FAILURE(
isolate,
NewReferenceError(
MessageTemplate::kDisposableStackIsDisposed,
isolate->factory()->NewStringFromAsciiChecked(kMethodName)));
}
// 4. Let newDisposableStack be ?
// OrdinaryCreateFromConstructor(%DisposableStack%,
// "%DisposableStack.prototype%", « [[DisposableState]],
// [[DisposeCapability]] »).
// 5. Set newDisposableStack.[[DisposableState]] to pending.
Tagged<JSFunction> constructor_function =
Cast<JSFunction>(isolate->native_context()->get(
Context::JS_DISPOSABLE_STACK_FUNCTION_INDEX));
DirectHandle<Map> map(constructor_function->initial_map(), isolate);
DirectHandle<JSSyncDisposableStack> new_disposable_stack =
isolate->factory()->NewJSSyncDisposableStack(map);
// 6. Set newDisposableStack.[[DisposeCapability]] to
// disposableStack.[[DisposeCapability]].
new_disposable_stack->set_stack(disposable_stack->stack());
new_disposable_stack->set_length(disposable_stack->length());
new_disposable_stack->set_state(DisposableStackState::kPending);
new_disposable_stack->set_error(*(isolate->factory()->uninitialized_value()));
new_disposable_stack->set_error_message(
*(isolate->factory()->uninitialized_value()));
// 7. Set disposableStack.[[DisposeCapability]] to NewDisposeCapability().
disposable_stack->set_stack(ReadOnlyRoots(isolate).empty_fixed_array());
disposable_stack->set_length(0);
disposable_stack->set_error(*(isolate->factory()->uninitialized_value()));
disposable_stack->set_error_message(
*(isolate->factory()->uninitialized_value()));
// 8. Set disposableStack.[[DisposableState]] to disposed.
disposable_stack->set_state(DisposableStackState::kDisposed);
// 9. Return newDisposableStack.
return *new_disposable_stack;
}
} // namespace internal
} // namespace v8