| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
1 parent a640543 commit 879fdc4
8 files changed
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -41,7 +41,7 @@ | |||
| 41 | 41 | ||
| 42 | 42 | # Reset this number to 0 on major V8 upgrades. | |
| 43 | 43 | # Increment by one for each non-official patch applied to deps/v8. | |
| 44 | - 'v8_embedder_string': '-node.23', | ||
| 44 | + 'v8_embedder_string': '-node.24', | ||
| 45 | 45 | ||
| 46 | 46 | ##### V8 defaults for Node.js ##### | |
| 47 | 47 | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -13,11 +13,9 @@ namespace internal { | |||
| 13 | 13 | class PromiseBuiltins { | |
| 14 | 14 | public: | |
| 15 | 15 | enum PromiseResolvingFunctionContextSlot { | |
| 16 | - // The promise which resolve/reject callbacks fulfill. | ||
| 17 | - kPromiseSlot = Context::MIN_CONTEXT_SLOTS, | ||
| 18 | - | ||
| 19 | - // Whether the callback was already invoked. | ||
| 20 | - kAlreadyResolvedSlot, | ||
| 16 | + // The promise which resolve/reject callbacks fulfill, or Undefined | ||
| 17 | + // if already resolved. | ||
| 18 | + kPromiseIfNotResolvedSlot = Context::MIN_CONTEXT_SLOTS, | ||
| 21 | 19 | ||
| 22 | 20 | // Whether to trigger a debug event or not. Used in catch | |
| 23 | 21 | // prediction. | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -265,8 +265,8 @@ const kPromiseCapabilitySize: | |||
| 265 | 265 | type PromiseResolvingFunctionContext extends FunctionContext; | |
| 266 | 266 | extern enum PromiseResolvingFunctionContextSlot extends intptr | |
| 267 | 267 | constexpr 'PromiseBuiltins::PromiseResolvingFunctionContextSlot' { | |
| 268 | - kPromiseSlot: Slot<PromiseResolvingFunctionContext, JSPromise>, | ||
| 269 | - kAlreadyResolvedSlot: Slot<PromiseResolvingFunctionContext, Boolean>, | ||
| 268 | + kPromiseIfNotResolvedSlot: | ||
| 269 | + Slot<PromiseResolvingFunctionContext, JSPromise|Undefined>, | ||
| 270 | 270 | kDebugEventSlot: Slot<PromiseResolvingFunctionContext, Boolean>, | |
| 271 | 271 | kPromiseContextLength | |
| 272 | 272 | } | |
@@ -390,25 +390,29 @@ transitioning builtin NewPromiseCapability( | |||
| 390 | 390 | transitioning javascript builtin PromiseCapabilityDefaultReject( | |
| 391 | 391 | js-implicit context: Context, receiver: JSAny)(reason: JSAny): JSAny { | |
| 392 | 392 | const context = %RawDownCast<PromiseResolvingFunctionContext>(context); | |
| 393 | - // 2. Let promise be F.[[Promise]]. | ||
| 394 | - const promise = | ||
| 395 | - *ContextSlot(context, PromiseResolvingFunctionContextSlot::kPromiseSlot); | ||
| 396 | - | ||
| 397 | - // 3. Let alreadyResolved be F.[[AlreadyResolved]]. | ||
| 398 | - const alreadyResolved = *ContextSlot( | ||
| 399 | - context, PromiseResolvingFunctionContextSlot::kAlreadyResolvedSlot); | ||
| 400 | - | ||
| 401 | - // 4. If alreadyResolved.[[Value]] is true, return undefined. | ||
| 402 | - if (alreadyResolved == True) { | ||
| 403 | - return Undefined; | ||
| 393 | + // 2. Let promise be promiseOrEmpty.[[Value]]. | ||
| 394 | + const promiseOrEmpty = | ||
| 395 | + *ContextSlot( | ||
| 396 | + context, PromiseResolvingFunctionContextSlot::kPromiseIfNotResolvedSlot); | ||
| 397 | + | ||
| 398 | + // 1. If promiseOrEmpty.[[Value]] is ~empty~, return undefined. | ||
| 399 | + let promise: JSPromise; | ||
| 400 | + typeswitch (promiseOrEmpty) { | ||
| 401 | + case (Undefined): { | ||
| 402 | + return Undefined; | ||
| 403 | + } | ||
| 404 | + case (p: JSPromise): { | ||
| 405 | + promise = p; | ||
| 406 | + } | ||
| 404 | 407 | } | |
| 405 | 408 | ||
| 406 | - // 5. Set alreadyResolved.[[Value]] to true. | ||
| 409 | + // 3. Set promiseOrEmpty.[[Value]] to ~empty~. | ||
| 407 | 410 | *ContextSlot( | |
| 408 | - context, PromiseResolvingFunctionContextSlot::kAlreadyResolvedSlot) = | ||
| 409 | - True; | ||
| 411 | + context, PromiseResolvingFunctionContextSlot::kPromiseIfNotResolvedSlot) = | ||
| 412 | + Undefined; | ||
| 410 | 413 | ||
| 411 | - // 6. Return RejectPromise(promise, reason). | ||
| 414 | + // 4. Perform RejectPromise(promise, reason). | ||
| 415 | + // 5. Return undefined. | ||
| 412 | 416 | const debugEvent = *ContextSlot( | |
| 413 | 417 | context, PromiseResolvingFunctionContextSlot::kDebugEventSlot); | |
| 414 | 418 | return RejectPromise(promise, reason, debugEvent); | |
@@ -418,23 +422,26 @@ transitioning javascript builtin PromiseCapabilityDefaultReject( | |||
| 418 | 422 | transitioning javascript builtin PromiseCapabilityDefaultResolve( | |
| 419 | 423 | js-implicit context: Context, receiver: JSAny)(resolution: JSAny): JSAny { | |
| 420 | 424 | const context = %RawDownCast<PromiseResolvingFunctionContext>(context); | |
| 421 | - // 2. Let promise be F.[[Promise]]. | ||
| 422 | - const promise: JSPromise = | ||
| 423 | - *ContextSlot(context, PromiseResolvingFunctionContextSlot::kPromiseSlot); | ||
| 424 | - | ||
| 425 | - // 3. Let alreadyResolved be F.[[AlreadyResolved]]. | ||
| 426 | - const alreadyResolved: Boolean = *ContextSlot( | ||
| 427 | - context, PromiseResolvingFunctionContextSlot::kAlreadyResolvedSlot); | ||
| 428 | - | ||
| 429 | - // 4. If alreadyResolved.[[Value]] is true, return undefined. | ||
| 430 | - if (alreadyResolved == True) { | ||
| 431 | - return Undefined; | ||
| 425 | + // 2. Let promise be promiseOrEmpty.[[Value]]. | ||
| 426 | + const promiseOrEmpty = | ||
| 427 | + *ContextSlot( | ||
| 428 | + context, PromiseResolvingFunctionContextSlot::kPromiseIfNotResolvedSlot); | ||
| 429 | + | ||
| 430 | + // 1. If promiseOrEmpty.[[Value]] is ~empty~, return undefined. | ||
| 431 | + let promise: JSPromise; | ||
| 432 | + typeswitch (promiseOrEmpty) { | ||
| 433 | + case (Undefined): { | ||
| 434 | + return Undefined; | ||
| 435 | + } | ||
| 436 | + case (p: JSPromise): { | ||
| 437 | + promise = p; | ||
| 438 | + } | ||
| 432 | 439 | } | |
| 433 | 440 | ||
| 434 | - // 5. Set alreadyResolved.[[Value]] to true. | ||
| 441 | + // 3. Set promiseOrEmpty.[[Value]] to ~empty~. | ||
| 435 | 442 | *ContextSlot( | |
| 436 | - context, PromiseResolvingFunctionContextSlot::kAlreadyResolvedSlot) = | ||
| 437 | - True; | ||
| 443 | + context, PromiseResolvingFunctionContextSlot::kPromiseIfNotResolvedSlot) = | ||
| 444 | + Undefined; | ||
| 438 | 445 | ||
| 439 | 446 | // The rest of the logic (and the catch prediction) is | |
| 440 | 447 | // encapsulated in the dedicated ResolvePromise builtin. | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -63,17 +63,14 @@ macro CreatePromiseResolvingFunctionsContext( | |||
| 63 | 63 | nativeContext, | |
| 64 | 64 | PromiseResolvingFunctionContextSlot::kPromiseContextLength)); | |
| 65 | 65 | InitContextSlot( | |
| 66 | - resolveContext, PromiseResolvingFunctionContextSlot::kPromiseSlot, | ||
| 67 | - promise); | ||
| 68 | - InitContextSlot( | ||
| 69 | - resolveContext, PromiseResolvingFunctionContextSlot::kAlreadyResolvedSlot, | ||
| 70 | - False); | ||
| 66 | + resolveContext, | ||
| 67 | + PromiseResolvingFunctionContextSlot::kPromiseIfNotResolvedSlot, promise); | ||
| 71 | 68 | InitContextSlot( | |
| 72 | 69 | resolveContext, PromiseResolvingFunctionContextSlot::kDebugEventSlot, | |
| 73 | 70 | debugEvent); | |
| 74 | 71 | static_assert( | |
| 75 | 72 | PromiseResolvingFunctionContextSlot::kPromiseContextLength == | |
| 76 | - ContextSlot::MIN_CONTEXT_SLOTS + 3); | ||
| 73 | + ContextSlot::MIN_CONTEXT_SLOTS + 2); | ||
| 77 | 74 | return resolveContext; | |
| 78 | 75 | } | |
| 79 | 76 | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -2543,10 +2543,8 @@ TNode<Object> PromiseBuiltinReducerAssembler::ReducePromiseConstructor( | |||
| 2543 | 2543 | // Allocate a promise context for the closures below. | |
| 2544 | 2544 | TNode<Context> promise_context = CreateFunctionContext( | |
| 2545 | 2545 | native_context, context, PromiseBuiltins::kPromiseContextLength); | |
| 2546 | - StoreContextNoCellSlot(promise_context, PromiseBuiltins::kPromiseSlot, | ||
| 2547 | - promise); | ||
| 2548 | - StoreContextNoCellSlot(promise_context, PromiseBuiltins::kAlreadyResolvedSlot, | ||
| 2549 | - FalseConstant()); | ||
| 2546 | + StoreContextNoCellSlot(promise_context, | ||
| 2547 | + PromiseBuiltins::kPromiseIfNotResolvedSlot, promise); | ||
| 2550 | 2548 | StoreContextNoCellSlot(promise_context, PromiseBuiltins::kDebugEventSlot, | |
| 2551 | 2549 | TrueConstant()); | |
| 2552 | 2550 | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -1253,9 +1253,12 @@ void CaptureAsyncStackTrace(Isolate* isolate, DirectHandle<JSPromise> promise, | |||
| 1253 | 1253 | DirectHandle<JSFunction> function( | |
| 1254 | 1254 | Cast<JSFunction>(reaction->fulfill_handler()), isolate); | |
| 1255 | 1255 | DirectHandle<Context> context(function->context(), isolate); | |
| 1256 | - promise = direct_handle( | ||
| 1257 | - Cast<JSPromise>(context->GetNoCell(PromiseBuiltins::kPromiseSlot)), | ||
| 1258 | - isolate); | ||
| 1256 | + Tagged<Object> promise_or_undefined = | ||
| 1257 | + context->GetNoCell(PromiseBuiltins::kPromiseIfNotResolvedSlot); | ||
| 1258 | + if (!TryCast(direct_handle(promise_or_undefined, isolate), &promise)) { | ||
| 1259 | + DCHECK(IsUndefined(promise_or_undefined)); | ||
| 1260 | + return; | ||
| 1261 | + } | ||
| 1259 | 1262 | } else { | |
| 1260 | 1263 | // We have some generic promise chain here, so try to | |
| 1261 | 1264 | // continue with the chained promise on the reaction | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -3037,7 +3037,8 @@ TEST(CreatePromiseResolvingFunctionsContext) { | |||
| 3037 | 3037 | DirectHandle<Context> context_js = Cast<Context>(result); | |
| 3038 | 3038 | CHECK_EQ(isolate->root(RootIndex::kEmptyScopeInfo), context_js->scope_info()); | |
| 3039 | 3039 | CHECK_EQ(*isolate->native_context(), context_js->native_context()); | |
| 3040 | - CHECK(IsJSPromise(context_js->GetNoCell(PromiseBuiltins::kPromiseSlot))); | ||
| 3040 | + CHECK(IsJSPromise( | ||
| 3041 | + context_js->GetNoCell(PromiseBuiltins::kPromiseIfNotResolvedSlot))); | ||
| 3041 | 3042 | CHECK_EQ(ReadOnlyRoots(isolate).false_value(), | |
| 3042 | 3043 | context_js->GetNoCell(PromiseBuiltins::kDebugEventSlot)); | |
| 3043 | 3044 | } | |
@@ -3246,7 +3247,8 @@ TEST(NewPromiseCapability) { | |||
| 3246 | 3247 | CHECK_EQ(*isolate->native_context(), callback_context->native_context()); | |
| 3247 | 3248 | CHECK_EQ(PromiseBuiltins::kPromiseContextLength, | |
| 3248 | 3249 | callback_context->length()); | |
| 3249 | - CHECK_EQ(callback_context->GetNoCell(PromiseBuiltins::kPromiseSlot), | ||
| 3250 | + CHECK_EQ(callback_context->GetNoCell( | ||
| 3251 | + PromiseBuiltins::kPromiseIfNotResolvedSlot), | ||
| 3250 | 3252 | result->promise()); | |
| 3251 | 3253 | } | |
| 3252 | 3254 | } | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -0,0 +1,26 @@ | |||
| 1 | + // Copyright 2026 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 | + // Flags: --expose-gc | ||
| 6 | + | ||
| 7 | + const pending = new Promise(() => {}); | ||
| 8 | + | ||
| 9 | + (async function () { | ||
| 10 | + let wr; | ||
| 11 | + | ||
| 12 | + await (async function () { | ||
| 13 | + const payload = { }; | ||
| 14 | + wr = new WeakRef(payload); | ||
| 15 | + const resolved = Promise.resolve(payload); | ||
| 16 | + // The pending Promise should not prevent GC of the race Promise once the race settles. | ||
| 17 | + await Promise.race([pending, resolved]); | ||
| 18 | + })(); | ||
| 19 | + | ||
| 20 | + await gc({ type: 'major', execution: 'async' }); | ||
| 21 | + | ||
| 22 | + assertEquals(undefined, wr.deref()); | ||
| 23 | + })().catch((e) => { | ||
| 24 | + console.error(e); | ||
| 25 | + quit(1); | ||
| 26 | + }); | ||
| Back | FazBrowse Home | New Git URL |
0 commit comments