v8
V8 is Google’s open source high-performance JavaScript and WebAssembly engine, written in C++.
Loading...
Searching...
No Matches
builtins-async-disposable-stack.cc
Go to the documentation of this file.
1// Copyright 2024 the V8 project authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "src/api/api.h"
6#include "src/base/logging.h"
7#include "src/base/macros.h"
18#include "src/objects/objects.h"
19#include "src/roots/roots.h"
20
21namespace v8 {
22namespace internal {
23
24BUILTIN(AsyncDisposableStackOnFulfilled) {
25 HandleScope scope(isolate);
26
28 Cast<JSDisposableStackBase>(isolate->context()->get(static_cast<int>(
30 isolate);
32 Cast<JSPromise>(isolate->context()->get(static_cast<int>(
34 kOuterPromise))),
35 isolate);
36
38 promise),
39 ReadOnlyRoots(isolate).exception());
40 return ReadOnlyRoots(isolate).undefined_value();
41}
42
43BUILTIN(AsyncDisposableStackOnRejected) {
44 HandleScope scope(isolate);
45
47 Cast<JSDisposableStackBase>(isolate->context()->get(static_cast<int>(
49 isolate);
51 Cast<JSPromise>(isolate->context()->get(static_cast<int>(
53 kOuterPromise))),
54 isolate);
55
56 DirectHandle<Object> rejection_error = args.at(1);
57 // (TODO:rezvan): Pass the correct pending message.
58 DirectHandle<Object> message(isolate->pending_message(), isolate);
59 DCHECK(isolate->is_catchable_by_javascript(*rejection_error));
60 JSDisposableStackBase::HandleErrorInDisposal(isolate, stack, rejection_error,
61 message);
62
64 promise),
65 ReadOnlyRoots(isolate).exception());
66 return ReadOnlyRoots(isolate).undefined_value();
67}
68
69// Part of
70// https://tc39.es/proposal-explicit-resource-management/#sec-getdisposemethod
71BUILTIN(AsyncDisposeFromSyncDispose) {
72 HandleScope scope(isolate);
73 // 1. If hint is async-dispose
74 // b. If GetMethod(V, @@asyncDispose) is undefined,
75 // i. If GetMethod(V, @@dispose) is not undefined, then
76 // 1. Let closure be a new Abstract Closure with no parameters that
77 // captures method and performs the following steps when called:
78 // a. Let O be the this value.
79 // b. Let promiseCapability be ! NewPromiseCapability(%Promise%).
81 DirectHandle<JSPromise> promise = isolate->factory()->NewJSPromise();
82
83 // c. Let result be Completion(Call(method, O)).
84 DirectHandle<JSFunction> sync_method(
85 Cast<JSFunction>(isolate->context()->get(static_cast<int>(
87 kMethod))),
88 isolate);
89
90 v8::TryCatch try_catch(reinterpret_cast<v8::Isolate*>(isolate));
91 try_catch.SetVerbose(false);
92 try_catch.SetCaptureMessage(false);
93
95 Execution::Call(isolate, sync_method, receiver, {});
96
97 if (!result.is_null()) {
98 // e. Perform ? Call(promiseCapability.[[Resolve]], undefined, «
99 // undefined »).
100 JSPromise::Resolve(promise, isolate->factory()->undefined_value())
101 .ToHandleChecked();
102 } else {
103 Tagged<Object> exception = isolate->exception();
104 if (!isolate->is_catchable_by_javascript(exception)) {
105 return {};
106 }
107 // d. IfAbruptRejectPromise(result, promiseCapability).
108 DCHECK(try_catch.HasCaught());
109 JSPromise::Reject(promise, direct_handle(exception, isolate));
110 }
111
112 // f. Return promiseCapability.[[Promise]].
113 return *promise;
114}
115
116// https://tc39.es/proposal-explicit-resource-management/#sec-asyncdisposablestack
117BUILTIN(AsyncDisposableStackConstructor) {
118 const char kMethodName[] = "AsyncDisposableStack";
119 HandleScope scope(isolate);
120 isolate->CountUsage(v8::Isolate::kExplicitResourceManagement);
121
122 // 1. If NewTarget is undefined, throw a TypeError exception.
123 if (!IsJSReceiver(*args.new_target(), isolate)) {
125 isolate, NewTypeError(MessageTemplate::kConstructorNotFunction,
126 isolate->factory()->NewStringFromAsciiChecked(
127 kMethodName)));
128 }
129
130 // 2. Let asyncDisposableStack be ? OrdinaryCreateFromConstructor(NewTarget,
131 // "%AsyncDisposableStack.prototype%", « [[AsyncDisposableState]],
132 // [[DisposeCapability]] »).
134 DirectHandle<JSFunction> target = args.target();
136
137 DCHECK_EQ(*target,
138 target->native_context()->js_async_disposable_stack_function());
139
141 isolate, map, JSFunction::GetDerivedMap(isolate, target, new_target));
142
143 DirectHandle<JSAsyncDisposableStack> async_disposable_stack =
144 isolate->factory()->NewJSAsyncDisposableStack(map);
145 // 3. Set asyncDisposableStack.[[AsyncDisposableState]] to pending.
146 // 4. Set asyncDisposableStack.[[DisposeCapability]] to
147 // NewDisposeCapability().
149 isolate, async_disposable_stack);
150 // 5. Return asyncDisposableStack.
151 return *async_disposable_stack;
152}
153
154// https://tc39.es/proposal-explicit-resource-management/#sec-asyncdisposablestack.prototype.use
155BUILTIN(AsyncDisposableStackPrototypeUse) {
156 const char kMethodName[] = "AsyncDisposableStack.prototype.use";
157 HandleScope scope(isolate);
158
159 // 1. Let asyncDisposableStack be the this value.
160 // 2. Perform ? RequireInternalSlot(asyncDisposableStack,
161 // [[AsyncDisposableState]]).
162 CHECK_RECEIVER(JSAsyncDisposableStack, async_disposable_stack, kMethodName);
163 DirectHandle<JSAny> value = args.at<JSAny>(1);
164
165 // 3. If asyncDisposableStack.[[AsyncDisposableState]] is disposed, throw a
166 // ReferenceError exception.
167 if (async_disposable_stack->state() == DisposableStackState::kDisposed) {
169 isolate,
170 NewReferenceError(
171 MessageTemplate::kDisposableStackIsDisposed,
172 isolate->factory()->NewStringFromAsciiChecked(kMethodName)));
173 }
174
175 // 4. Perform ?
176 // AddDisposableResource(asyncDisposableStack.[[DisposeCapability]],
177 // value, async-dispose).
180 isolate, method,
182 isolate, value, DisposeMethodHint::kAsyncDispose));
183
185 isolate, async_disposable_stack,
186 (IsNullOrUndefined(*value) ? isolate->factory()->undefined_value()
187 : value),
190
191 // 5. Return value.
192 return *value;
193}
194
195// https://tc39.es/proposal-explicit-resource-management/#sec-asyncdisposablestack.prototype.disposeAsync
196BUILTIN(AsyncDisposableStackPrototypeDisposeAsync) {
197 HandleScope scope(isolate);
198
199 // 1. Let asyncDisposableStack be the this value.
201
202 // 2. Let promiseCapability be ! NewPromiseCapability(%Promise%).
203 DirectHandle<JSPromise> promise = isolate->factory()->NewJSPromise();
204
205 // 3. If asyncDisposableStack does not have an [[AsyncDisposableState]]
206 // internal slot, then
207 if (!IsJSAsyncDisposableStack(*receiver)) {
208 // a. Perform ! Call(promiseCapability.[[Reject]], undefined, « a newly
209 // created TypeError object »).
210 JSPromise::Reject(promise,
211 isolate->factory()->NewTypeError(
212 MessageTemplate::kNotAnAsyncDisposableStack));
213 // b. Return promiseCapability.[[Promise]].
214 return *promise;
215 }
216
217 DirectHandle<JSAsyncDisposableStack> async_disposable_stack =
219
220 // 4. If asyncDisposableStack.[[AsyncDisposableState]] is disposed, then
221 if (async_disposable_stack->state() == DisposableStackState::kDisposed) {
222 // a. Perform ! Call(promiseCapability.[[Resolve]], undefined, «
223 // undefined »).
225 promise,
226 direct_handle(ReadOnlyRoots(isolate).undefined_value(), isolate))
227 .ToHandleChecked();
228 // b. Return promiseCapability.[[Promise]].
229 return *promise;
230 }
231
232 // 5. Set asyncDisposableStack.[[AsyncDisposableState]] to disposed.
233 async_disposable_stack->set_state(DisposableStackState::kDisposed);
234
235 // 6. Let result be
236 // DisposeResources(asyncDisposableStack.[[DisposeCapability]],
237 // NormalCompletion(undefined)).
238 // 7. IfAbruptRejectPromise(result, promiseCapability).
239 // 8. Perform ! Call(promiseCapability.[[Resolve]], undefined, « result
240 // »).
241 // 9. Return promiseCapability.[[Promise]].
243 isolate, async_disposable_stack, promise),
244 ReadOnlyRoots(isolate).exception());
245 return *promise;
246}
247
248// https://tc39.es/proposal-explicit-resource-management/#sec-get-asyncdisposablestack.prototype.disposed
249BUILTIN(AsyncDisposableStackPrototypeGetDisposed) {
250 const char kMethodName[] = "get AsyncDisposableStack.prototype.disposed";
251 HandleScope scope(isolate);
252
253 // 1. Let AsyncdisposableStack be the this value.
254 // 2. Perform ? RequireInternalSlot(asyncDisposableStack,
255 // [[AsyncDisposableState]]).
256 CHECK_RECEIVER(JSAsyncDisposableStack, async_disposable_stack, kMethodName);
257
258 // 3. If AsyncdisposableStack.[[AsyncDisposableState]] is disposed, return
259 // true.
260 // 4. Otherwise, return false.
261 return *(isolate->factory()->ToBoolean(async_disposable_stack->state() ==
263}
264
265// https://tc39.es/proposal-explicit-resource-management/#sec-asyncdisposablestack.prototype.adopt
266BUILTIN(AsyncDisposableStackPrototypeAdopt) {
267 const char kMethodName[] = "AsyncDisposableStack.prototype.adopt";
268 HandleScope scope(isolate);
269 DirectHandle<Object> value = args.at(1);
270 DirectHandle<Object> on_dispose_async = args.at(2);
271
272 // 1. Let asyncDisposableStack be the this value.
273 // 2. Perform ? RequireInternalSlot(asyncDisposableStack,
274 // [[AsyncDisposableState]]).
275 CHECK_RECEIVER(JSAsyncDisposableStack, async_disposable_stack, kMethodName);
276
277 // 3. If asyncDisposableStack.[[AsyncDisposableState]] is disposed, throw a
278 // ReferenceError exception.
279 if (async_disposable_stack->state() == DisposableStackState::kDisposed) {
281 isolate,
282 NewReferenceError(
283 MessageTemplate::kDisposableStackIsDisposed,
284 isolate->factory()->NewStringFromAsciiChecked(kMethodName)));
285 }
286
287 // 4. If IsCallable(onDisposeAsync) is false, throw a TypeError exception.
288 if (!IsCallable(*on_dispose_async)) {
290 isolate, NewTypeError(MessageTemplate::kNotCallable, on_dispose_async));
291 }
292
293 // 5. Let closure be a new Abstract Closure with no parameters that captures
294 // value and onDisposeAsync and performs the following steps when called:
295 // a. Return ? Call(onDisposeAsync, undefined, « value »).
296 // 6. Let F be CreateBuiltinFunction(closure, 0, "", « »).
297 // 7. Perform ?
298 // AddDisposableResource(asyncDisposableStack.[[DisposeCapability]],
299 // undefined, async-dispose, F).
300 // Instead of creating an abstract closure and a function, we pass
301 // DisposeMethodCallType::kArgument so at the time of disposal, the value will
302 // be passed as the argument to the method.
303 JSDisposableStackBase::Add(isolate, async_disposable_stack, value,
304 on_dispose_async,
307
308 // 8. Return value.
309 return *value;
310}
311
312// https://tc39.es/proposal-explicit-resource-management/#sec-asyncdisposablestack.prototype.defer
313BUILTIN(AsyncDisposableStackPrototypeDefer) {
314 const char kMethodName[] = "AsyncDisposableStack.prototype.defer";
315 HandleScope scope(isolate);
316 DirectHandle<Object> on_dispose_async = args.at(1);
317
318 // 1. Let asyncDisposableStack be the this value.
319 // 2. Perform ? RequireInternalSlot(asyncDisposableStack,
320 // [[AsyncDisposableState]]).
321 CHECK_RECEIVER(JSAsyncDisposableStack, async_disposable_stack, kMethodName);
322
323 // 3. If asyncDisposableStack.[[AsyncDisposableState]] is disposed, throw a
324 // ReferenceError exception.
325 if (async_disposable_stack->state() == DisposableStackState::kDisposed) {
327 isolate,
328 NewReferenceError(
329 MessageTemplate::kDisposableStackIsDisposed,
330 isolate->factory()->NewStringFromAsciiChecked(kMethodName)));
331 }
332
333 // 4. If IsCallable(onDisposeAsync) is false, throw a TypeError exception.
334 if (!IsCallable(*on_dispose_async)) {
336 isolate, NewTypeError(MessageTemplate::kNotCallable, on_dispose_async));
337 }
338
339 // 5. Perform ?
340 // AddDisposableResource(asyncDisposableStack.[[DisposeCapability]],
341 // undefined, async-dispose, onDisposeAsync).
343 isolate, async_disposable_stack, isolate->factory()->undefined_value(),
346
347 // 6. Return undefined.
348 return ReadOnlyRoots(isolate).undefined_value();
349}
350
351// https://tc39.es/proposal-explicit-resource-management/#sec-asyncdisposablestack.prototype.move
352BUILTIN(AsyncDisposableStackPrototypeMove) {
353 const char kMethodName[] = "AsyncDisposableStack.prototype.move";
354 HandleScope scope(isolate);
355
356 // 1. Let asyncDisposableStack be the this value.
357 // 2. Perform ? RequireInternalSlot(asyncDisposableStack,
358 // [[AsyncDisposableState]]).
359 CHECK_RECEIVER(JSAsyncDisposableStack, async_disposable_stack, kMethodName);
360
361 // 3. If asyncDisposableStack.[[AsyncDisposableState]] is disposed, throw a
362 // ReferenceError exception.
363 if (async_disposable_stack->state() == DisposableStackState::kDisposed) {
365 isolate,
366 NewReferenceError(
367 MessageTemplate::kDisposableStackIsDisposed,
368 isolate->factory()->NewStringFromAsciiChecked(kMethodName)));
369 }
370
371 // 4. Let newAsyncDisposableStack be ?
372 // OrdinaryCreateFromConstructor(%AsyncDisposableStack%,
373 // "%AsyncDisposableStack.prototype%", « [[AsyncDisposableState]],
374 // [[DisposeCapability]] »).
375 // 5. Set newAsyncDisposableStack.[[AsyncDisposableState]] to pending.
376
377 Tagged<JSFunction> constructor_function =
378 Cast<JSFunction>(isolate->native_context()->get(
379 Context::JS_ASYNC_DISPOSABLE_STACK_FUNCTION_INDEX));
380 DirectHandle<Map> map(constructor_function->initial_map(), isolate);
381
382 DirectHandle<JSAsyncDisposableStack> new_async_disposable_stack =
383 isolate->factory()->NewJSAsyncDisposableStack(map);
384
385 // 6. Set newAsyncDisposableStack.[[DisposeCapability]] to
386 // asyncDisposableStack.[[DisposeCapability]].
387 new_async_disposable_stack->set_stack(async_disposable_stack->stack());
388 new_async_disposable_stack->set_length(async_disposable_stack->length());
389 new_async_disposable_stack->set_state(DisposableStackState::kPending);
390 new_async_disposable_stack->set_error(
391 *(isolate->factory()->uninitialized_value()));
392
393 // 7. Set asyncDisposableStack.[[DisposeCapability]] to
394 // NewDisposeCapability().
395 async_disposable_stack->set_stack(ReadOnlyRoots(isolate).empty_fixed_array());
396 async_disposable_stack->set_length(0);
397 async_disposable_stack->set_error(
398 *(isolate->factory()->uninitialized_value()));
399
400 // 8. Set disposableStack.[[DisposableState]] to disposed.
401 async_disposable_stack->set_state(DisposableStackState::kDisposed);
402
403 // 9. Return newDisposableStack.
404 return *new_async_disposable_stack;
405}
406
407} // namespace internal
408} // namespace v8
#define CHECK_RECEIVER(Type, name, method)
#define BUILTIN(name)
@ kExplicitResourceManagement
Definition v8-isolate.h:635
void SetVerbose(bool value)
Definition api.cc:2856
void SetCaptureMessage(bool value)
Definition api.cc:2860
bool HasCaught() const
Definition api.cc:2781
V8_EXPORT_PRIVATE static V8_WARN_UNUSED_RESULT MaybeHandle< Object > Call(Isolate *isolate, DirectHandle< Object > callable, DirectHandle< Object > receiver, base::Vector< const DirectHandle< Object > > args)
Definition execution.cc:523
static Maybe< bool > NextDisposeAsyncIteration(Isolate *isolate, DirectHandle< JSDisposableStackBase > async_disposable_stack, DirectHandle< JSPromise > outer_promise)
static void InitializeJSDisposableStackBase(Isolate *isolate, DirectHandle< JSDisposableStackBase > stack)
Definition objects.cc:6384
static void HandleErrorInDisposal(Isolate *isolate, DirectHandle< JSDisposableStackBase > disposable_stack, DirectHandle< Object > current_error, DirectHandle< Object > current_error_message)
static void Add(Isolate *isolate, DirectHandle< JSDisposableStackBase > disposable_stack, DirectHandle< Object > value, DirectHandle< Object > method, DisposeMethodCallType type, DisposeMethodHint hint)
static MaybeDirectHandle< Object > CheckValueAndGetDisposeMethod(Isolate *isolate, DirectHandle< JSAny > value, DisposeMethodHint hint)
static V8_EXPORT_PRIVATE V8_WARN_UNUSED_RESULT MaybeHandle< Map > GetDerivedMap(Isolate *isolate, DirectHandle< JSFunction > constructor, DirectHandle< JSReceiver > new_target)
static Handle< Object > Reject(DirectHandle< JSPromise > promise, DirectHandle< Object > reason, bool debug_event=true)
Definition objects.cc:5069
static V8_WARN_UNUSED_RESULT MaybeHandle< Object > Resolve(DirectHandle< JSPromise > promise, DirectHandle< Object > resolution)
Definition objects.cc:5109
#define ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, dst, call)
Definition isolate.h:284
#define THROW_NEW_ERROR_RETURN_FAILURE(isolate, call)
Definition isolate.h:294
#define MAYBE_RETURN(call, value)
Definition isolate.h:408
base::Vector< const DirectHandle< Object > > args
Definition execution.cc:74
DirectHandle< Object > new_target
Definition execution.cc:75
TNode< Object > receiver
std::map< const std::string, const std::string > map
ZoneVector< RpoNumber > & result
ZoneStack< RpoNumber > & stack
V8_INLINE DirectHandle< T > direct_handle(Tagged< T > object, Isolate *isolate)
bool IsNullOrUndefined(Tagged< Object > obj, Isolate *isolate)
return value
Definition map-inl.h:893
Tagged< To > Cast(Tagged< From > value, const v8::SourceLocation &loc=INIT_SOURCE_LOCATION_IN_DEBUG)
Definition casting.h:150
#define DCHECK(condition)
Definition logging.h:482
#define DCHECK_EQ(v1, v2)
Definition logging.h:485
Symbol method