JSC `using` Declarations (Explicit Resource Management)
Source/JavaScriptCore/bytecompiler/BytecodeGenerator.cpp
Source/JavaScriptCore/parser/Parser.cpp
This commit implements using declarations from the TC39 Explicit Resource Management proposal (Stage 4) in JavaScriptCore — the JavaScript equivalent of C++ RAII or Python with statements. When a using binding goes out of scope, JSC automatically calls Symbol.dispose on the resource.
The implementation spans three major areas. The parser gains multi-token lookahead logic to disambiguate using as a keyword versus a plain identifier — using is not a reserved word in JavaScript, so using x = ... (declaration) must be distinguished from using as a variable name. The disambiguation handles several edge cases including for (using of expr) (must parse as plain for-of per the spec's [lookahead ≠ using of] restriction) and escaped \u0075sing (must be treated as an identifier, not a keyword).
The bytecode compiler models disposal as a synthetic finally block. Disposal slots are pre-allocated before the try body so that partial initialization — an initializer that throws midway through a list — still runs cleanup for successfully initialized resources. Disposal runs in reverse declaration order, and if multiple disposals throw, errors are chained into a SuppressedError rather than dropped.
Scope entry
│
├─ Pre-allocate slots (slot[i].method = undefined) ← before try
│
├─ try block
│ ├─ slot[0].value = resource1; slot[0].method = getDisposeMethod(resource1)
│ └─ slot[1].value = resource2; slot[1].method = getDisposeMethod(resource2)
│
└─ finally block (reverse order)
├─ call slot[1].method(slot[1].value) → on throw: SuppressedError(newErr, pendingErr)
├─ call slot[0].method(slot[0].value) → on throw: SuppressedError(newErr, pendingErr)
└─ if disposeThrew → throw pendingError
else if body threw → rethrow originalError
await using is deferred to a follow-up patch.
Significance
This is a non-trivial new JavaScript language feature with complex parser disambiguation, new scope tracking, and error-chaining semantics. The parser lookahead alone — distinguishing using x = ... from using as an identifier, handling escaped unicode, and interacting with for-of head parsing — introduces subtle edge cases. The disposal-as-finally-block model adds new bytecode patterns that interact with exception handling, generators, and scope exit in ways that are historically fertile ground for JIT and bytecode compiler bugs.