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

Reply via email to