v8
V8 is Google’s open source high-performance JavaScript and WebAssembly engine, written in C++.
Loading...
Searching...
No Matches
js-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
6
7#include "include/v8-maybe.h"
9#include "src/base/logging.h"
10#include "src/base/macros.h"
11#include "src/debug/debug.h"
13#include "src/handles/handles.h"
15#include "src/heap/factory.h"
24#include "src/objects/objects.h"
25#include "src/objects/oddball.h"
26#include "src/objects/tagged.h"
27
28namespace v8 {
29namespace internal {
30
31#define CHECK_EXCEPTION_ON_DISPOSAL(isolate, disposable_stack, return_value) \
32 do { \
33 DCHECK(isolate->has_exception()); \
34 DirectHandle<Object> current_error(isolate->exception(), isolate); \
35 DirectHandle<Object> current_error_message(isolate->pending_message(), \
36 isolate); \
37 if (!isolate->is_catchable_by_javascript(*current_error)) { \
38 return return_value; \
39 } \
40 isolate->clear_internal_exception(); \
41 isolate->clear_pending_message(); \
42 HandleErrorInDisposal(isolate, disposable_stack, current_error, \
43 current_error_message); \
44 } while (false)
45
46// https://arai-a.github.io/ecma262-compare/?pr=3000&id=sec-disposeresources
48 Isolate* isolate, DirectHandle<JSDisposableStackBase> disposable_stack,
49 DisposableStackResourcesType resources_type) {
50 DCHECK(!IsUndefined(disposable_stack->stack()));
51 DCHECK_EQ(disposable_stack->state(), DisposableStackState::kDisposed);
52
53 DirectHandle<FixedArray> stack(disposable_stack->stack(), isolate);
54
55 // 1. Let needsAwait be false.
56 // 2. Let hasAwaited be false.
57 // `false` is assigned to both in the initialization of the DisposableStack.
58
59 int length = disposable_stack->length();
60
62
63 // 3. For each element resource of
64 // disposeCapability.[[DisposableResourceStack]], in reverse list order, do
65 while (length > 0) {
66 // a. Let value be resource.[[ResourceValue]].
67 // b. Let hint be resource.[[Hint]].
68 // c. Let method be resource.[[DisposeMethod]].
69 Tagged<Object> stack_type = stack->get(--length);
70
71 Tagged<Object> tagged_method = stack->get(--length);
72 DirectHandle<Object> method(tagged_method, isolate);
73
74 Tagged<Object> tagged_value = stack->get(--length);
75 DirectHandle<Object> value(tagged_value, isolate);
76
77 DirectHandle<Object> args[] = {value};
78
79 auto stack_type_case = static_cast<int>(Cast<Smi>(stack_type).value());
80 DisposeMethodCallType call_type =
81 DisposeCallTypeBit::decode(stack_type_case);
82 DisposeMethodHint hint = DisposeHintBit::decode(stack_type_case);
83
84 // d. If hint is sync-dispose and needsAwait is true and hasAwaited is
85 // false, then
86 // i. Perform ! Await(undefined).
87 // ii. Set needsAwait to false.
88
90 disposable_stack->needs_await() == true &&
91 disposable_stack->has_awaited() == false) {
92 // i. Perform ! Await(undefined).
93 // ii. Set needsAwait to false.
94 disposable_stack->set_needs_await(false);
95
97 isolate, isolate->factory()->undefined_value());
98 }
99
100 // e. If method is not undefined, then
101 if (!IsUndefined(*method)) {
102 // i. Let result be Completion(Call(method, value)).
103
105 result = Execution::Call(isolate, method, value, {});
106 } else if (call_type == DisposeMethodCallType::kValueIsArgument) {
107 result = Execution::Call(isolate, method,
108 isolate->factory()->undefined_value(),
110 }
111
112 DirectHandle<Object> result_handle;
113 // ii. If result is a normal completion and hint is async-dispose, then
114 // 1. Set result to Completion(Await(result.[[Value]])).
115 // 2. Set hasAwaited to true.
116 if (result.ToHandle(&result_handle)) {
119 disposable_stack->set_length(length);
120
121 disposable_stack->set_has_awaited(true);
122
123 MaybeDirectHandle<JSReceiver> resolved_promise =
124 ResolveAPromiseWithValueAndReturnIt(isolate, result_handle);
125
126 if (resolved_promise.is_null()) {
127 // iii. If result is a throw completion, then
128 // 1. If completion is a throw completion, then
129 // a. Set result to result.[[Value]].
130 // b. Let suppressed be completion.[[Value]].
131 // c. Let error be a newly created SuppressedError object.
132 // d. Perform CreateNonEnumerableDataPropertyOrThrow(error,
133 // "error", result).
134 // e. Perform CreateNonEnumerableDataPropertyOrThrow(error,
135 // "suppressed", suppressed).
136 // f. Set completion to ThrowCompletion(error).
137 // 2. Else,
138 // a. Set completion to result.
139 CHECK_EXCEPTION_ON_DISPOSAL(isolate, disposable_stack, {});
140 } else {
141 return resolved_promise;
142 }
143 }
144 } else {
145 CHECK_EXCEPTION_ON_DISPOSAL(isolate, disposable_stack, {});
146 }
147 } else {
148 // Else,
149 // i. Assert: hint is async-dispose.
151 // ii. Set needsAwait to true.
152 // iii. NOTE: This can only indicate a case where either null or
153 // undefined was the initialized value of an await using declaration.
154 disposable_stack->set_length(length);
155 disposable_stack->set_needs_await(true);
156 }
157 }
158
159 // 4. If needsAwait is true and hasAwaited is false, then
160 // a. Perform ! Await(undefined).
161 if (disposable_stack->needs_await() == true &&
162 disposable_stack->has_awaited() == false) {
163 disposable_stack->set_length(length);
164 disposable_stack->set_has_awaited(true);
165
167 isolate, isolate->factory()->undefined_value());
168 }
169
170 // 5. NOTE: After disposeCapability has been disposed, it will never be used
171 // again. The contents of disposeCapability.[[DisposableResourceStack]] can be
172 // discarded in implementations, such as by garbage collection, at this point.
173 // 6. Set disposeCapability.[[DisposableResourceStack]] to a new empty List.
174 disposable_stack->set_stack(ReadOnlyRoots(isolate).empty_fixed_array());
175 disposable_stack->set_length(0);
176
177 Handle<Object> existing_error_handle(disposable_stack->error(), isolate);
178 DirectHandle<Object> existing_error_message_handle(
179 disposable_stack->error_message(), isolate);
180 disposable_stack->set_error(*(isolate->factory()->uninitialized_value()));
181 disposable_stack->set_error_message(
182 *(isolate->factory()->uninitialized_value()));
183
184 // 7. Return ? completion.
185 if (!IsUninitialized(*existing_error_handle)) {
186 if (disposable_stack->suppressed_error_created() == true) {
187 // Created SuppressedError is intentionally suppressed here for debug.
188 SuppressDebug while_processing(isolate->debug());
189 isolate->Throw(*existing_error_handle);
190 } else {
191 isolate->ReThrow(*existing_error_handle, *existing_error_message_handle);
192 }
194 }
195 return isolate->factory()->true_value();
196}
197
200 Isolate* isolate, DirectHandle<Object> value) {
201 DirectHandle<JSFunction> promise_function = isolate->promise_function();
202 DirectHandle<Object> args[] = {value};
205 isolate, result,
206 Execution::CallBuiltin(isolate, isolate->promise_resolve(),
207 promise_function, base::VectorOf(args)),
209 return Cast<JSReceiver>(result);
210}
211
213 Isolate* isolate,
214 DirectHandle<JSDisposableStackBase> async_disposable_stack,
215 DirectHandle<JSPromise> outer_promise) {
217
218 bool done;
219 do {
220 done = true;
221
222 // 6. Let result be
223 // DisposeResources(asyncDisposableStack.[[DisposeCapability]],
224 // NormalCompletion(undefined)).
225 result = DisposeResources(isolate, async_disposable_stack,
227
228 DirectHandle<Object> result_handle;
229
230 if (result.ToHandle(&result_handle)) {
231 if (!IsTrue(*result_handle)) {
232 DirectHandle<Context> async_disposable_stack_context =
233 isolate->factory()->NewBuiltinContext(
234 isolate->native_context(),
235 static_cast<int>(
237 kLength));
238 async_disposable_stack_context->set(
239 static_cast<int>(JSDisposableStackBase::
240 AsyncDisposableStackContextSlots::kStack),
241 *async_disposable_stack);
242 async_disposable_stack_context->set(
243 static_cast<int>(
245 kOuterPromise),
246 *outer_promise);
247
248 DirectHandle<JSFunction> on_fulfilled =
250 isolate,
251 isolate->factory()
252 ->async_disposable_stack_on_fulfilled_shared_fun(),
253 async_disposable_stack_context}
254 .Build();
255
256 DirectHandle<JSFunction> on_rejected =
258 isolate,
259 isolate->factory()
260 ->async_disposable_stack_on_rejected_shared_fun(),
261 async_disposable_stack_context}
262 .Build();
263
264 DirectHandle<Object> args[] = {on_fulfilled, on_rejected};
265 if (Execution::CallBuiltin(isolate, isolate->perform_promise_then(),
266 Cast<JSPromise>(result_handle),
268 .is_null()) {
269 CHECK_EXCEPTION_ON_DISPOSAL(isolate, async_disposable_stack,
270 Nothing<bool>());
271 done = false;
272 }
273 } else {
274 // 8. Perform ! Call(promiseCapability.[[Resolve]], undefined, « result
275 // »).
276 if (JSPromise::Resolve(outer_promise,
277 isolate->factory()->undefined_value())
278 .is_null()) {
279 CHECK_EXCEPTION_ON_DISPOSAL(isolate, async_disposable_stack,
280 Nothing<bool>());
281 done = false;
282 }
283 }
284 } else {
285 // 7. IfAbruptRejectPromise(result, promiseCapability).
286 DirectHandle<Object> exception(isolate->exception(), isolate);
287 if (!isolate->is_catchable_by_javascript(*exception)) {
288 return Nothing<bool>();
289 }
290 isolate->clear_internal_exception();
291 isolate->clear_pending_message();
292 JSPromise::Reject(outer_promise, exception);
293 }
294 } while (!done);
295
296 return Just(true);
297}
298
299} // namespace internal
300} // namespace v8
static constexpr T decode(U value)
Definition bit-field.h:66
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 V8_WARN_UNUSED_RESULT MaybeHandle< Object > CallBuiltin(Isolate *isolate, DirectHandle< JSFunction > builtin, DirectHandle< Object > receiver, base::Vector< const DirectHandle< Object > > args)
Definition execution.cc:545
V8_WARN_UNUSED_RESULT Handle< JSFunction > Build()
Definition factory.cc:4732
static Maybe< bool > NextDisposeAsyncIteration(Isolate *isolate, DirectHandle< JSDisposableStackBase > async_disposable_stack, DirectHandle< JSPromise > outer_promise)
static MaybeDirectHandle< Object > DisposeResources(Isolate *isolate, DirectHandle< JSDisposableStackBase > disposable_stack, DisposableStackResourcesType resources_type)
static MaybeDirectHandle< JSReceiver > ResolveAPromiseWithValueAndReturnIt(Isolate *isolate, DirectHandle< Object > value)
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
V8_INLINE bool is_null() const
#define ASSIGN_RETURN_ON_EXCEPTION_VALUE(isolate, dst, call, value)
Definition isolate.h:276
base::Vector< const DirectHandle< Object > > args
Definition execution.cc:74
Isolate * isolate
#define CHECK_EXCEPTION_ON_DISPOSAL(isolate, disposable_stack, return_value)
ZoneVector< RpoNumber > & result
ZoneStack< RpoNumber > & stack
constexpr Vector< T > VectorOf(T *start, size_t size)
Definition vector.h:360
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
Maybe< T > Nothing()
Definition v8-maybe.h:112
Maybe< T > Just(const T &t)
Definition v8-maybe.h:117
#define DCHECK_NE(v1, v2)
Definition logging.h:486
#define DCHECK(condition)
Definition logging.h:482
#define DCHECK_EQ(v1, v2)
Definition logging.h:485
Symbol method