Branch: refs/heads/main
Home: https://github.com/WebKit/WebKit
Commit: 4ca75592d16592632adf93cdc3cbbea201bc6510
https://github.com/WebKit/WebKit/commit/4ca75592d16592632adf93cdc3cbbea201bc6510
Author: Sosuke Suzuki <[email protected]>
Date: 2026-03-09 (Mon, 09 Mar 2026)
Changed paths:
M .gitignore
A JSTests/stress/using-declaration-async.js
A JSTests/stress/using-declaration-basic.js
A JSTests/stress/using-declaration-error-suppression.js
A JSTests/stress/using-declaration-error.js
A JSTests/stress/using-declaration-eval.js
A JSTests/stress/using-declaration-for-disambiguation.js
A JSTests/stress/using-declaration-for-loop.js
A JSTests/stress/using-declaration-for-of-continue.js
A JSTests/stress/using-declaration-for-of.js
A JSTests/stress/using-declaration-generator.js
A JSTests/stress/using-declaration-identifier.js
A JSTests/stress/using-declaration-nested-scopes.js
A JSTests/stress/using-declaration-return.js
A JSTests/stress/using-declaration-switch.js
A JSTests/stress/using-declaration-syntax-errors.js
A JSTests/stress/using-declaration-tdz.js
M JSTests/test262/config.yaml
M Source/JavaScriptCore/builtins/DisposableStackPrototype.js
M Source/JavaScriptCore/bytecompiler/BytecodeGenerator.cpp
M Source/JavaScriptCore/bytecompiler/BytecodeGenerator.h
M Source/JavaScriptCore/bytecompiler/NodesCodegen.cpp
M Source/JavaScriptCore/parser/Nodes.h
M Source/JavaScriptCore/parser/Parser.cpp
M Source/JavaScriptCore/parser/Parser.h
M Source/JavaScriptCore/parser/VariableEnvironment.h
M Source/JavaScriptCore/runtime/CommonIdentifiers.cpp
M Source/JavaScriptCore/runtime/CommonIdentifiers.h
Log Message:
-----------
[JSC] Implement `using` syntax from Explicit Resource Management proposal
https://bugs.webkit.org/show_bug.cgi?id=308507
Reviewed by Yusuke Suzuki.
This patch implements `using` declarations from the Explicit Resource Management
proposal. `await using` will be addressed in a subsequent patch.
A `using` declaration automatically invokes the @@dispose method of a resource
when the enclosing scope is exited. The disposal timing is the same as an
existing finally block.
This patch primarily modifies the parser and the bytecode compiler. No new
bytecodes are introduced.
** Parser **
`using` is not a reserved word. When the parser encounters an unescaped
identifier named `using`, it looks ahead several tokens to determine whether it
is a using declaration. If so, it is parsed through the existing
parseVariableDeclarationList function. Like `const`, a `using` binding is
non-assignable and requires an initializer, except in for-of heads. A new
IsUsing bit is set on VariableEnvironmentEntry so that the bytecode generator
can later determine the number of using declarations in each scope.
`using` can also appear in for-of heads, e.g. `for (using a of o)`, which
requires somewhat complex disambiguation. For example, `for (using of expr)`
must be interpreted as a regular for-of loop rather than a using declaration,
per the spec's [lookahead ≠ `using` `of`] constraint. Additionally, writing a
`using` declaration directly inside a switch case/default clause without a block
is a SyntaxError.
** Bytecode Compiler **
BytecodeGenerator maintains a new stack structure called using scope. Each entry
in a using scope is a pair of the resource value and its @@dispose method. Slots
are pre-allocated before the try block, with the method initialized to
undefined. This ensures that the finally block operates safely even if an
initializer throws partway through. As each `using` declaration is encountered
within the try block, the value and the dispose method obtained via
getDisposeMethod are written into the pre-allocated slots. In the finally block,
the slots are iterated in reverse order to invoke @@dispose on each resource.
If an error occurs during disposal in the finally block, it is chained with any
existing error using SuppressedError, so that all error information is preserved
even when multiple disposals fail. SuppressedError is already implemented in
JSC, so this patch reuses the existing implementation.
For example, given the following using declarations:
{
using a = resource1;
using b = resource2;
}
The following bytecode is generated:
// --- Pre-allocate slots (before try) ---
mov slot[0].method, Undefined
mov slot[1].method, Undefined
mov completionType, Normal
// --- try block ---
// using a = resource1;
mov slot[0].value, resource1
call slot[0].method, getDisposeMethod(resource1)
// using b = resource2;
mov slot[1].value, resource2
call slot[1].method, getDisposeMethod(resource2)
// --- finally block ---
mov pendingError, Undefined
mov hasError, False
mov disposeThrew, False
// Record body error if the body threw
jnstricteq completionType, Throw, skip_body_error
mov pendingError, thrownValue
mov hasError, True
skip_body_error:
// Dispose slot[1] (reverse order: b first)
jstricteq slot[1].method, Undefined, skip_slot1
call_ignore_result slot[1].method.call(slot[1].value)
jmp skip_slot1
catch_slot1: // Disposal threw
jfalse hasError, first_error_slot1
call pendingError, SuppressedError(newError, pendingError)
jmp done_slot1
first_error_slot1:
mov pendingError, newError
mov hasError, True
done_slot1:
mov disposeThrew, True
skip_slot1:
// Dispose slot[0] (a)
jstricteq slot[0].method, Undefined, skip_slot0
call_ignore_result slot[0].method.call(slot[0].value)
jmp skip_slot0
catch_slot0: // Same pattern
...
skip_slot0:
// Final completion
jfalse disposeThrew, no_dispose_error
throw pendingError // Throw disposal error
no_dispose_error:
jnstricteq completionType, Throw, done
throw originalError // Re-throw body error
done:
ret
* JSTests/stress/using-declaration-basic.js: Added.
(shouldBe):
(shouldThrow):
(throw.new.Error):
(using.a.Symbol.dispose):
(using.b.Symbol.dispose):
(using.c.Symbol.dispose):
(shouldBe.order.join.test):
(shouldBe.order.join):
* JSTests/stress/using-declaration-error.js: Added.
(shouldBe):
(throw.new.Error):
* JSTests/stress/using-declaration-for-of.js: Added.
(shouldBe):
(throw.new.Error):
* JSTests/stress/using-declaration-syntax-errors.js: Added.
(shouldThrowSyntaxError):
(shouldThrowSyntaxError.string_appeared_here.shouldThrowSyntaxError):
* JSTests/test262/config.yaml:
* Source/JavaScriptCore/builtins/DisposableStackPrototype.js:
(linkTimeConstant.disposeResources):
* Source/JavaScriptCore/bytecompiler/BytecodeGenerator.cpp:
(JSC::BytecodeGenerator::emitAddDisposableResource):
(JSC::BytecodeGenerator::emitDisposeResources):
(JSC::BytecodeGenerator::emitUsingBodyScope):
* Source/JavaScriptCore/bytecompiler/BytecodeGenerator.h:
(JSC::BytecodeGenerator::pushDisposeCapability):
(JSC::BytecodeGenerator::currentDisposeCapability):
(JSC::BytecodeGenerator::popDisposeCapability):
* Source/JavaScriptCore/bytecompiler/NodesCodegen.cpp:
(JSC::initializationModeForAssignmentContext):
(JSC::AssignResolveNode::emitBytecode):
(JSC::BlockNode::emitBytecode):
(JSC::ForNode::emitBytecode):
(JSC::ForOfNode::emitBytecode):
(JSC::SwitchNode::emitBytecode):
(JSC::emitProgramNodeBytecode):
(JSC::EvalNode::emitBytecode):
(JSC::FunctionNode::emitBytecode):
(JSC::BindingNode::bindValue const):
* Source/JavaScriptCore/parser/Nodes.h:
(JSC::VariableEnvironmentNode::hasUsingDeclaration const):
* Source/JavaScriptCore/parser/Parser.cpp:
(JSC::Parser<LexerType>::parseStatementListItem):
(JSC::Parser<LexerType>::parseVariableDeclaration):
(JSC::Parser<LexerType>::parseVariableDeclarationList):
(JSC::Parser<LexerType>::parseForStatement):
(JSC::Parser<LexerType>::parseSwitchClauses):
(JSC::Parser<LexerType>::parseSwitchDefaultClause):
(JSC::Parser<LexerType>::parseBlockStatement):
* Source/JavaScriptCore/parser/Parser.h:
(JSC::Scope::takeLexicalEnvironment):
(JSC::Scope::declareLexicalVariable):
(JSC::Scope::setHasUsingDeclaration):
(JSC::Scope::hasUsingDeclaration const):
(JSC::Parser::destructuringKindFromDeclarationType):
(JSC::Parser::declarationTypeToVariableKind):
(JSC::Parser::assignmentContextFromDeclarationType):
(JSC::Parser::popScopeInternal):
(JSC::Parser::declareVariable):
* Source/JavaScriptCore/parser/VariableEnvironment.cpp:
(JSC::VariableEnvironment::swap):
* Source/JavaScriptCore/parser/VariableEnvironment.h:
(JSC::VariableEnvironmentEntry::isUsing const):
(JSC::VariableEnvironmentEntry::setIsUsing):
(JSC::VariableEnvironment::VariableEnvironment):
(JSC::VariableEnvironment::hasUsingDeclaration const):
(JSC::VariableEnvironment::setHasUsingDeclaration):
* Source/JavaScriptCore/runtime/CachedTypes.cpp:
(JSC::CachedVariableEnvironment::encode):
(JSC::CachedVariableEnvironment::decode const):
* Source/JavaScriptCore/runtime/CommonIdentifiers.cpp:
(JSC::CommonIdentifiers::CommonIdentifiers):
* Source/JavaScriptCore/runtime/CommonIdentifiers.h:
Canonical link: https://commits.webkit.org/308955@main
To unsubscribe from these emails, change your notification settings at
https://github.com/WebKit/WebKit/settings/notifications