Fix call argument evaluation order and enable await in call arguments#1034
Merged
xeioex merged 8 commits intonginx:masterfrom Mar 12, 2026
Merged
Fix call argument evaluation order and enable await in call arguments#1034xeioex merged 8 commits intonginx:masterfrom
xeioex merged 8 commits intonginx:masterfrom
Conversation
Previously, the generator inferred reference intent from raw AST shape. Now the parser marks the relevant property node as PROPERTY_REF before building METHOD_CALL, assignment, or update nodes, and the generator accepts both PROPERTY and PROPERTY_REF via njs_generate_is_property_lvalue(). Introduce NJS_TOKEN_PROPERTY_REF as an explicit parser-side marker for property accesses that carry reference semantics (assignment targets, delete operands, increment/decrement, method-call receivers).
Previously, grouped optional calls like (o?.m)() resolved the callee through the optional chain but dispatched via plain FUNCTION_CALL, losing the original receiver. The fix stores the receiver on the call node so the upcoming call-argument reorder can emit METHOD_FRAME with an explicit "this". Call-expression setup and optional-chain preserve lookup are routed through named helpers with generator-side validation.
Introduce NJS_TOKEN_OPTIONAL_PRESERVE for optional-chain preserve nodes instead of reusing OBJECT_VALUE, so OBJECT_VALUE remains strictly an object/array literal structure token. Route optional-preserve, object-value, and optional method-preserve access through dedicated helper functions with narrow assertions, removing direct u.object/left/right access from general parser and generator paths. No behavior change.
Previously, the "in" operator swap flag was relayed through generator context. This broke when call-end handlers switched to njs_generator_stack_pop(NULL), which released the context before the swap was read in njs_generate_3addr_operation_end(). The fix derives the swap directly from the node token type (NJS_TOKEN_IN), eliminating the context relay.
Previously, call lowering created FUNCTION_FRAME and METHOD_FRAME before argument evaluation. This made call ordering observably wrong: for non-callable callees, the error was thrown before arguments with side effects were evaluated, violating the ECMAScript specification. It also prevented await expressions in call arguments, which were rejected at parse time because suspending inside a half-created frame was not supported. The fix evaluates arguments first, then emits the frame, PUT_ARG, and FUNCTION_CALL. Callee and receiver values are captured into temporaries before argument evaluation to guard against argument side effects. Method properties are resolved via PROPERTY_GET before arguments. METHOD_FRAME is redefined from a composite opcode (property lookup + callability check + frame creation) to a pure frame-creation opcode that takes an already-resolved function and explicit "this" value. The parser always wraps call expressions in a NJS_TOKEN_FUNCTION_CALL node, removing the NJS_TOKEN_NAME special case.
c76899f to
9872442
Compare
Contributor
|
Looks good. May be useful add next testcases: |
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.
Why
Spec compliance: ECMAScript requires arguments (including their side effects)
to be evaluated before the callee's callability is checked. The old call lowering
created the frame first, which threw non-callable errors before argument side
effects ran.
Enable await in call arguments and tagged templates: The old frame-first
design made it impossible to suspend on
awaitduring argument evaluationbecause the frame was already half-constructed. The parser rejected both as a
workaround.
Preserve
thisfor grouped optional calls:(o?.m)()resolved the calleethrough the optional chain but dispatched via plain FUNCTION_CALL, losing the
receiver.
Behavioral changes
are thrown.
constructable.
(intermediate value)["x"] is not a functiontotype is not a function(e.g.,"number is not a function").the native function.
awaitin call arguments and tagged templates is now allowed.(o?.m)()correctly preserves the receiver.Examples
Test262: +12 tests passed (14457 → 14469)
language/expressions/call/11.2.3-3_{1,2,4,6,7}— call argument orderinglanguage/expressions/new/ctorExpr-isCtor-after-args-eval{,-fn-wrapup}—constructor ordering
language/expressions/await/await-{awaits-thenables,monkey-patched-promise}—await in args
language/expressions/optional-chaining/{member-expression-async-literal,optional-chain-async-*-square-brackets}—await + optional chain