// |jit-test| skip-if: !getBuildConfiguration("explicit-resource-management"); --enable-explicit-resource-management load(libdir + "asserts.js"); { const disposed = []; const stack = new AsyncDisposableStack(); async function testDisposalsWithAsyncDisposableStack() { const obj = { [Symbol.asyncDispose]() { disposed.push(1); }, }; stack.use(obj); assertEq(stack.disposed, false); assertEq(await stack.disposeAsync(), undefined); } testDisposalsWithAsyncDisposableStack(); drainJobQueue(); assertEq(stack.disposed, true); assertArrayEq(disposed, [1]); // `use` should throw if the stack is already disposed. assertThrowsInstanceOf(() => stack.use({ [Symbol.asyncDispose]: () => {} }), ReferenceError); } { const disposed = []; const stack = new AsyncDisposableStack(); async function testAsyncDisposableStackMultipleDispose() { for (let i = 0; i < 5; i++) { stack.use({ [Symbol.dispose]() { disposed.push(i); }, }); } assertEq(stack.disposed, false); await stack.disposeAsync(); // Calling again shouldn't do anything. await stack.disposeAsync(); } testAsyncDisposableStackMultipleDispose(); drainJobQueue(); assertEq(stack.disposed, true); assertArrayEq(disposed, [4, 3, 2, 1, 0]); } { const disposed = []; const stack = new AsyncDisposableStack(); async function testAsyncDisposableStackUseWithFallback() { stack.use({ [Symbol.asyncDispose]() { disposed.push(1); } }); stack.use({ [Symbol.dispose]() { disposed.push(2); } }); stack.use({ [Symbol.asyncDispose]: undefined, [Symbol.dispose]() { disposed.push(3); } }); stack.use({ [Symbol.asyncDispose]: null, [Symbol.dispose]() { disposed.push(4); } }); stack.use({ [Symbol.asyncDispose]() { disposed.push(5); }, [Symbol.dispose]() { // This shouldn't be called because @@asyncDispose exists. disposed.push(6); } }); assertEq(stack.disposed, false); await stack.disposeAsync(); } testAsyncDisposableStackUseWithFallback(); drainJobQueue(); assertEq(stack.disposed, true); assertArrayEq(disposed, [5, 4, 3, 2, 1]); } { const disposed = []; const stack = new AsyncDisposableStack(); for (let i = 0; i < 5; i++) { stack.use({ [Symbol.asyncDispose]() { disposed.push(i); }, }); } assertEq(stack.disposed, false); async function testAsyncDisposableStackWithUsingDecl() { { await using stk = stack; stk.use({ [Symbol.asyncDispose]() { disposed.push(5); }, }); stk.use({ [Symbol.dispose]() { disposed.push(6); } }); } assertEq(stack.disposed, true); { // This should be no-op. await using stk2 = stack; } assertEq(stack.disposed, true); } testAsyncDisposableStackWithUsingDecl(); drainJobQueue(); assertArrayEq(disposed, [6, 5, 4, 3, 2, 1, 0]); } { const disposed = []; const stack = new AsyncDisposableStack(); async function testAsyncDisposableStackWithNullUndefineds() { stack.use(undefined); stack.use(null); stack.use({ [Symbol.asyncDispose]() { disposed.push(1); } }); await stack.disposeAsync(); } testAsyncDisposableStackWithNullUndefineds(); drainJobQueue(); assertArrayEq(disposed, [1]); assertEq(stack.disposed, true); } { const disposed = []; const stack = new AsyncDisposableStack(); let error; stack.use({ [Symbol.asyncDispose]() { disposed.push(1); // This method is called during disposal, and before the // disposal process is started the stack's state is set to disposed. // Thus, calling stack use here again would throw. stack.use({ [Symbol.asyncDispose]() { disposed.push(2); }, }); } }); stack.disposeAsync().catch((e) => { error = e; }); drainJobQueue(); assertEq(error instanceof ReferenceError, true); assertArrayEq(disposed, [1]); assertEq(stack.disposed, true); } { assertThrowsInstanceOf(() => { const stack = new AsyncDisposableStack(); stack.use(1); }, TypeError); } { assertThrowsInstanceOf(() => { const stack = new AsyncDisposableStack(); stack.use({ [Symbol.asyncDispose]: undefined }); }, TypeError); assertThrowsInstanceOf(() => { const stack = new AsyncDisposableStack(); stack.use({ [Symbol.asyncDispose]: null }); }, TypeError); assertThrowsInstanceOf(() => { const stack = new AsyncDisposableStack(); stack.use({ [Symbol.asyncDispose]: 1 }); }, TypeError); }