Files
rust_browser/_bmad-output/implementation-artifacts/3-3-async-await.md
Zachary D. Rowitsch b43cd2d58b Implement async/await for bytecode VM with code review fixes (§3.3)
Add full async/await support: parser recognizes async functions, arrows,
methods, and await expressions; compiler emits MakeAsyncFunction/Await
opcodes; runtime reuses generator suspension with Promise-based
auto-advancement via microtask queue. All await resumptions go through
microtasks per ECMAScript §27.7.5.3 for correct ordering. Includes
adversarial code review fixes (18 issues: correct await precedence,
async_depth leak guard, rejection type preservation, compiler-level
await validation, typed async resume detection). 27 integration tests
in tests/js_async.rs cover all acceptance criteria. Demotes 125
pre-existing false-positive Test262 full-suite entries.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-15 21:11:10 -04:00

30 KiB

Story 3.3: async/await

Status: done

Story

As a web developer using JavaScript, I want async functions and await expressions to work, So that asynchronous code can be written in a readable, sequential style.

Acceptance Criteria

  1. Async function declarations and expressions return Promises: async function syntax is recognized by the parser and produces async functions. Calling an async function returns a Promise that resolves with the function's return value. Async functions can be declared as statements, expressions, arrow functions, and as object/class methods.

  2. await suspends on Promises and resumes with resolved value: Inside an async function, await expr suspends execution when expr is a Promise. When the Promise fulfills, execution resumes with the resolved value. When the Promise rejects, the rejection is thrown as an exception at the await point.

  3. await wraps non-Promise values: If the awaited value is not a Promise, it is treated as an already-resolved value and execution continues synchronously (technically via a microtask, per spec).

  4. try/catch works with rejected Promises inside async functions: A rejected awaited Promise throws at the await point. If inside a try block, the catch block receives the rejection reason. Uncaught rejections propagate to the returned Promise's rejection.

  5. Microtask ordering is correct: Multiple async functions with interleaved awaits schedule microtasks in the correct order per ECMAScript §27.7. Promise resolution and async function resumption interleave correctly.

  6. Async arrow functions work: async () => { ... } and async (x) => x + 1 syntax is parsed and executes correctly, returning a Promise.

  7. Async methods in classes and objects work: async method() { ... } in class bodies and object literals is parsed and executes correctly.

  8. Test262 async/await tests promoted: All relevant vendored and full-suite Test262 tests for async/await are promoted from known_fail to pass. docs/JavaScript_Implementation_Checklist.md is updated. just ci passes.

Tasks / Subtasks

  • Task 1: Parser support for async syntax (AC: #1, #6, #7)

    • 1.1 Add Async keyword to crates/js_parser/src/token.rs TokenKind enum and keyword recognition
    • 1.2 Add Await keyword to crates/js_parser/src/token.rs TokenKind enum (if not already present)
    • 1.3 Add is_async: bool field to FnDecl statement variant in crates/js_parser/src/ast.rs
    • 1.4 Add is_async: bool field to FunctionExpr expression variant in crates/js_parser/src/ast.rs
    • 1.5 Add is_async: bool field to ClassMethod (or equivalent) in crates/js_parser/src/ast.rs
    • 1.6 Add AwaitExpr { argument: Box<Expr> } variant to Expr enum in crates/js_parser/src/ast.rs
    • 1.7 Update parse_fn_decl() in crates/js_parser/src/parser/statements.rs to recognize async function (two-token lookahead: async followed by function)
    • 1.8 Update function expression parsing in crates/js_parser/src/parser/expressions.rs to recognize async function
    • 1.9 Implement async arrow function parsing: async () => { ... } and async x => expr — requires distinguishing async as keyword vs identifier based on context (no line terminator between async and arrow params)
    • 1.10 Implement await expression parsing in crates/js_parser/src/parser/expressions.rs:
      • await expr produces AwaitExpr { argument: expr }
      • await has unary-expression precedence
      • await is only valid inside async functions — emit parse error otherwise
    • 1.11 Support async methods in object literals and class bodies: async methodName() { ... } syntax
    • 1.12 Support async * generator methods (async generators) — parse only, execution is out of scope for this story
    • 1.13 Add parser unit tests for all async syntax variations
  • Task 2: Bytecode opcodes for async functions (AC: #1, #2, #3)

    • 2.1 Add new opcodes to crates/js_vm/src/bytecode/opcodes.rs:
      • Await — suspend async function execution, schedule resumption as microtask when Promise settles
      • MakeAsyncFunction(u16) — create an async function from a compiled function (constant pool index), similar to MakeGenerator but wraps in Promise-returning wrapper
    • 2.2 Assign opcode byte values in the available range (after existing GeneratorReturn 0xA9)
    • 2.3 Update opcode encoding/decoding in chunk.rs if needed
  • Task 3: Bytecode compiler for async functions (AC: #1, #2, #6, #7)

    • 3.1 Add is_async: bool field to FunctionBlueprint in crates/js_vm/src/bytecode/chunk.rs
    • 3.2 Update compile_fn_decl() and function expression compilation in crates/js_vm/src/bytecode/compiler/ to:
      • Track is_async flag on the compiler context (similar to is_generator_body)
      • Compile async function bodies with Await opcodes where await expressions appear
      • Emit MakeAsyncFunction instead of MakeClosure for async functions
      • Use Return at end of async function body (the wrapping handles Promise resolution)
    • 3.3 Implement compile_await_expr():
      • Compile the argument expression (the thing being awaited)
      • Emit Await opcode
      • The resolved value becomes the expression result (pushed onto operand stack on resume)
    • 3.4 Ensure await inside try/catch/finally compiles correctly (the async function must be resumable within a try block — reuse generator's SavedTryHandler mechanism)
    • 3.5 Validate that await only appears inside async function bodies at compile time
    • 3.6 Handle async arrow functions and async methods in compiler
  • Task 4: Async function runtime infrastructure (AC: #1, #2, #3, #4)

    • 4.1 Add is_async: bool field to JsFunction struct in crates/js_vm/src/environment.rs
    • 4.2 Design async function execution model — the key architecture decision:
      • When an async function is called, immediately create a Promise
      • Begin executing the function body
      • On await: suspend execution (reuse generator suspension mechanism: save CallFrame, stack, try handlers, scopes)
      • Schedule resumption as a microtask when the awaited Promise settles
      • On function return: resolve the outer Promise with the return value
      • On uncaught throw: reject the outer Promise with the error
    • 4.3 Implement AsyncFunctionObject or reuse GeneratorObject with async-specific state:
      • Option A: Reuse GeneratorObject with an async_promise_id: Option<u64> field (the outer Promise)
      • Option B: New AsyncFunctionObject struct (cleaner separation)
      • Decision: Use Option A (reuse generator infrastructure) for maximum code reuse — async functions are semantically "generators that yield Promises and auto-advance"
    • 4.4 Add async_promise_id: Option<u64> to GeneratorObject for tracking the async function's return Promise
    • 4.5 Implement the "auto-advance" mechanism: when an await point resolves, the microtask queue must contain a task that calls the equivalent of .next(resolved_value) on the internal generator, advancing it to the next await or completion
  • Task 5: Bytecode VM async execution (AC: #2, #3, #4, #5)

    • 5.1 Implement MakeAsyncFunction opcode handler in crates/js_vm/src/interpreter/bytecode_exec.rs:
      • Create a wrapper function that, when called:
        1. Creates a new pending Promise (via PromiseRegistry in web_api)
        2. Creates an internal generator-like object from the async function body
        3. Calls the internal generator's .next() to start execution
        4. Returns the Promise to the caller
    • 5.2 Implement Await opcode handler in bytecode_exec.rs:
      • Pop the awaited value from the operand stack
      • If the value is a Promise: register a then-handler that enqueues a microtask to resume the async function with the resolved value (or throw the rejection)
      • If the value is NOT a Promise: wrap it — enqueue a microtask to resume immediately with the value
      • Suspend execution (save CallFrame, stack, try handlers, scopes — same as Yield)
      • Return control to the caller (the microtask drain loop or event loop)
    • 5.3 Implement async function resumption:
      • On await resolution: restore saved frame, push resolved value onto operand stack, continue execution
      • On await rejection: restore saved frame, throw the rejection value at the await point (use try-catch machinery)
      • On function return: resolve the outer Promise with the return value, set state to Completed
      • On uncaught throw: reject the outer Promise with the error, set state to Completed
    • 5.4 Integrate with microtask queue:
      • Await resolution must go through the microtask queue (not resolve synchronously)
      • The HostEnvironment (DomHost in web_api) must support scheduling async resumption microtasks
      • This likely requires a new method or extending existing call_global_function() to support "resume async function" operations
    • 5.5 Handle edge cases:
      • await on an already-resolved Promise: still schedules a microtask (per spec §27.7.5.3 — Await creates a new PromiseResolve then adds reaction)
      • await on a thenable (non-Promise with .then method): call .then() to get the value
      • Nested async functions: each has its own generator-like state, independent suspension
      • return await promise inside try/finally: must execute finally block
  • Task 6: Host environment integration (AC: #5)

    • 6.1 Extend DomHost (in crates/web_api/src/dom_host/) to support async function resumption:
      • Add a mechanism to register "pending async resumptions" that get triggered when Promises settle
      • This could use the existing then_handlers on PromiseRecord, with a special callback type that resumes the async function
    • 6.2 Ensure microtask drain loop handles async function resumptions:
      • When a then-handler from an await is executed, it must invoke the bytecode VM to resume the async function
      • The drain loop in crates/web_api/src/lib.rs (lines ~407-482) must handle this correctly
      • Consider: the drain loop currently calls callbacks via JsFunction — async resumption may need a different mechanism (e.g., a closure that captures the GeneratorObject and calls .next())
    • 6.3 Test microtask ordering:
      • Promise.resolve().then(f1) and async function with await Promise.resolve() must interleave correctly
      • Multiple awaits in sequence must each go through the microtask queue
  • Task 7: Testing and validation (AC: #8)

    • 7.1 Add unit tests for parser: async function, await, async arrow functions, async methods, error cases
    • 7.2 Add unit tests for async function execution:
      • Basic async function: returns a Promise that resolves with return value
      • Async function with single await on resolved Promise
      • Async function with await on pending Promise that later resolves
      • Async function with await on rejected Promise — catch block receives rejection
      • Async function with multiple sequential awaits
      • Async function with try/catch around await of rejected Promise
      • Async function calling another async function
      • Async arrow function: async () => { return 42; }
      • Async method in class
      • Async method in object literal
      • Await on non-Promise value (should work like resolved Promise)
      • Microtask ordering: interleaved async functions execute in correct order
      • Error propagation: uncaught throw rejects the returned Promise
      • Return value: return 42 resolves Promise with 42
      • Void return: async function f() {} resolves with undefined
    • 7.3 Add integration tests for async + existing features:
      • Async function with for-of loop
      • Async function with generator (call generator, await each value)
      • Async function with Promise.resolve/reject
      • Async function with setTimeout (timer fires, resolves Promise, async function resumes)
    • 7.4 Run vendored Test262 suite and promote passing async/await tests:
      • cargo test -p rust_browser --test js262_harness js262_suite_matches_manifest_expectations -- --nocapture
      • Promote all newly passing tests with just js262-status promote --id <test-id>
    • 7.5 Run full Test262 suite and triage async tests:
      • just test262-full
      • just triage-test262-full
    • 7.6 Run all existing JS test suites to verify no regressions:
      • cargo test -p rust_browser --test js_tests
      • cargo test -p rust_browser --test js_dom_tests
      • cargo test -p rust_browser --test js_events
      • cargo test -p rust_browser --test js_scheduling
      • cargo test -p js_vm
    • 7.7 Update docs/JavaScript_Implementation_Checklist.md:
      • Check off Phase 24 items: async function, await, async arrow, async methods
    • 7.8 Run just ci — full validation pass

Dev Notes

Architecture: How Async/Await Maps to Generator Suspension

Async functions are semantically equivalent to generators that yield Promises and auto-advance. The existing generator suspension mechanism (Story 3.2) provides the foundation:

Generator suspension (existing):

  1. Yield opcode → save CallFrame, stack, try handlers, scopes into GeneratorObject
  2. Return {value, done} to caller
  3. .next(value) → restore frame, push value, resume execution

Async function suspension (new, same mechanism):

  1. Await opcode → save CallFrame, stack, try handlers, scopes into GeneratorObject
  2. Register a then-handler on the awaited Promise that will resume execution via microtask
  3. Return control (the outer Promise is already returned to the async function's caller)
  4. When Promise settles → microtask fires → restore frame, push resolved value (or throw rejection), resume

The key difference: generators require manual .next() calls from user code, while async functions auto-advance via the microtask queue when awaited Promises settle.

Current Promise Infrastructure (What Exists)

Fully operational (from crates/web_api/src/promise.rs):

  • PromiseRegistry with HashMap<u64, PromiseRecord> tracking all active Promises
  • Three states: Pending, Fulfilled(JsValue), Rejected(JsValue)
  • settle_promise() — single source of truth for settlement, prevents double-settle
  • then_handlers Vec on each Promise for callback chaining
  • new Promise(executor), Promise.resolve(), Promise.reject(), .then(), .catch()

Microtask queue (from crates/web_api/src/scheduling.rs):

  • FIFO VecDeque<Microtask> with enqueue/dequeue
  • Starvation guard: 1000 iteration limit per drain
  • Drain happens after: script execution, timer callbacks, event handlers

Integration for async/await: When an await suspends, it must:

  1. Get or create a Promise from the awaited value
  2. Add a then-handler to that Promise
  3. The then-handler callback must enqueue a microtask that resumes the async function

Bytecode VM Frame Architecture (from Story 3.2)

Each CallFrame stores: ip: usize, base_slot: usize, local_count: usize, this_value: JsValue

GeneratorObject saves: CallFrame, operand stack segment, Vec<SavedTryHandler>, Vec<Scope>

For async/await, extend GeneratorObject with:

  • async_promise_id: Option<u64> — the outer Promise returned to the async function's caller
  • State transitions: SuspendedStartExecutingSuspendedYield (at await) → Executing (resumed) → Completed

Critical Implementation Detail: Await Wrapping

Per ECMAScript §27.7.5.3 (Await), every await creates an implicit Promise wrapping:

1. Let asyncContext be the running execution context
2. Let promise be PromiseResolve(%Promise%, value)  // wraps non-Promise
3. Create onFulfilled that resumes with value
4. Create onRejected that resumes with throw
5. PerformPromiseThen(promise, onFulfilled, onRejected)
6. Remove asyncContext, suspend

This means await 42 must still go through the microtask queue (wrap 42 in Promise.resolve(42), add then-handler, microtask fires, resume). This is critical for correct microtask ordering.

Cross-Component Communication: JS VM ↔ Web API

The async function execution crosses the JS VM / Web API boundary:

  • JS VM (js_vm crate): Executes bytecode, handles Await opcode, saves/restores frames
  • Web API (web_api crate): Manages Promise registry, microtask queue, schedules resumptions

Communication flows through the HostEnvironment trait:

  • VM calls host.call_global_function() or similar to create Promises and register then-handlers
  • Web API's microtask drain loop calls back into the VM to resume async functions

Design consideration: The resume callback needs access to the GeneratorObject (which lives in js_vm). Options:

  1. Store the GeneratorObject as a JsValue (object with internal slot) — the callback receives it as an argument
  2. Use a dedicated "async resume" host call
  3. The then-handler callback IS a JsFunction that wraps the resume logic

Option 1 is recommended: the then-handler receives the GeneratorObject as a captured value and calls the equivalent of .next(resolved_value) on it. This reuses the existing generator .next() infrastructure.

Previous Story Intelligence (3.2: Generators & Iterator Protocol)

Key learnings:

  • Generator suspension is fully operational — saves CallFrame, operand stack, try handlers, scopes
  • run_generator_step() in bytecode_exec.rs handles the VM re-entry for generator resumption
  • Generator objects use internal slots on JsObject (generator_data field)
  • Generator methods (.next(), .return(), .throw()) dispatched via bc_call_method()

Known limitations from 3.2 that may affect async/await:

  • .return() does NOT execute finally blocks when suspended inside try-finally — this means return inside an async function's try block may not correctly execute finally cleanup. May need fixing in this story or tracked separately.
  • Iterator close protocol not fully implemented — not directly relevant to async/await
  • obj[Symbol.iterator] computed property access not supported — not relevant

Code patterns established:

  • New opcodes added to opcodes.rs with sequential byte values (Yield=0xA6, YieldStar=0xA7, MakeGenerator=0xA8, GeneratorReturn=0xA9)
  • Compiler uses is_generator_body flag to control code generation — add is_async_body similarly
  • Test files in src/tests/ directory pattern (e.g., generator_tests.rs)
  • After implementation, run all existing JS test suites to verify no regressions

What NOT to Implement

  • Do NOT implement in the AST interpreter — all async/await work must be in the bytecode path exclusively
  • Do NOT implement async generators (async function*) — that combines generators + async and is a separate concern
  • Do NOT implement for await...of — requires async iterators + Symbol.asyncIterator, separate story
  • Do NOT implement top-level await — requires ES modules (Story 3.4)
  • Do NOT implement Promise.all/race/allSettled/any — those are Story 3.5 (built-in completeness)
  • Do NOT implement .finally() — nice-to-have but not required for async/await core
  • Do NOT add new external dependencies — pure Rust using existing crate deps
  • Do NOT change the HostEnvironment trait signature if avoidable — extend via existing methods

Risk Assessment

High Risk: Microtask queue re-entry When an async function resumes via microtask, it re-enters the bytecode VM from within the microtask drain loop. If the resumed function hits another await, it must suspend again and return control to the drain loop. This creates a complex control flow: drain loop → resume → execute → await → suspend → return to drain loop → drain next microtask. Must ensure no stack overflow or state corruption.

Medium Risk: Promise integration across crate boundary The Await opcode executes in js_vm but must interact with Promises in web_api. The HostEnvironment trait is the bridge. Must ensure the protocol for "register a then-handler that resumes an async function" works correctly across this boundary without introducing tight coupling.

Medium Risk: Correct microtask ordering ECMAScript specifies precise ordering for microtask scheduling with async/await. await on an already-resolved Promise must still go through the microtask queue (not resume synchronously). Multiple async functions must interleave correctly. This requires careful testing.

Low Risk: Parser changes Async syntax is well-defined. async function is a two-token pattern. await is context-dependent (keyword inside async functions). Async arrow functions require careful lookahead (async followed by ( or identifier, then =>).

Low Risk: Backward compatibility Adding is_async: bool to FnDecl, FunctionExpr, ClassMethod, and JsFunction with default false should not affect any existing functionality.

Phased Implementation Strategy

Phase A — Parser: Add async function, await, async arrow, async methods. All existing tests must pass.

Phase B — Opcodes + Compiler: Add Await and MakeAsyncFunction opcodes. Implement compiler support for async function bodies and await expressions.

Phase C — Runtime: Extend GeneratorObject for async (add async_promise_id). Implement MakeAsyncFunction handler (creates Promise, starts internal generator). Implement Await handler (suspends, registers then-handler on Promise).

Phase D — Integration: Wire up microtask queue to resume async functions. Implement the "auto-advance" mechanism. Test cross-component flow.

Phase E — Testing: Run all test suites, promote Test262 tests, update docs.

Project Structure Notes

  • All parser changes in crates/js_parser/src/ (Layer 1) — no layer violations
  • All VM changes in crates/js_vm/src/ (Layer 1) — no layer violations
  • Promise/microtask integration in crates/web_api/src/ (Layer 1) — no layer violations
  • No unsafe code needed — js_vm inherits workspace unsafe_code = "forbid"
  • Reuse GeneratorObject in crates/js_vm/src/generator.rs — extend rather than create new struct
  • New parser test file: crates/js_parser/src/parser/tests/async_tests.rs
  • New VM test file: crates/js_vm/src/interpreter/tests/async_tests.rs

References

  • ECMAScript §27.7 — AsyncFunction Objects — async function semantics, Await algorithm
  • ECMAScript §27.7.5.3 — Await — Promise wrapping, microtask scheduling for await
  • ECMAScript §15.8 — Async Function Definitions — syntax, async arrow functions
  • ECMAScript §27.2 — Promise Objects — Promise state machine, PerformPromiseThen
  • ECMAScript §27.2.1.3.2 — Promise Resolve Functions — wrapping non-Promise values
  • [Source: crates/js_parser/src/token.rs] — TokenKind enum (add Async, Await keywords)
  • [Source: crates/js_parser/src/ast.rs] — AST nodes (add is_async, AwaitExpr)
  • [Source: crates/js_parser/src/parser/statements.rs] — parse_fn_decl (add async function recognition)
  • [Source: crates/js_parser/src/parser/expressions.rs] — expression parsing (add await, async arrow)
  • [Source: crates/js_vm/src/bytecode/opcodes.rs] — opcode definitions (add Await, MakeAsyncFunction)
  • [Source: crates/js_vm/src/bytecode/chunk.rs] — FunctionBlueprint (add is_async)
  • [Source: crates/js_vm/src/bytecode/compiler/] — bytecode compiler (add async compilation)
  • [Source: crates/js_vm/src/interpreter/bytecode_exec.rs] — bytecode execution (add Await handling, async resume)
  • [Source: crates/js_vm/src/generator.rs] — GeneratorObject (extend for async, add async_promise_id)
  • [Source: crates/js_vm/src/environment.rs] — JsFunction struct (add is_async)
  • [Source: crates/web_api/src/promise.rs] — PromiseRegistry, PromiseRecord, settle_promise
  • [Source: crates/web_api/src/scheduling.rs] — MicrotaskQueue, Microtask struct
  • [Source: crates/web_api/src/lib.rs] — drain_microtasks loop (lines ~407-482)
  • [Source: crates/web_api/src/dom_host/host_environment.rs] — HostEnvironment impl for DomHost
  • [Source: crates/web_api/src/dom_host/mod.rs] — ID space layout, PROMISE_ID_START
  • [Source: docs/JavaScript_Implementation_Checklist.md] — Phase 24: async/await
  • [Source: _bmad-output/planning-artifacts/architecture.md#JavaScript Engine Evolution] — bytecode + async/await decision

Dev Agent Record

Agent Model Used

Claude Opus 4.6 (1M context)

Debug Log References

Completion Notes List

  • Implemented full async/await parser support: async function, async arrow functions, await expressions, async methods in classes and object literals, async generators (parse only)
  • Added Await (0xAA) and MakeAsyncFunction (0xAB) opcodes to the bytecode VM
  • Compiler emits MakeAsyncFunction for async functions/arrows/methods and Await for await expressions
  • Runtime reuses generator suspension mechanism: async functions are compiled as generator functions with is_async=true, using the same CallFrame save/restore for await points
  • Host environment integration: added internal host functions (__async_create_promise__, __async_resolve_promise__, __async_reject_promise__, __async_promise_state__, __async_register_resume__) for Promise creation and settlement from the bytecode VM
  • Microtask drain loop extended with __async_resume_fulfilled__/__async_resume_rejected__ callback detection for async function resumption from pending Promises
  • All await resumptions now go through microtask queue (including already-settled Promises and non-Promise values) per ECMAScript §27.7.5.3
  • All 634 parser tests pass (including 20 new async tests), all 1651 js_vm tests pass, all integration tests pass, just ci passes cleanly
  • Note: Subtasks 7.2, 7.3, 7.5 (runtime integration tests, full Test262 triage) remain for future work — parser and runtime infrastructure is complete

Code Review Fixes Applied

  • H1: Await on already-settled Promises/non-Promises now goes through microtask queue via __async_enqueue_resume__ host function
  • H2: compile_function_body now takes is_async param; async bodies emit GeneratorReturn instead of Return
  • H3: Eliminated recursive drive_async_generator calls (consequence of H1 fix)
  • H4: Fixed async_depth leak when async arrow body parsing fails
  • H5: RuntimeError::Thrown values now pass through directly as rejection reasons instead of being wrapped in Error objects
  • H6: Compiler-level guard added: await outside async function body is now a CompileError
  • H7: await dispatch moved from parse_assignment to parse_unary for correct precedence
  • M1: call_async_function returns TypeError instead of silent undefined when no host
  • M2: All let _ = on host calls replaced with ? error propagation
  • M3: Speculative arrow-param scanner now handles Async/Yield/Await keyword tokens
  • M4: Speculative arrow-param scanner now handles default parameter values (= expr)
  • M5: Object literal async method guard now excludes TokenKind::Assign
  • M6: await as parameter name rejected in async functions
  • M7: Async resume detection uses is_async_resume bool field on Microtask instead of magic string names
  • L3: parse_async_function_expr accepts await as function name in non-async outer context
  • L4: MakeAsyncFunction no longer sets func.is_generator = true
  • L5: Opcode roundtrip tests now cover Await and MakeAsyncFunction
  • L6: Async function declarations in blocks allowed in sloppy mode (Annex B)

File List

  • crates/js_parser/src/token.rs — Added Async and Await keyword variants to TokenKind
  • crates/js_parser/src/ast.rs — Added is_async to FnDecl, FunctionExpr, ClassMethod, ArrowFunctionExpr; added AwaitExpr to Expr
  • crates/js_parser/src/parser/mod.rs — Added async_depth to Parser, updated describe, expect_property_name, expr_span, expect_identifier
  • crates/js_parser/src/parser/statements.rs — Added parse_async_fn_decl, async method support in class bodies
  • crates/js_parser/src/parser/expressions.rs — Added parse_await_expr, parse_async_function_expr, async arrow/method parsing in parse_primary
  • crates/js_parser/src/parser/tests/mod.rs — Added async_tests module
  • crates/js_parser/src/parser/tests/async_tests.rs — New: 20 parser unit tests for all async syntax
  • crates/js_vm/src/bytecode/opcodes.rs — Added Await (0xAA) and MakeAsyncFunction(u16) (0xAB) opcodes
  • crates/js_vm/src/bytecode/chunk.rs — Added is_async to FunctionBlueprint
  • crates/js_vm/src/bytecode/compiler/expressions.rs — Emit MakeAsyncFunction for async functions, Await opcode for await expr
  • crates/js_vm/src/bytecode/compiler/statements.rs — Emit MakeAsyncFunction for async class methods
  • crates/js_vm/src/bytecode/compiler/mod.rs — Updated compile_function_hoist for async, passed is_async through pipeline
  • crates/js_vm/src/environment.rs — Added is_async to JsFunction
  • crates/js_vm/src/generator.rs — Added async_promise_id: Option<u64> to GeneratorObject
  • crates/js_vm/src/interpreter/bytecode_exec.rs — MakeAsyncFunction/Await opcode handlers, call_async_function, drive_async_generator, handle_async_await, Await in run_generator_step
  • crates/js_vm/src/interpreter/expressions/mod.rs — AwaitExpr stub in AST interpreter
  • crates/js_vm/src/lib.rs — Added resume_async_function to JsVm
  • crates/js/src/lib.rs — Added resume_async_function to JsEngine
  • crates/web_api/src/dom_host/host_environment.rs — Added _async* host functions for Promise integration
  • crates/web_api/src/lib.rs — Extended drain_microtasks for async resume callbacks
  • crates/web_api/src/scheduling.rs — Added is_async_resume field to Microtask struct
  • crates/web_api/src/promise.rs — Added is_async_resume field to ThenHandler
  • tests/external/js262/js262_full_manifest.toml — Updated full-suite test manifest
  • tests/external/js262/js262_full_status.toml — Updated full-suite test status overrides
  • tests/js_async.rs — New: 27 integration tests for async function execution and cross-feature integration
  • Cargo.toml — Added [[test]] entry for js_async
  • docs/JavaScript_Implementation_Checklist.md — Checked off Phase 24 async/await items