Skip to content

[JSC] Inline async function body when there is no await - #55900

Merged
webkit-commit-queue merged 1 commit into
WebKit:mainfrom
Constellation:eng/JSC-Inline-async-function-body-when-there-is-no-await
Dec 29, 2025
Merged

webkit-commit-queue merged 1 commit into
WebKit:mainfrom
Constellation:eng/JSC-Inline-async-function-body-when-there-is-no-await

Conversation

@Constellation

@Constellation Constellation commented Dec 28, 2025 •

Copy link
Copy Markdown
Member

f42c363

[JSC] Inline async function body when there is no await
https://bugs.webkit.org/show_bug.cgi?id=304740
rdar://167254635

Reviewed by Justin Michaud and Sosuke Suzuki.

When there is no await in async function, it does not need to have a
separate body function and we do not need to use generator since it does
not have any suspend and resume. We can detect it lexically and we can
fully inline the implementation in the async wrapper function side.
The inlined code is something like,

    let promise = newPromise();
    try {
        async-function-body
    } catch (error) {
        rejectPromise(proimse, error);
    } finally {
        resolvePromise(promise, returnedValue);
        return promise;
    }

We also fix async stack trace by setting
implementationVisibility::Private for async wrapper function (when body
is not inlined).

Test: JSTests/stress/async-function-without-await.js

* JSTests/stress/async-function-without-await.js: Added.
(shouldBe):
(shouldThrow):
(async asyncReturn42):
(async asyncReturnUndefined):
(async asyncThrow):
(async asyncWithLocals):
(helper):
(async asyncCallsHelper):
(async asyncWithTryCatch):
(async asyncWithControlFlow):
(AsyncClass.prototype.async asyncMethod):
(AsyncClass.async staticAsyncMethod):
(AsyncClass):
(async asyncWithAwait):
(async runTests):
* JSTests/stress/async-stack-trace-basic.js:
* JSTests/stress/async-stack-trace-nested-deep.js:
* JSTests/stress/async-stack-trace-nested.js:
(async problematicFunction):
* LayoutTests/http/tests/webgpu/webgpu/api/operation/uncapturederror-expected.txt:
* LayoutTests/http/tests/webgpu/webgpu/api/validation/capability_checks/limits/maxInterStageShaderVariables-expected.txt:
* Source/JavaScriptCore/bytecompiler/BytecodeGenerator.cpp:
(JSC::BytecodeGenerator::BytecodeGenerator):
* Source/JavaScriptCore/bytecompiler/BytecodeGenerator.h:
(JSC::BytecodeGenerator::isAsyncFunctionWithoutAwait const):
(JSC::BytecodeGenerator::hasFinallyScopes const):
* Source/JavaScriptCore/bytecompiler/NodesCodegen.cpp:
(JSC::ReturnNode::emitBytecode):
(JSC::FunctionNode::emitBytecode):
* Source/JavaScriptCore/parser/Nodes.h:
(JSC::ScopeNode::isAsyncFunctionWithoutAwait const):
* Source/JavaScriptCore/parser/Parser.cpp:
(JSC::Parser<LexerType>::parseInner):
(JSC::Parser<LexerType>::parseAsyncFunctionSourceElements):
(JSC::Parser<LexerType>::parseForStatement):
(JSC::Parser<LexerType>::parseFunctionBody):
(JSC::Parser<LexerType>::parseFunctionInfo):
(JSC::Parser<LexerType>::parseAwaitExpression):
* Source/JavaScriptCore/parser/Parser.h:
(JSC::Scope::resetImplementationVisibility):
(JSC::Scope::setImplementationVisibility):
(JSC::Scope::setAsyncFunctionBodyDoesNotUseAwait):
(JSC::Scope::asyncFunctionBodyDoesNotUseAwait const):
(JSC::Scope::setUsesAwait):
(JSC::Scope::usesAwait const):
(JSC::Scope::restoreFromSourceProviderCache):
* Source/JavaScriptCore/parser/ParserModes.h:
* Source/JavaScriptCore/parser/SourceProviderCacheItem.h:
(JSC::SourceProviderCacheItem::SourceProviderCacheItem):

Canonical link: https://commits.webkit.org/304987@main

a5f2050

Misc iOS, visionOS, tvOS & watchOS macOS Linux Windows Apple Internal
✅ 🧪 style ✅ 🛠 ios ✅ 🛠 mac ✅ 🛠 wpe ✅ 🛠 win ✅ 🛠 ios-apple
✅ 🛠 ios-sim ✅ 🛠 mac-AS-debug ✅ 🧪 wpe-wk2   🧪 win-tests ✅ 🛠 mac-apple
✅ 🧪 webkitperl ✅ 🧪 ios-wk2 ✅ 🧪 api-mac ✅ 🧪 api-wpe ✅ 🛠 vision-apple
✅ 🧪 ios-wk2-wpt ✅ 🧪 api-mac-debug ✅ 🛠 wpe-cairo-libwebrtc
✅ 🛠 🧪 jsc ✅ 🧪 api-ios ✅ 🧪 mac-wk1 ✅ 🛠 gtk
✅ 🛠 🧪 jsc-arm64 ✅ 🛠 vision ✅ 🧪 mac-wk2 ✅ 🧪 gtk-wk2
✅ 🛠 vision-sim ✅ 🧪 mac-AS-debug-wk2 ✅ 🧪 api-gtk
✅ 🧪 vision-wk2 ✅ 🧪 mac-wk2-stress ✅ 🛠 playstation
✅ 🛠 🧪 unsafe-merge ✅ 🛠 tv ✅ 🧪 mac-intel-wk2 ✅ 🛠 jsc-armv7
✅ 🛠 tv-sim ✅ 🛠 mac-safer-cpp ✅ 🧪 jsc-armv7-tests
✅ 🛠 watch
✅ 🛠 watch-sim

@Constellation
Constellation requested a review from a team as a code owner December 28, 2025 10:48
@Constellation Constellation self-assigned this Dec 28, 2025
@Constellation Constellation added the JavaScriptCore For bugs in JavaScriptCore, the JS engine used by WebKit, other than kxmlcore issues. label Dec 28, 2025
@webkit-ews-buildbot webkit-ews-buildbot added the merging-blocked Applied to prevent a change from being merged label Dec 28, 2025
@Constellation Constellation removed the merging-blocked Applied to prevent a change from being merged label Dec 28, 2025
@Constellation
Constellation force-pushed the eng/JSC-Inline-async-function-body-when-there-is-no-await branch from 76b0d7e to 22992b6 Compare December 28, 2025 22:01
@Constellation
Constellation force-pushed the eng/JSC-Inline-async-function-body-when-there-is-no-await branch from 22992b6 to a5f2050 Compare December 28, 2025 23:52

@sosukesuzuki sosukesuzuki left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

r=me

@Constellation Constellation added the unsafe-merge-queue Applied to send a pull request to merge-queue, but skip building and testing label Dec 29, 2025
https://bugs.webkit.org/show_bug.cgi?id=304740
rdar://167254635

Reviewed by Justin Michaud and Sosuke Suzuki.

When there is no await in async function, it does not need to have a
separate body function and we do not need to use generator since it does
not have any suspend and resume. We can detect it lexically and we can
fully inline the implementation in the async wrapper function side.
The inlined code is something like,

    let promise = newPromise();
    try {
        async-function-body
    } catch (error) {
        rejectPromise(proimse, error);
    } finally {
        resolvePromise(promise, returnedValue);
        return promise;
    }

We also fix async stack trace by setting
implementationVisibility::Private for async wrapper function (when body
is not inlined).

Test: JSTests/stress/async-function-without-await.js

* JSTests/stress/async-function-without-await.js: Added.
(shouldBe):
(shouldThrow):
(async asyncReturn42):
(async asyncReturnUndefined):
(async asyncThrow):
(async asyncWithLocals):
(helper):
(async asyncCallsHelper):
(async asyncWithTryCatch):
(async asyncWithControlFlow):
(AsyncClass.prototype.async asyncMethod):
(AsyncClass.async staticAsyncMethod):
(AsyncClass):
(async asyncWithAwait):
(async runTests):
* JSTests/stress/async-stack-trace-basic.js:
* JSTests/stress/async-stack-trace-nested-deep.js:
* JSTests/stress/async-stack-trace-nested.js:
(async problematicFunction):
* LayoutTests/http/tests/webgpu/webgpu/api/operation/uncapturederror-expected.txt:
* LayoutTests/http/tests/webgpu/webgpu/api/validation/capability_checks/limits/maxInterStageShaderVariables-expected.txt:
* Source/JavaScriptCore/bytecompiler/BytecodeGenerator.cpp:
(JSC::BytecodeGenerator::BytecodeGenerator):
* Source/JavaScriptCore/bytecompiler/BytecodeGenerator.h:
(JSC::BytecodeGenerator::isAsyncFunctionWithoutAwait const):
(JSC::BytecodeGenerator::hasFinallyScopes const):
* Source/JavaScriptCore/bytecompiler/NodesCodegen.cpp:
(JSC::ReturnNode::emitBytecode):
(JSC::FunctionNode::emitBytecode):
* Source/JavaScriptCore/parser/Nodes.h:
(JSC::ScopeNode::isAsyncFunctionWithoutAwait const):
* Source/JavaScriptCore/parser/Parser.cpp:
(JSC::Parser<LexerType>::parseInner):
(JSC::Parser<LexerType>::parseAsyncFunctionSourceElements):
(JSC::Parser<LexerType>::parseForStatement):
(JSC::Parser<LexerType>::parseFunctionBody):
(JSC::Parser<LexerType>::parseFunctionInfo):
(JSC::Parser<LexerType>::parseAwaitExpression):
* Source/JavaScriptCore/parser/Parser.h:
(JSC::Scope::resetImplementationVisibility):
(JSC::Scope::setImplementationVisibility):
(JSC::Scope::setAsyncFunctionBodyDoesNotUseAwait):
(JSC::Scope::asyncFunctionBodyDoesNotUseAwait const):
(JSC::Scope::setUsesAwait):
(JSC::Scope::usesAwait const):
(JSC::Scope::restoreFromSourceProviderCache):
* Source/JavaScriptCore/parser/ParserModes.h:
* Source/JavaScriptCore/parser/SourceProviderCacheItem.h:
(JSC::SourceProviderCacheItem::SourceProviderCacheItem):

Canonical link: https://commits.webkit.org/304987@main
@webkit-commit-queue
webkit-commit-queue force-pushed the eng/JSC-Inline-async-function-body-when-there-is-no-await branch from a5f2050 to f42c363 Compare December 29, 2025 22:39
@webkit-commit-queue

Copy link
Copy Markdown
Collaborator

Committed 304987@main (f42c363): https://commits.webkit.org/304987@main

Reviewed commits have been landed. Closing PR #55900 and removing active labels.

@webkit-commit-queue
webkit-commit-queue merged commit f42c363 into WebKit:main Dec 29, 2025
@webkit-commit-queue webkit-commit-queue removed the unsafe-merge-queue Applied to send a pull request to merge-queue, but skip building and testing label Dec 29, 2025
@Constellation
Constellation deleted the eng/JSC-Inline-async-function-body-when-there-is-no-await branch December 30, 2025 00:20
sosukesuzuki added a commit to oven-sh/WebKit that referenced this pull request Jan 4, 2026
…der.js

After upstream WebKit PR WebKit#55900, async functions containing 'await' are
automatically set to ImplementationVisibility::Private by the parser.
The hand-rolled parser in BuiltinExecutables.cpp doesn't detect this,
causing metadata mismatch in debug builds.

Add @visibility=PrivateRecursive annotations to the 4 async functions
in ModuleLoader.js to align with upstream and fix debug build crashes:
- asyncModuleEvaluation
- loadModule
- loadAndEvaluateModule
- requestImportModule
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

JavaScriptCore For bugs in JavaScriptCore, the JS engine used by WebKit, other than kxmlcore issues.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants