Skip to content

Commit 19dc01a

Browse files
committed
[JSC] Optimize returned promise from async function when it does not have await
https://bugs.webkit.org/show_bug.cgi?id=313029 rdar://175369313 Reviewed by Yijia Huang. Let's optimize returned promise from async function when we do not have `await`. Currently, we are calling operationFulfillPromiseFirstResolving for the created promise when resolving. But since we know that we do not have `await`, we do not need to create a promise a-priori, and we can just create and resolve a promise when returning. This is more efficient since we can fold it into a fulfilled promise allocation when input is proven as non-object. Test: JSTests/stress/async-function-empty-resolved-promise.js * JSTests/stress/async-function-empty-resolved-promise.js: Added. (shouldBe): (async empty): (async emptyArrow): (C.prototype.async emptyMethod): (C): (const.obj.async emptyShort): (async onlyUsing): (async onlyAwaitUsing): (async returnsThenable): (async throwsLiteral): (async throwsError): (thrower): (async defaultThrower): (async defaultThrowerBody): (async run.async returnsPromise): (async run): * Source/JavaScriptCore/builtins/BuiltinNames.h: * Source/JavaScriptCore/bytecode/LinkTimeConstant.h: * Source/JavaScriptCore/bytecompiler/BytecodeGenerator.cpp: (JSC::BytecodeGenerator::generate): (JSC::BytecodeGenerator::BytecodeGenerator): * Source/JavaScriptCore/bytecompiler/NodesCodegen.cpp: (JSC::FunctionNode::emitBytecode): * Source/JavaScriptCore/dfg/DFGAbstractInterpreterInlines.h: (JSC::DFG::AbstractInterpreter<AbstractStateType>::executeEffects): * Source/JavaScriptCore/dfg/DFGByteCodeParser.cpp: (JSC::DFG::ByteCodeParser::handleIntrinsicCall): * Source/JavaScriptCore/dfg/DFGClobberize.h: (JSC::DFG::clobberize): * Source/JavaScriptCore/dfg/DFGDoesGC.cpp: (JSC::DFG::doesGC): * Source/JavaScriptCore/dfg/DFGFixupPhase.cpp: (JSC::DFG::FixupPhase::fixupNode): * Source/JavaScriptCore/dfg/DFGNodeType.h: * Source/JavaScriptCore/dfg/DFGOperations.cpp: (JSC::DFG::JSC_DEFINE_JIT_OPERATION): * Source/JavaScriptCore/dfg/DFGOperations.h: * Source/JavaScriptCore/dfg/DFGPredictionPropagationPhase.cpp: * Source/JavaScriptCore/dfg/DFGSafeToExecute.h: (JSC::DFG::safeToExecute): * Source/JavaScriptCore/dfg/DFGSpeculativeJIT.cpp: * Source/JavaScriptCore/dfg/DFGSpeculativeJIT.h: * Source/JavaScriptCore/dfg/DFGSpeculativeJIT32_64.cpp: (JSC::DFG::SpeculativeJIT::compile): * Source/JavaScriptCore/dfg/DFGSpeculativeJIT64.cpp: (JSC::DFG::SpeculativeJIT::compile): * Source/JavaScriptCore/ftl/FTLCapabilities.cpp: (JSC::FTL::canCompile): * Source/JavaScriptCore/ftl/FTLLowerDFGToB3.cpp: (JSC::FTL::DFG::LowerDFGToB3::compileNode): (JSC::FTL::DFG::LowerDFGToB3::compileCompareStrictEq): * Source/JavaScriptCore/parser/Nodes.h: (JSC::ScopeNode::isEmptyBody const): * Source/JavaScriptCore/runtime/Intrinsic.h: * Source/JavaScriptCore/runtime/JSGlobalObject.cpp: (JSC::JSC_DEFINE_HOST_FUNCTION): (JSC::JSGlobalObject::init): Canonical link: https://commits.webkit.org/311883@main
1 parent 1d23413 commit 19dc01a

24 files changed

Lines changed: 383 additions & 49 deletions
Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
//@ requireOptions("--useExplicitResourceManagement=1")
2+
3+
function shouldBe(actual, expected) {
4+
if (actual !== expected)
5+
throw new Error(`bad value: ${actual} expected ${expected}`);
6+
}
7+
8+
// Empty async function: must hit the `return @newResolvedPromise(undefined)` short-circuit.
9+
async function empty() {}
10+
async function emptyArrow() {}
11+
class C { async emptyMethod() {} }
12+
const obj = { async emptyShort() {} };
13+
14+
// `await using` declaration is still classified as "without await" (no AwaitExpression),
15+
// but hasStatements() is true, so we must NOT take the empty short-circuit — the disposal
16+
// stack still has to run.
17+
let disposedSync = 0;
18+
let disposedAsync = 0;
19+
async function onlyUsing() {
20+
using x = { [Symbol.dispose]() { disposedSync++; } };
21+
}
22+
async function onlyAwaitUsing() {
23+
await using y = { [Symbol.asyncDispose]() { disposedAsync++; return Promise.resolve(); } };
24+
}
25+
26+
// No-await body that returns a thenable: the resolved promise must pipe from the thenable,
27+
// not return it as-is (spec compliance — async function wrapper allocates its own promise).
28+
async function returnsThenable() {
29+
return { then(r) { r(42); } };
30+
}
31+
32+
// No-await body that throws: must return a rejected promise with the thrown value.
33+
async function throwsLiteral() { throw "boom"; }
34+
async function throwsError() { throw new Error("e"); }
35+
36+
// No-await body with default arg that throws — crashes without the param-catch fix.
37+
function thrower() { throw new Error("default-arg"); }
38+
async function defaultThrower(arg = thrower()) {}
39+
async function defaultThrowerBody(arg = thrower()) { return arg; }
40+
41+
async function run() {
42+
shouldBe(await empty(), undefined);
43+
shouldBe(await emptyArrow(), undefined);
44+
shouldBe(await new C().emptyMethod(), undefined);
45+
shouldBe(await obj.emptyShort(), undefined);
46+
47+
shouldBe(await onlyUsing(), undefined);
48+
shouldBe(disposedSync, 1);
49+
50+
shouldBe(await onlyAwaitUsing(), undefined);
51+
shouldBe(disposedAsync, 1);
52+
53+
// Thenable pipes through — final resolution is 42, not the thenable object.
54+
shouldBe(await returnsThenable(), 42);
55+
56+
// The wrapper promise must be a brand-new promise, not === to any returned inner promise.
57+
const inner = Promise.resolve(7);
58+
async function returnsPromise() { return inner; }
59+
const outer = returnsPromise();
60+
if (outer === inner)
61+
throw new Error("wrapper must not return the input promise");
62+
shouldBe(await outer, 7);
63+
64+
// Rejections from throws and default-arg throws.
65+
let caught;
66+
try { await throwsLiteral(); } catch (e) { caught = e; }
67+
shouldBe(caught, "boom");
68+
69+
try { await throwsError(); caught = null; } catch (e) { caught = e.message; }
70+
shouldBe(caught, "e");
71+
72+
try { await defaultThrower(); caught = null; } catch (e) { caught = e.message; }
73+
shouldBe(caught, "default-arg");
74+
75+
try { await defaultThrowerBody(); caught = null; } catch (e) { caught = e.message; }
76+
shouldBe(caught, "default-arg");
77+
}
78+
79+
// Drive enough iterations to tier up through DFG and FTL.
80+
(async () => {
81+
for (let i = 0; i < 1000; i++)
82+
await run();
83+
})().then(() => {}, e => { throw e; });

‎Source/JavaScriptCore/builtins/BuiltinNames.h‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,8 @@ namespace JSC {
8181
macro(resolvePromiseWithFirstResolvingFunctionCallCheck) \
8282
macro(rejectPromiseWithFirstResolvingFunctionCallCheck) \
8383
macro(fulfillPromiseWithFirstResolvingFunctionCallCheck) \
84+
macro(newResolvedPromise) \
85+
macro(newRejectedPromise) \
8486
macro(resolveWithInternalMicrotaskForAsyncAwait) \
8587
macro(asyncGeneratorQueueEnqueue) \
8688
macro(asyncGeneratorQueueDequeueResolve) \

‎Source/JavaScriptCore/bytecode/LinkTimeConstant.h‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,8 @@ class JSGlobalObject;
6161
v(resolvePromiseWithFirstResolvingFunctionCallCheck, nullptr) \
6262
v(rejectPromiseWithFirstResolvingFunctionCallCheck, nullptr) \
6363
v(fulfillPromiseWithFirstResolvingFunctionCallCheck, nullptr) \
64+
v(newResolvedPromise, nullptr) \
65+
v(newRejectedPromise, nullptr) \
6466
v(resolveWithInternalMicrotaskForAsyncAwait, nullptr) \
6567
v(asyncGeneratorQueueEnqueue, nullptr) \
6668
v(asyncGeneratorQueueDequeueResolve, nullptr) \

‎Source/JavaScriptCore/bytecompiler/BytecodeGenerator.cpp‎

Lines changed: 22 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -306,16 +306,25 @@ ParserError BytecodeGenerator::generate(unsigned& size)
306306
AsyncFuncParametersTryCatchInfo& info = m_asyncFuncParametersTryCatchInfo.value();
307307
ASSERT(info.catchStartLabel && info.thrownValue);
308308
emitLabel(*info.catchStartLabel.get());
309-
// @rejectPromiseWithFirstResolvingFunctionCallCheck(@promise, thrownValue);
310-
// return @promise;
311-
RefPtr<RegisterID> rejectPromise = moveLinkTimeConstant(nullptr, LinkTimeConstant::rejectPromiseWithFirstResolvingFunctionCallCheck);
312-
CallArguments args(*this, nullptr, 2);
313-
emitLoad(args.thisRegister(), jsUndefined());
314-
move(args.argumentRegister(0), promiseRegister());
315-
move(args.argumentRegister(1), info.thrownValue.get());
316309
JSTextPosition divot(m_scopeNode->firstLine(), m_scopeNode->startOffset(), m_scopeNode->lineStartOffset());
317-
emitCallIgnoreResult(newTemporary(), rejectPromise.get(), NoExpectedFunction, args, divot, divot, divot, DebuggableCall::No);
318-
emitReturn(promiseRegister());
310+
if (promiseRegister()) {
311+
RefPtr<RegisterID> rejectPromise = moveLinkTimeConstant(nullptr, LinkTimeConstant::rejectPromiseWithFirstResolvingFunctionCallCheck);
312+
CallArguments args(*this, nullptr, 2);
313+
emitLoad(args.thisRegister(), jsUndefined());
314+
move(args.argumentRegister(0), promiseRegister());
315+
move(args.argumentRegister(1), info.thrownValue.get());
316+
emitCallIgnoreResult(newTemporary(), rejectPromise.get(), NoExpectedFunction, args, divot, divot, divot, DebuggableCall::No);
317+
emitReturn(promiseRegister());
318+
} else {
319+
// If we are not creating a promise yet, we can just do `return @newRejectedPromise(thrownValue)`.
320+
RefPtr<RegisterID> newRejectedPromise = moveLinkTimeConstant(nullptr, LinkTimeConstant::newRejectedPromise);
321+
CallArguments args(*this, nullptr, 1);
322+
emitLoad(args.thisRegister(), jsUndefined());
323+
move(args.argumentRegister(0), info.thrownValue.get());
324+
RefPtr<RegisterID> result = newTemporary();
325+
emitCall(result.get(), newRejectedPromise.get(), NoExpectedFunction, args, divot, divot, divot, DebuggableCall::No);
326+
emitReturn(result.get());
327+
}
319328
}
320329

321330
m_staticPropertyAnalyzer.kill();
@@ -822,9 +831,10 @@ IGNORE_GCC_WARNINGS_END
822831
bool isAsyncFunctionWithoutAwait = m_scopeNode->isAsyncFunctionWithoutAwait();
823832
// Check if this async function body doesn't use await.
824833
// If so, we can skip generator creation entirely.
825-
if (!isAsyncFunctionWithoutAwait)
834+
if (!isAsyncFunctionWithoutAwait) {
826835
m_generatorRegister = addVar();
827-
m_promiseRegister = addVar();
836+
m_promiseRegister = addVar();
837+
}
828838

829839
bool willEmitToThis = false;
830840
if (parseMode != SourceParseMode::AsyncArrowFunctionMode) {
@@ -838,9 +848,8 @@ IGNORE_GCC_WARNINGS_END
838848
if (willEmitToThis)
839849
emitToThis();
840850

841-
emitNewPromise(promiseRegister());
842-
843851
if (!isAsyncFunctionWithoutAwait) {
852+
emitNewPromise(promiseRegister());
844853
emitNewGenerator(m_generatorRegister);
845854
emitPutInternalField(generatorRegister(), static_cast<unsigned>(JSGenerator::Field::Context), promiseRegister());
846855
}

‎Source/JavaScriptCore/bytecompiler/NodesCodegen.cpp‎

Lines changed: 63 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -5516,13 +5516,39 @@ void FunctionNode::emitBytecode(BytecodeGenerator& generator, RegisterID*)
55165516
// - catch: Reject promise with the exception
55175517
// - finally: Resolve promise with completion value and return promise
55185518
//
5519-
// The finally block always resolves and returns the promise. Since promise
5520-
// resolution only takes effect on the first call, calling resolve after
5521-
// reject (from the catch block) is a no-op.
5522-
5519+
// try {
5520+
// body
5521+
// transfer completion-value with completion-type = normal / return
5522+
// } catch (error) {
5523+
// transfer error with completion-type = throw
5524+
// } finally {
5525+
// get value with completion-type
5526+
// if (completion-type === throw)
5527+
// return @newRejectedPromise(completion-value);
5528+
// return @newResolvedPromise(completion-value);
5529+
// }
55235530
if (generator.parseMode() == SourceParseMode::AsyncArrowFunctionMode && generator.isThisUsedInInnerArrowFunction())
55245531
generator.emitLoadThisFromArrowFunctionLexicalEnvironment();
55255532

5533+
// If async function is just used for a signaling, not having a body, then just convert it to @newResolvedPromise(undefined).
5534+
//
5535+
// Turn this: async function empty() { }
5536+
// Into this: function empty() { return @newResolvedPromise(undefined); }
5537+
//
5538+
if (isEmptyBody()) {
5539+
ASSERT(!usingDeclarationCount());
5540+
ASSERT(!hasAwaitUsingDeclaration());
5541+
RefPtr<RegisterID> newResolvedPromise = generator.moveLinkTimeConstant(nullptr, LinkTimeConstant::newResolvedPromise);
5542+
CallArguments resolveArgs(generator, nullptr, 1);
5543+
generator.emitLoad(resolveArgs.thisRegister(), jsUndefined());
5544+
generator.emitLoad(resolveArgs.argumentRegister(0), jsUndefined());
5545+
RefPtr<RegisterID> result = generator.newTemporary();
5546+
generator.emitCall(result.get(), newResolvedPromise.get(), NoExpectedFunction, resolveArgs, divot, divot, divot, DebuggableCall::No);
5547+
generator.emitWillLeaveCallFrameDebugHook();
5548+
generator.emitReturn(result.get());
5549+
break;
5550+
}
5551+
55265552
// Set up finally context to capture return values from the body.
55275553
// When a return statement is hit, it stores the value in completionValueRegister
55285554
// and jumps to the finally block.
@@ -5531,10 +5557,11 @@ void FunctionNode::emitBytecode(BytecodeGenerator& generator, RegisterID*)
55315557
generator.pushFinallyControlFlowScope(finallyContext);
55325558
generator.emitLoad(finallyContext.completionValueRegister(), jsUndefined());
55335559

5534-
// Try block. Finally handler puts return value to completionValueRegister.
5560+
// Try block. FinallyContext routes `return V` to finally with completionType=Return
5561+
// and completionValue=V. Normal fallthrough keeps completionType=Normal (its initial value).
55355562
Ref<Label> catchLabel = generator.newLabel();
55365563
Ref<Label> tryStartLabel = generator.newEmittedLabel();
5537-
TryData* tryData = generator.pushTry(tryStartLabel.get(), catchLabel.get(), HandlerType::Catch);
5564+
TryData* tryData = generator.pushTry(tryStartLabel.get(), catchLabel.get(), HandlerType::Finally);
55385565
generator.emitBodyWithUsingIfNeeded(usingDeclarationCount(), hasAwaitUsingDeclaration(),
55395566
scopedLambda<void(BytecodeGenerator&)>([&](BytecodeGenerator& generator) {
55405567
emitStatementsBytecode(generator, generator.ignoredResult());
@@ -5544,43 +5571,43 @@ void FunctionNode::emitBytecode(BytecodeGenerator& generator, RegisterID*)
55445571
Ref<Label> tryEndLabel = generator.newEmittedLabel();
55455572
generator.popTry(tryData, tryEndLabel.get());
55465573

5547-
// Catch block. Reject promise with the exception.
5574+
// Catch handler. Runtime populates completionValueRegister with the thrown value
5575+
// and completionTypeRegister with CompletionType::Throw.
55485576
generator.emitLabel(catchLabel.get());
5549-
RefPtr<RegisterID> thrownValue = generator.newTemporary();
5550-
generator.emitOutOfLineCatchHandler(thrownValue.get(), finallyContext.completionTypeRegister(), tryData);
5551-
5552-
// Push try for catch block pointing to finally (for exceptions in catch).
5553-
TryData* catchTryData = generator.pushTry(catchLabel.get(), finallyLabel.get(), HandlerType::Finally);
5554-
{
5555-
RefPtr<RegisterID> rejectPromise = generator.moveLinkTimeConstant(nullptr, LinkTimeConstant::rejectPromiseWithFirstResolvingFunctionCallCheck);
5556-
CallArguments rejectArgs(generator, nullptr, 2);
5557-
generator.emitLoad(rejectArgs.thisRegister(), jsUndefined());
5558-
generator.move(rejectArgs.argumentRegister(0), generator.promiseRegister());
5559-
generator.move(rejectArgs.argumentRegister(1), thrownValue.get());
5560-
generator.emitCallIgnoreResult(generator.newTemporary(), rejectPromise.get(), NoExpectedFunction, rejectArgs, divot, divot, divot, DebuggableCall::No);
5561-
}
5562-
5563-
// Catch handled the exception, set completion type to Normal.
5564-
generator.emitLoad(finallyContext.completionTypeRegister(), CompletionType::Normal);
5565-
generator.popTry(catchTryData, finallyLabel.get());
5577+
generator.emitOutOfLineCatchHandler(finallyContext.completionValueRegister(), finallyContext.completionTypeRegister(), tryData);
55665578

55675579
generator.popFinallyControlFlowScope();
55685580

5569-
// Emit out-of-line finally handler for exceptions thrown in catch block.
5570-
generator.emitOutOfLineFinallyHandler(finallyContext.completionValueRegister(), finallyContext.completionTypeRegister(), catchTryData);
5571-
5572-
// Finally block. Resolve promise with completion value and return promise.
5581+
// Dispatch on completionType: Throw -> rejected, else -> resolved.
55735582
generator.emitLabel(finallyLabel.get());
55745583
{
5575-
RefPtr<RegisterID> resolvePromise = generator.moveLinkTimeConstant(nullptr, LinkTimeConstant::resolvePromiseWithFirstResolvingFunctionCallCheck);
5576-
CallArguments resolveArgs(generator, nullptr, 2);
5577-
generator.emitLoad(resolveArgs.thisRegister(), jsUndefined());
5578-
generator.move(resolveArgs.argumentRegister(0), generator.promiseRegister());
5579-
generator.move(resolveArgs.argumentRegister(1), finallyContext.completionValueRegister());
5580-
generator.emitCallIgnoreResult(generator.newTemporary(), resolvePromise.get(), NoExpectedFunction, resolveArgs, divot, divot, divot, DebuggableCall::No);
5584+
Ref<Label> resolveCase = generator.newLabel();
5585+
RefPtr<RegisterID> throwType = generator.emitLoad(nullptr, jsNumber(static_cast<int32_t>(CompletionType::Throw)));
5586+
generator.emitJumpIfFalse(generator.emitEqualityOp<OpStricteq>(generator.newTemporary(), finallyContext.completionTypeRegister(), throwType.get()), resolveCase.get());
5587+
5588+
{
5589+
RefPtr<RegisterID> newRejectedPromise = generator.moveLinkTimeConstant(nullptr, LinkTimeConstant::newRejectedPromise);
5590+
CallArguments rejectArgs(generator, nullptr, 1);
5591+
generator.emitLoad(rejectArgs.thisRegister(), jsUndefined());
5592+
generator.move(rejectArgs.argumentRegister(0), finallyContext.completionValueRegister());
5593+
RefPtr<RegisterID> result = generator.newTemporary();
5594+
generator.emitCall(result.get(), newRejectedPromise.get(), NoExpectedFunction, rejectArgs, divot, divot, divot, DebuggableCall::No);
5595+
generator.emitWillLeaveCallFrameDebugHook();
5596+
generator.emitReturn(result.get());
5597+
}
5598+
5599+
generator.emitLabel(resolveCase.get());
5600+
{
5601+
RefPtr<RegisterID> newResolvedPromise = generator.moveLinkTimeConstant(nullptr, LinkTimeConstant::newResolvedPromise);
5602+
CallArguments resolveArgs(generator, nullptr, 1);
5603+
generator.emitLoad(resolveArgs.thisRegister(), jsUndefined());
5604+
generator.move(resolveArgs.argumentRegister(0), finallyContext.completionValueRegister());
5605+
RefPtr<RegisterID> result = generator.newTemporary();
5606+
generator.emitCall(result.get(), newResolvedPromise.get(), NoExpectedFunction, resolveArgs, divot, divot, divot, DebuggableCall::No);
5607+
generator.emitWillLeaveCallFrameDebugHook();
5608+
generator.emitReturn(result.get());
5609+
}
55815610
}
5582-
generator.emitDebugHook(WillLeaveCallFrame, JSTextPosition(lastLine(), startOffset(), lineStartOffset()));
5583-
generator.emitReturn(generator.promiseRegister());
55845611
break;
55855612
}
55865613

‎Source/JavaScriptCore/dfg/DFGAbstractInterpreterInlines.h‎

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3973,6 +3973,13 @@ bool AbstractInterpreter<AbstractStateType>::executeEffects(unsigned clobberLimi
39733973
break;
39743974
}
39753975

3976+
case NewResolvedPromise:
3977+
case NewRejectedPromise: {
3978+
clobberWorld();
3979+
setTypeForNode(node, SpecPromiseObject);
3980+
break;
3981+
}
3982+
39763983
case CreateGenerator:
39773984
case CreateAsyncGenerator: {
39783985
auto tryToFold = [&] (const ClassInfo* classInfo) -> bool {

‎Source/JavaScriptCore/dfg/DFGByteCodeParser.cpp‎

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4699,6 +4699,26 @@ auto ByteCodeParser::handleIntrinsicCall(Node* callee, Operand resultOperand, Ca
46994699
return CallOptimizationResult::Inlined;
47004700
}
47014701

4702+
case NewResolvedPromiseIntrinsic: {
4703+
if (argumentCountIncludingThis < 2)
4704+
return CallOptimizationResult::DidNothing;
4705+
4706+
insertChecks();
4707+
Node* argument = get(virtualRegisterForArgumentIncludingThis(1, registerOffset));
4708+
setResult(addToGraph(NewResolvedPromise, Edge(argument)));
4709+
return CallOptimizationResult::Inlined;
4710+
}
4711+
4712+
case NewRejectedPromiseIntrinsic: {
4713+
if (argumentCountIncludingThis < 2)
4714+
return CallOptimizationResult::DidNothing;
4715+
4716+
insertChecks();
4717+
Node* argument = get(virtualRegisterForArgumentIncludingThis(1, registerOffset));
4718+
setResult(addToGraph(NewRejectedPromise, Edge(argument)));
4719+
return CallOptimizationResult::Inlined;
4720+
}
4721+
47024722
case PromiseConstructorResolveIntrinsic: {
47034723
if (argumentCountIncludingThis < 2)
47044724
return CallOptimizationResult::DidNothing;

‎Source/JavaScriptCore/dfg/DFGClobberize.h‎

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2545,6 +2545,11 @@ void clobberize(Graph& graph, Node* node, const ReadFunctor& read, const WriteFu
25452545
clobberTop();
25462546
return;
25472547

2548+
case NewResolvedPromise:
2549+
case NewRejectedPromise:
2550+
clobberTop();
2551+
return;
2552+
25482553
case PromiseResolve:
25492554
case PromiseReject:
25502555
case PromiseThen:

‎Source/JavaScriptCore/dfg/DFGDoesGC.cpp‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -488,6 +488,8 @@ bool doesGC(Graph& graph, Node* node)
488488
case ResolvePromiseFirstResolving:
489489
case RejectPromiseFirstResolving:
490490
case FulfillPromiseFirstResolving:
491+
case NewResolvedPromise:
492+
case NewRejectedPromise:
491493
case PromiseResolve:
492494
case PromiseReject:
493495
case PromiseThen:

‎Source/JavaScriptCore/dfg/DFGFixupPhase.cpp‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3602,6 +3602,8 @@ class FixupPhase : public Phase {
36023602
case ResolvePromiseFirstResolving:
36033603
case RejectPromiseFirstResolving:
36043604
case FulfillPromiseFirstResolving:
3605+
case NewResolvedPromise:
3606+
case NewRejectedPromise:
36053607
case PromiseResolve:
36063608
case PromiseReject:
36073609
case PromiseThen:

0 commit comments

Comments
 (0)