Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Acorn への Explicit Resource Management 構文サポート実装

Sponsored · Ship Features Fearlessly Turn features on and off without deploys. Used by thousands of Ruby developers.

Acorn への Explicit Resource Management 構文サポート実装

本スライドでは、JavaScript パーサー Acorn における Explicit Resource Management (ERM) 構文サポートの実装について解説します。
• ECMAScript 提案の背景と目的
メモリやファイルハンドルなどのリソースを明示的に管理する仕組みを提供することで、コードの安全性・可読性を高める狙いがあります。
• 仕様書の読み解き方
ECMAScript 言語仕様の構文定義を実例を交えながら紹介し、using や await using などの新しい構文がどのように定義されているかを説明します 。
• 実装における課題
• using が 予約語でないことによるパース上の曖昧さ
• バックトラックのない Acorn での判定の難しさ
• 部分的に再解釈する実装手法やカバー文法の工夫
• 具体的な実装例
Acorn のコード断片を引用し、どのように await using を解釈しているかを詳細に解説します 。

この発表は、言語仕様やパーサー実装に関心のあるエンジニアを対象に、仕様書と実装の橋渡しを意識した内容となっています。

Avatar for Yuichiro Yamashita

Yuichiro Yamashita

September 06, 2025
Tweet

More Decks by Yuichiro Yamashita

Other Decks in Programming

Transcript

  1. 自己紹介
 • 株式会社フライル
 ◦ ソフトウェアエンジニア
 ◦ 最近は ClickHouse というデータベースと格闘しています
 


    • Svelte コアチーム
 ◦ 最近は eslint-plugin-svelte や svelte-eslint-parser を担当しています

  2. アジェンダ
 • ECMAScript / TC39 とは何か
 • JavaScript パーサー /

    Acorn とは何か
 • Explicit Resource Management とは何か
 • 言語仕様書の読み方
 • 実装する上で難しかったこと

  3. ECMAScript / TC39 とは何か
 Ecma International という標準化団体が様々な仕様の標準化を推進しており、「ECMA-262」という文書にて、 「ECMAScript」を定義しています。 
 (ちなみに、JSONの規格は

    ECMA-404 で定義されています) 
 「ECMAScript」は、JavaScriptの言語仕様です。JavaScriptの機能や文法、APIなどの仕様を定義しています。 
 Ecma International の TC39 は、JavaScript開発者、実装者、専門家などのグループで、JavaScriptの仕様をメ ンテナンスし発展させるためにコミュニティと協力しています。 
 (出典: https://tc39.es/ja/)
 1言で簡単にいうと TC39 というグループが JavaScript の仕様を決めている。

  4. ECMAScript / TC39 とは何か
 TC39 には、正式に仕様として承認されるまでに複数のステージが存在します 
 ステージ
 説明
 0


    新しい提案。TC39では未検討 
 1
 検討中。チャンピオン (提案責任者) が設定される 
 2
 設計はまだ草案段階であり、今後大幅に変更される可能性があるが、委員会は、この 機能が開発され、最終的に標準規格に含まれることを期待している 
 2.7
 提案は原則的に承認され、検証中 
 3
 この提案は実装が推奨されている 
 4
 提案された機能は完成している。2つの独立したVMによって提供されている 

  5. JavaScript パーサー / Acorn とは何か
 JavaScriptパーサーとは、JavaScriptのコードを「構造化された情報」に変換するプログラムのことで す。
 Acorn とは、JavaScript パーサーのひとつ。Next.js

    / Svelte / Webpack / Prettier / ESLint / Rollup (スター数順) など多くの有名なライブラリが Acorn に依存しています。 
 A tiny, fast JavaScript parser, written completely in JavaScript. 
 (完全に JavaScript で書かれた、小型で高速な JavaScript パーサーです) 
 (出典: https://github.com/Acornjs/Acorn) 

  6. JavaScript パーサー / Acorn とは何か
 console.log("hi!")
 {
 "type": "Program",
 "body":

    [{
 "type": "ExpressionStatement",
 "expression": {
 "type": "CallExpression",
 "callee": {
 "type": "MemberExpression",
 "object": { "type": "Identifier", "name": "console" },
 "property": { "type": "Identifier", "name": "log" },
 "computed": false,
 "optional": false
 },
 "arguments": [{ "type": "Literal", "value": "hi", "raw": "\"hi\"" }],
 "optional": false
 }
 }],
 "sourceType": "module"
 }
 
 パースすると
  7. Explicit Resource Management とは何か
 This proposal intends to address a

    common pattern in software development regarding the lifetime and management of various resources (memory, I/O, etc.). This pattern generally includes the allocation of a resource and the ability to explicitly release critical resources. 
 この提案は、ソフトウェア開発における様々なリソース(メモリ、I/Oなど)の有効期間と管理に関する 一般的なパターンに対処することを目的としています。このパターンには、一般的に、リソースの割り 当てと、重要なリソースを明示的に解放する機能が含まれます。 
 
 出典: https://github.com/tc39/proposal-explicit-resource-management 
 
 

  8. Explicit Resource Management とは何か
 function begin() {
 const conn =

    {
 execute: (sql) => { console.log(`[log] Run SQL "${sql}".`) },
 dispose: () => { console.log('[log] Connection disposed.' ) },
 };
 console.log(`[log] Connection established.` );
 return {
 execute: conn.execute,
 [Symbol.dispose]() {
 conn.dispose();
 },
 };
 }
 function main() {
 using conn = begin();
 conn.execute('SELECT 1');
 }
 
 main();

  9. Explicit Resource Management とは何か
 function begin() {
 const conn =

    {
 execute: (sql) => { console.log(`[log] Run SQL "${sql}".`) },
 dispose: () => { console.log('[log] Connection disposed.' ) },
 };
 console.log(`[log] Connection established.` );
 return {
 execute: conn.execute,
 [Symbol.dispose]() {
 conn.dispose();
 },
 };
 }
 function main() {
 using conn = begin();
 conn.execute('SELECT 1');
 }
 
 main();
 ポイント1
 
 dispose というシンボルの関数を定 義する
 ポイント2
 
 using というキーワード (≠予約語) を用いて変数宣言する 

  10. Explicit Resource Management とは何か
 function begin() {
 const conn =

    {
 execute: (sql) => { console.log(`[log] Run SQL "${sql}".`) },
 dispose: () => { console.log('[log] Connection disposed.' ) },
 };
 console.log(`[log] Connection established.` );
 return {
 execute: conn.execute,
 [Symbol.dispose]() {
 conn.dispose();
 },
 };
 }
 function main() {
 using conn = begin();
 conn.execute('SELECT 1');
 }
 
 main();

  11. Explicit Resource Management とは何か
 他にも
 • using の非同期版である await using

    / symbol.asyncDispose
 • using を更に込み入った使い方をするための DisposableStack
 • DisposableStack の非同期版である AsyncDisposableStack
 があります。

  12. Explicit Resource Management とは何か
 ⚠ 注意
 • 本機能は TC39 にてステージ4に進むことが承認されているフェーズです


    • 2025年9月4日時点では、 v8 (Chrome) / SpiderMonkey (Firefox) に実装済みで す。(JavaScript Core (Safari) は現在実装中)
 

  13. 言語仕様書の読み方
 UsingDeclaration は、 
 using という終端記号の後に、 
 改行を含まずに BindingList 非終端記号が続く

    
 
 (BindingList は、例えば foo = 1; みたいな) 
 出典: https://arai-a.github.io/ecma262-compare/snapshot.html?pr=3000
  14. 言語仕様書の読み方
 LexicalDeclaration は、 let, const で宣言する以外 に using, await using

    で宣言できる。 
 出典: https://arai-a.github.io/ecma262-compare/snapshot.html?pr=3000
  15. using が予約語でないことによる難しさ
 予約語とは変数名や関数名に使用できないキーワードのこと 
 例: if, for, while, class, const

    など
 出典: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Lexical_grammar#keywords
 条件つきの予約語もある
 • strict mode でのみ予約語: let, static, yield 
 • モジュールもしくは非同期関数でのみ予約語: await 
 将来のために予め予約されている語もある 
 例: enum, implements, interface (※ enum 以外は strict mode でのみ予約されている)
 👉 ここに using もあったらよかったのに 😅

  16. using が予約語でないことによる難しさ
 {
 // ケース1
 using using = foo();
 //

    ケース2
 using = 5;
 
 // ケース3
 using
 x = resource;
 }
 

  17. using が予約語でないことによる難しさ
 【ケース1,2】
 using だけを読んでも、何も確定できない。 
 using の後にシンボルが来た場合のみ、変数宣言であると確定できる。 
 (await

    using の場合は2トークン分先読みしないと変数宣言であると確定できな い)
 {
 // ケース1
 using using = foo();
 // ケース2
 using = 5;
 
 // ケース3
 using
 x = resource;
 }
 【ケース3】
 using だけを読んでも、何も確定できない。 
 using の後に改行が来た場合は using はただの変数参照になる。 
 
 using が予約語であれば、using がシンボルに使われることはない ので、using が現れた時点で (Await)UsingDeclaration であることを 前提に処理できた。

  18. using が予約語でないことによる難しさ
 問い: 単に using を予約語にしたらいいやん
 
 回答: 例えば const

    using = 5; というコードがあったとして、
 Chrome の特定のバージョンまでは動くけど、特定のバージョン以降ではエラーになり ます! だと困る。
 既に存在する JavaScript を壊さないようにするために、後方互換性を維持しながら新 機能を追加していく必要がある。

  19. Acorn にバックトラックがないことによる難しさ
 • パーサーの種類 (一部)
 ◦ LLパーサー (Acornはこれがベース) 
 ▪

    実装はシンプルだが曖昧な構文に弱い 
 ◦ LRパーサー (V8 はこれ (らしい)) 
 ▪ 実装は複雑だが曖昧な構文に強い 
 
 • LLパーサーには、バックトラッキングを備える場合がある 
 ◦ 例えば await が来たら一旦 await foo() のような文法が来ると過程して読み進めて、違っ ていたら、戻ってきて、次は await using foo = bar() のような文法であると過程して読んで みる。

  20. Acorn にバックトラックがないことによる難しさ
 • LLパーサーとは (非常に簡単に説明すると)
 await bar(); / await using

    foo = bar();
 
 
 • LRパーサーとは (非常に簡単に説明すると)
 await bar(); / await using foo = bar();
 
 await を見つけたら、文字列を右に読み進めて (lookaheadして)
 どの文法規則を適用できるかを判定する。
 適用できる文法規則が1つに定まるまで読み進めて、文法規則が確定した時点で、 その範囲の文法規則を確定する。

  21. Acorn にバックトラックがないことによる難しさ
 例: await というキーワードが来た場合に、それが await using であるかを判定するプログラム (の一部) 


    
 直後が “using” でなければ違う 
 出典: https://github.com/acornjs/acorn/commit/b4ae0d29384f2bf3fafac7d42f1c3e2ee9a48204#diff-781c700ea716836ca6843f093b6a7d4 86849dec5b3c1ab428c6ce286592913dfR86-R91
  22. Acorn にバックトラックがないことによる難しさ
 例: await というキーワードが来た場合に、それが await using であるかを判定するプログラム (の一部) 


    
 “await” でファイルが終わっていたら違う 
 出典: https://github.com/acornjs/acorn/commit/b4ae0d29384f2bf3fafac7d42f1c3e2ee9a48204#diff-781c700ea716836ca6843f093b6a7d4 86849dec5b3c1ab428c6ce286592913dfR86-R91
  23. Acorn にバックトラックがないことによる難しさ
 例: await というキーワードが来た場合に、それが await using であるかを判定するプログラム (の一部) 


    
 “await” の次の文字が 識別子 の場合は違う 
 (サロゲートペアの上位領域であるかも確認しないと いけない
 出典: https://github.com/acornjs/acorn/commit/b4ae0d29384f2bf3fafac7d42f1c3e2ee9a48204#diff-781c700ea716836ca6843f093b6a7d4 86849dec5b3c1ab428c6ce286592913dfR86-R91
  24. 振り返り
 • パーサーの種類
 ◦ LLパーサー (Acornはこれがベース) 
 ▪ 実装はシンプルだが曖昧な構文に弱い 


    ◦ LRパーサー (V8 はこれ (らしい)) 
 ▪ 実装は複雑だが曖昧な構文に強い 
 
 • Acorn は性能を良くするためにバックトラックを使用しない 

  25. 振り返り
 A tiny, fast JavaScript parser, written completely in JavaScript.

    
 (完全に JavaScript で書かれた、小型で高速な JavaScript パーサーです) 
 (出典: https://github.com/Acornjs/Acorn) 
 言い換えると 完全に JavaScript で書かれた、 LL方式でバックトラックを使用しない JavaScript パーサー です

  26. 想定質問と回答
 Q. using って何を解決するんですか? finally で書けばよくないんですか? 
 A. finally で手動で解放処理を書くのは忘れやすく冗長です。using

    を使うとスコープ終了時に自動で解放されるため、コードが簡潔で安全になります。 
 
 Q. 非同期の using はどうやって書くんですか? 
 A. await using と書きます。例えば非同期でリソースを開放するオブジェクトを扱う場合に使えます。 
 
 Q. using と await using の違いは何ですか? 
 A. using は同期的に解放、await using は非同期処理を待ってから解放します。 
 
 Q. TypeScript でも使えますか? 
 A. TypeScript 5.2 から使用可能です。