fix(parser): object-literal async shorthand methods now track async-function context (#129) - #134
Merged
Merged
Conversation
…unction context (#129) The async-method-shorthand branch in parseObjectLiteral (async foo() {...} and async *foo() {...} inside an object literal) never saved, incremented, or restored p.inAsyncFunction/p.inNonAsyncFunction around its own body parse, unlike every other async body (parseFunctionLiteral, parseArrowFunctionBodyAndFinish, and the class-method equivalents). Without it, 'await' inside the method's body was resolved against whatever async/non-async depth the *enclosing* function had left behind, not the method's own (always-async) context. Defining such an object literal inside a plain, non-async function made 'await' mis-parse as a plain identifier instead of an await-expression, throwing "await is not defined" instead of awaiting. Fixed by copying the save/increment/restore pattern from the reference call sites. Restoration happens before checking the parsed body for nil (rather than after, like the reference sites do unconditionally) since this branch, unlike those, has an early-return between the parse call and the rest of the function - restoring first avoids leaking the incremented depth into whatever the parser recovers into on a malformed body. Added tests/scripts/async_method_object_literal_nested.ts and tests/scripts/async_generator_method_object_literal_nested.ts (kept separate: combining both methods in one object literal and awaiting them sequentially trips an unrelated, pre-existing VM panic, filed as #133).
…oadFrame All PRs currently fail CI's golangci-lint job on this pre-existing issue, unrelated to any of their own changes (confirmed: main itself fails lint with these same 14 findings before this fix). Seven identical proxy-trap-error-handling sites set code/constants (along with frame/closure/function/registers/ip) just before `goto reloadFrame`. The reloadFrame: label unconditionally re-derives all seven of those same variables from vm.frames[vm.frameCount-1] immediately after the jump, so the code/constants assignments right before the goto are always overwritten before being read - ineffassign correctly flags them as dead. Removed the 14 dead lines; behavior is identical since reloadFrame already does this work.
…e' into fix/129-object-literal-async-shorthand
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Fixes #129.
The async-method-shorthand branch in
parseObjectLiteral(async foo() {...}andasync *foo() {...}inside an object literal) never saved/incremented/restoredp.inAsyncFunctionaround its own body parse, unlike the four reference sites:parser.go:2898(parseFunctionLiteral),parser.go:5175(parseArrowFunctionBodyAndFinish), and the class-method equivalents (parse_class.go:869,parse_class.go:1470).Without it,
awaitinside the method's body resolved against whatever async/non-async depth the enclosing function had left behind, not the method's own (always-async) context. Defining such an object literal inside a plain, non-async function madeawaitmis-parse as a plain identifier and throwawait is not definedinstead of awaiting.Fix
Copied the save/increment/restore pattern from the reference sites. One deviation, called out in the code comment: this branch has an early
return nilbetween the body parse and the rest of the function (on a malformed body), so the restore happens before that check rather than unconditionally after, to avoid leaking the incremented depth into whatever the parser recovers into.Testing
go test ./...— all greenmain), filed separately as vm: panic (index out of range) resuming an async function after an async generator method on the same object has run #133.🤖 Generated with Claude Code