JSC `using` Declarations (Explicit Resource Management)
Source/JavaScriptCore/bytecompiler/BytecodeGenerator.cpp
Source/JavaScriptCore/parser/Parser.cpp
TC39 Explicit Resource Management 제안(Stage 4)의 using 선언을 JavaScriptCore에 구현한 commit입니다. C++의 RAII, Python의 with 문과 동등한 JavaScript 기능으로, using 바인딩이 스코프를 벗어나는 시점에 JSC가 자동으로 해당 리소스의 Symbol.dispose를 호출합니다.
구현은 크게 세 영역에 걸쳐 있습니다. 먼저 파서에는 using을 키워드로 볼지, 일반 식별자로 볼지 판단하기 위한 multi-token lookahead 로직이 추가되었습니다. using은 JavaScript의 예약어가 아니기 때문에, using x = ... 형태의 선언과 변수명으로 쓰인 using을 구분해야 합니다. 이 과정에서 몇 가지 edge case가 처리됩니다. for (using of expr)는 스펙의 [lookahead ≠ using of] 제약에 따라 일반 for-of로 파싱해야 합니다. 또한 \u0075sing처럼 escape된 형태는 키워드가 아닌 식별자로 처리해야 합니다.
바이트코드 컴파일러는 disposal을 합성된 finally 블록으로 모델링합니다. Disposal slot은 try 블록 진입 전에 미리 할당되어 있어, partial initialization — 초기화 목록 중간에 예외가 발생하는 상황 — 에서도 이미 초기화된 리소스의 정리 작업은 빠짐없이 실행됩니다. 또한 disposal은 선언의 역순으로 진행되며, 여러 disposal에서 예외가 발생하면 오류를 버리는 대신 SuppressedError로 연결해 보존합니다.
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은 후속 패치로 미루어졌습니다.
Significance
새로운 JavaScript 언어 기능이지만, 구현이 단순하지 않습니다. Parser disambiguation이 복잡하고, 새로운 scope 추적 방식이 도입되며, error-chaining 시맨틱까지 포함됩니다. 파서의 lookahead만 하더라도 섬세한 edge case가 생겨납니다. using x = ...과 식별자로 쓰인 using의 구분, escape된 unicode 처리, for-of head 파싱과의 상호작용 — 각각이 서로 다른 처리를 필요로 합니다. 한편 disposal-as-finally-block 모델은 새로운 bytecode 패턴을 추가합니다. 이 패턴은 exception handling, generator, scope exit과 상호작용하는데, 이런 조합은 역사적으로 JIT 및 bytecode 컴파일러 버그가 빈번하게 발생해온 영역입니다.