← All issues

Implement `await using` syntax from Explicit Resource Management proposal

92e5222

JSTests/stress/await-using-declaration-basic.js

async function testNull() {
var order = [];
var wasSync = true;
var promise = (async function() {
{
order.push("before");
await using x = null; // null resource: 여전히 Await 하나를 강제함
order.push("after-decl");
}
order.push("after-block");
shouldBe(wasSync, false); // 실행이 일시 중단되었음을 확인 (awaited)
})();
wasSync = false;
await promise;
shouldBe(order.join(","), "before,after-decl,after-block");
}
async function testSyncFallback() {
var calledAsync = false;
var calledSync = false;
{
await using a = {
[Symbol.dispose]() { calledSync = true; } // @@asyncDispose 없음
};
}
shouldBe(calledAsync, false);
shouldBe(calledSync, true); // sync @@dispose로 fallback
}

Explicit Resource Management proposal은 JavaScript에 using(동기)과 await using(비동기) 선언을 추가합니다. 스코프를 벗어날 때 resource의 dispose method가 자동으로 호출되는 RAII 스타일의 resource 정리 방식입니다. JSC에는 이미 using이 구현되어 있었으며, 이 commit은 비동기 변형인 await using을 새롭게 추가합니다. await using은 disposal 중에 Await suspend point가 필요합니다. 구현에는 async function을 yield 기반 state machine으로 변환하는 JSC의 Generatorification 인프라가 재사용되었습니다.

bytecode compiler의 "using scope" 처리 로직은 UsingSlot 항목을 역순으로 순회하며 각 slot의 dispose method를 호출하는 finally 유사 블록을 생성합니다. async slot의 경우, 새로 추가된 @getAsyncDisposeMethod built-in이 먼저 @@asyncDispose를 조회하고 없으면 @@dispose로 fallback합니다. 이때 동기 결과를 resolved promise로 변환하는 wrapping closure가 함께 사용됩니다. spec에는 미묘한 불변 조건이 존재합니다. await using x = null은 정확히 하나의 Await(undefined)를 생성해야 한다는 요건입니다. 이를 위해 compiler는 needsAwait/hasAwaited 두 레지스터 프로토콜을 생성하여 disposal 중 실제 await 발생 여부를 추적합니다. 여기에 더해 per-slot reached boolean도 함께 추가되었습니다. 이 값은 method lookup 성공 여부를 별도로 추적하는 역할을 합니다. getter에서 예외가 발생하더라도 오류 전파 전에 spurious await가 유발되지 않도록 하기 위한 장치입니다. parser에는 await [no LineTerminator here] using production을 위한 두 토큰 lookahead와 savepoint restore가 추가되었습니다.

Disposal sequence for: { using a = r1; await using b = r2; }

  slot[1] (b, async, disposed first):
    @getAsyncDisposeMethod(b)
    ├─ method = undefined (null resource) → needsAwait = true
    └─ method found → call method(); Await(result); hasAwaited = true

  slot[0] (a, sync):
    Step 3.d: if (needsAwait && !hasAwaited) → Await(undefined)
    call a[@@dispose]()

  trailing Step 6: statically elided (last slot is sync)

JSC의 Explicit Resource Management 지원이 완성되었으며, scope exit finally 블록 안에 per-slot state machine 로직을 갖춘 새로운 비동기 실행 경로가 도입되었습니다. needsAwait/hasAwaited 프로토콜, reached 플래그의 의미론, @@asyncDispose@@dispose fallback wrapper는 각각 미묘한 정확성 요건을 수반합니다. 이들은 async generator lifecycle 및 예외 전파와 서로 맞물려 있습니다.

🔒

Multiple edge cases in the new async disposal state machine, method fallback path, and generator interaction are worth security investigation.

더 확인하려면 구독해 주세요