Skip to content

Implement optional chaining.#1024

Merged
xeioex merged 9 commits intonginx:masterfrom
xeioex:optional_chaining
Mar 2, 2026
Merged

Implement optional chaining.#1024
xeioex merged 9 commits intonginx:masterfrom
xeioex:optional_chaining

Conversation

@xeioex
Copy link
Copy Markdown
Contributor

@xeioex xeioex commented Feb 11, 2026

This closes #835 feature request on Github.

Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Adds runtime and parsing support for JavaScript optional chaining (?.) in njs, addressing the behavior reported in issue #835 (e.g. f?.() / obj?.prop short-circuiting on null/undefined).

Changes:

  • Introduces a new VM opcode (NJS_VMCODE_OPTIONAL_CHAIN) to implement optional-chain short-circuiting at runtime.
  • Updates the parser and bytecode generator to build/emit optional-chaining AST + vmcode (including ?.() call forms).
  • Adds a comprehensive set of unit tests covering property access, calls, delete semantics, and interactions with ??.

Reviewed changes

Copilot reviewed 7 out of 7 changed files in this pull request and generated 1 comment.

Show a summary per file
File Description
src/test/njs_unit_test.c Adds unit tests for optional chaining behavior across many forms/corner cases.
src/njs_vmcode.h Adds NJS_VMCODE_OPTIONAL_CHAIN opcode to the VM opcode enum.
src/njs_vmcode.c Implements interpreter handling for the optional-chain opcode and wires it into the dispatch table.
src/njs_parser.c Extends parsing to recognize/construct optional-chaining AST nodes and delete behavior adjustments.
src/njs_lexer.h Introduces AST token types for optional chaining nodes (OPTIONAL_CHAIN, OPTIONAL_CHAIN_REF).
src/njs_generator.c Generates bytecode for optional chaining, including short-circuit jump patching and this handling in method-call cases.
src/njs_disassembler.c Adds disassembly formatting for the new optional-chain opcode.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread src/njs_parser.c Outdated
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

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

Copilot encountered an error and was unable to review this pull request. You can try again by re-requesting a review.

Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 7 out of 7 changed files in this pull request and generated 2 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread src/njs_generator.c Outdated
Comment thread src/test/njs_unit_test.c Outdated
@VadimZhestikov
Copy link
Copy Markdown
Contributor

Do we need add more testcases, like:

for run-time:

  o[a].m?.()
  o?.[expr].m?.()      or mixed nested optional segments

for parser:

  a ? .b : c (weird spacing) does not accidentally parse as optional chaining.
  a?.b : c properly errors (because : would be nonsense there).
  a ? b?.c : d still parses as ternary with optional chaining inside.

negative-syntax tests:

o?.a = 1 (optional chaining is not a valid assignment target)
o?.a++ / ++o?.a
new o?.() (optional call with new is invalid in JS)
o?.a().b is allowed, but o?.a?.().b shapes should be validated if supported

@xeioex xeioex force-pushed the optional_chaining branch from e66d340 to 03ef60e Compare March 1, 2026 00:23
@xeioex
Copy link
Copy Markdown
Contributor Author

xeioex commented Mar 1, 2026

@VadimZhestikov

  1. I reworked the patch set by adding preparatory patches which minimize the code for new functionality.
    They are mostly mechanical and pretty regular.
  2. I also reworked Implement optional chaining be reducing the coupling between parser and generator. Now generator part is mostly straightforward (no complex logic).
  3. I added more tests based on your examples and found 2 bugs for expressions like o[a].m?.()
    and o?.[expr].m?.() they pass now.

Unlike regular compound assignments (+=, -=), these operators
short-circuit: the RHS is not evaluated and no assignment occurs
if the logical condition is already satisfied.
@xeioex xeioex force-pushed the optional_chaining branch from 03ef60e to 4e3cffd Compare March 1, 2026 00:39
Copy link
Copy Markdown
Contributor

@VadimZhestikov VadimZhestikov left a comment

Choose a reason for hiding this comment

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

Looks good

@xeioex xeioex merged commit aa8ff7d into nginx:master Mar 2, 2026
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Support optional chaining

3 participants