diff --git a/test/built-ins/AsyncDisposableStack/prototype/Symbol.toStringTag.js b/test/built-ins/AsyncDisposableStack/prototype/Symbol.toStringTag.js new file mode 100644 index 00000000000..aa4f13be72c --- /dev/null +++ b/test/built-ins/AsyncDisposableStack/prototype/Symbol.toStringTag.js @@ -0,0 +1,23 @@ +// Copyright (C) 2023 Ron Buckton. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-asyncdisposablestack.prototype-@@toStringTag +description: > + `Symbol.toStringTag` property descriptor +info: | + The initial value of the @@toStringTag property is the String value + 'AsyncDisposableStack'. + + This property has the attributes { [[Writable]]: false, [[Enumerable]]: + false, [[Configurable]]: true }. +includes: [propertyHelper.js] +features: [explicit-resource-management, Symbol, Symbol.toStringTag] +---*/ + +verifyProperty(AsyncDisposableStack.prototype, Symbol.toStringTag, { + value: 'AsyncDisposableStack', + writable: false, + enumerable: false, + configurable: true +}); diff --git a/test/built-ins/AsyncDisposableStack/prototype/adopt/adds-value-onDisposeAsync.js b/test/built-ins/AsyncDisposableStack/prototype/adopt/adds-value-onDisposeAsync.js new file mode 100644 index 00000000000..6df838a5d92 --- /dev/null +++ b/test/built-ins/AsyncDisposableStack/prototype/adopt/adds-value-onDisposeAsync.js @@ -0,0 +1,41 @@ +// Copyright (C) 2023 Ron Buckton. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-asyncdisposablestack.prototype.adopt +description: Adds a disposable resource to the stack +info: | + AsyncDisposableStack.prototype.adopt ( value, onDisposeAsync ) + + 1. Let asyncDisposableStack be the this value. + 2. Perform ? RequireInternalSlot(asyncDisposableStack, [[AsyncDisposableState]]). + 3. If asyncDisposableStack.[[AsyncDisposableState]] is disposed, throw a ReferenceError exception. + 4. If IsCallable(onDisposeAsync) is false, throw a TypeError exception. + 5. Let closure be a new Abstract Closure with no parameters that captures value and onDisposeAsync and performs the following steps when called: + a. Perform ? Call(onDisposeAsync, undefined, « value »). + 6. Let F be CreateBuiltinFunction(closure, 0, "", « »). + 7. Perform ? AddDisposableResource(asyncDisposableStack.[[DisposeCapability]], undefined, async-dispose, F). + ... + + AddDisposableResource ( disposeCapability, V, hint [, method ] ) + + 1. If method is not present then, + ... + 2. Else, + a. Assert: V is undefined. + b. Let resource be ? CreateDisposableResource(undefined, hint, method). + 3. Append resource to disposeCapability.[[DisposableResourceStack]]. + 4. Return unused. + +flags: [async] +includes: [asyncHelpers.js] +features: [explicit-resource-management] +---*/ + +asyncTest(async function () { + var stack = new AsyncDisposableStack(); + var resource = { disposed: false }; + stack.adopt(resource, async r => { r.disposed = true }); + await stack.disposeAsync(); + assert.sameValue(resource.disposed, true, 'Expected resource to have been disposed'); +}); diff --git a/test/built-ins/AsyncDisposableStack/prototype/adopt/allows-any-value.js b/test/built-ins/AsyncDisposableStack/prototype/adopt/allows-any-value.js new file mode 100644 index 00000000000..ff085f40a77 --- /dev/null +++ b/test/built-ins/AsyncDisposableStack/prototype/adopt/allows-any-value.js @@ -0,0 +1,33 @@ +// Copyright (C) 2023 Ron Buckton. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-asyncdisposablestack.prototype.adopt +description: Allows any 'value' +info: | + AsyncDisposableStack.prototype.adopt ( value, onDisposeAsync ) + + 1. Let asyncDisposableStack be the this value. + 2. Perform ? RequireInternalSlot(asyncDisposableStack, [[AsyncDisposableState]]). + 3. If asyncDisposableStack.[[AsyncDisposableState]] is disposed, throw a ReferenceError exception. + 4. If IsCallable(onDisposeAsync) is false, throw a TypeError exception. + 5. Let closure be a new Abstract Closure with no parameters that captures value and onDisposeAsync and performs the following steps when called: + a. Perform ? Call(onDisposeAsync, undefined, « value »). + 6. Let F be CreateBuiltinFunction(closure, 0, "", « »). + 7. Perform ? AddDisposableResource(asyncDisposableStack.[[DisposeCapability]], undefined, async-dispose, F). + ... + +features: [explicit-resource-management] +---*/ + +var stack = new AsyncDisposableStack(); +stack.adopt(null, async _ => {}); +stack.adopt(undefined, async _ => {}); +stack.adopt({}, async _ => {}); +stack.adopt({ async [Symbol.asyncDispose]() {} }, async _ => {}); +stack.adopt(() => {}, async _ => {}); +stack.adopt(true, async _ => {}); +stack.adopt(false, async _ => {}); +stack.adopt(1, async _ => {}); +stack.adopt('object', async _ => {}); +stack.adopt(Symbol(), async _ => {}); diff --git a/test/built-ins/AsyncDisposableStack/prototype/adopt/puts-value-onDisposeAsync-on-top-of-stack.js b/test/built-ins/AsyncDisposableStack/prototype/adopt/puts-value-onDisposeAsync-on-top-of-stack.js new file mode 100644 index 00000000000..a1941cce441 --- /dev/null +++ b/test/built-ins/AsyncDisposableStack/prototype/adopt/puts-value-onDisposeAsync-on-top-of-stack.js @@ -0,0 +1,50 @@ +// Copyright (C) 2023 Ron Buckton. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-asyncdisposablestack.prototype.adopt +description: Adds a disposable resource to the stack +info: | + AsyncDisposableStack.prototype.adopt ( value, onDisposeAsync ) + + 1. Let asyncDisposableStack be the this value. + 2. Perform ? RequireInternalSlot(asyncDisposableStack, [[AsyncDisposableState]]). + 3. If asyncDisposableStack.[[AsyncDisposableState]] is disposed, throw a ReferenceError exception. + 4. If IsCallable(onDisposeAsync) is false, throw a TypeError exception. + 5. Let closure be a new Abstract Closure with no parameters that captures value and onDisposeAsync and performs the following steps when called: + a. Perform ? Call(onDisposeAsync, undefined, « value »). + 6. Let F be CreateBuiltinFunction(closure, 0, "", « »). + 7. Perform ? AddDisposableResource(asyncDisposableStack.[[DisposeCapability]], undefined, async-dispose, F). + ... + + AddDisposableResource ( disposeCapability, V, hint [, method ] ) + + 1. If method is not present then, + ... + 2. Else, + a. Assert: V is undefined. + b. Let resource be ? CreateDisposableResource(undefined, hint, method). + 3. Append resource to disposeCapability.[[DisposableResourceStack]]. + 4. Return unused. + +flags: [async] +includes: [asyncHelpers.js] +features: [explicit-resource-management] +---*/ + +asyncTest(async function () { + var stack = new AsyncDisposableStack(); + var disposed = []; + var resource1 = {}; + async function dispose1(res) { disposed.push([res, dispose1]); } + var resource2 = {}; + function dispose2(res) { disposed.push([res, dispose2]); } + stack.adopt(resource1, dispose1); + stack.adopt(resource2, dispose2); + await stack.disposeAsync(); + assert.sameValue(2, disposed.length); + assert.sameValue(disposed[0][0], resource2, 'Expected resource2 to be the first disposed resource'); + assert.sameValue(disposed[0][1], dispose2, 'Expected dispose2 to be the first onDispose invoked'); + assert.sameValue(disposed[1][0], resource1, 'Expected resource1 to be the second disposed resource'); + assert.sameValue(disposed[1][1], dispose1, 'Expected dispose1 to be the second onDispose invoked'); +}); diff --git a/test/built-ins/AsyncDisposableStack/prototype/adopt/returns-value.js b/test/built-ins/AsyncDisposableStack/prototype/adopt/returns-value.js new file mode 100644 index 00000000000..5ac7988567e --- /dev/null +++ b/test/built-ins/AsyncDisposableStack/prototype/adopt/returns-value.js @@ -0,0 +1,20 @@ +// Copyright (C) 2023 Ron Buckton. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-asyncdisposablestack.prototype.adopt +description: Returns the argument provided. +info: | + AsyncDisposableStack.prototype.adopt ( value, onDisposeAsync ) + + ... + 8. Return value. + +features: [explicit-resource-management] +---*/ + +var stack = new AsyncDisposableStack(); +var resource = {}; +assert.sameValue(stack.adopt(resource, async _ => {}), resource); +assert.sameValue(stack.adopt(null, async _ => {}), null); +assert.sameValue(stack.adopt(undefined, async _ => {}), undefined); diff --git a/test/built-ins/AsyncDisposableStack/prototype/adopt/throws-if-disposed.js b/test/built-ins/AsyncDisposableStack/prototype/adopt/throws-if-disposed.js new file mode 100644 index 00000000000..a19e517a1a3 --- /dev/null +++ b/test/built-ins/AsyncDisposableStack/prototype/adopt/throws-if-disposed.js @@ -0,0 +1,23 @@ +// Copyright (C) 2026 Ron Buckton. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-asyncdisposablestack.prototype.adopt +description: Throws a ReferenceError if this is disposed. +info: | + AsyncDisposableStack.prototype.adopt ( value, onDisposeAsync ) + + 1. Let asyncDisposableStack be the this value. + 2. Perform ? RequireInternalSlot(asyncDisposableStack, [[AsyncDisposableState]]). + 3. If asyncDisposableStack.[[AsyncDisposableState]] is disposed, throw a ReferenceError exception. + ... + +features: [explicit-resource-management] +---*/ + +var stack = new AsyncDisposableStack(); +stack.disposeAsync(); + +assert.throws(ReferenceError, function() { + stack.adopt(null, async _ => {}); +}); diff --git a/test/built-ins/AsyncDisposableStack/prototype/adopt/throws-if-onDisposeAsync-not-callable.js b/test/built-ins/AsyncDisposableStack/prototype/adopt/throws-if-onDisposeAsync-not-callable.js new file mode 100644 index 00000000000..51a4b7418e0 --- /dev/null +++ b/test/built-ins/AsyncDisposableStack/prototype/adopt/throws-if-onDisposeAsync-not-callable.js @@ -0,0 +1,49 @@ +// Copyright (C) 2023 Ron Buckton. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-asyncdisposablestack.prototype.adopt +description: Throws if onDisposeAsync argument not callable +info: | + AsyncDisposableStack.prototype.adopt ( value, onDisposeAsync ) + + ... + 4. If IsCallable(onDisposeAsync) is false, throw a TypeError exception. + ... + +features: [explicit-resource-management] +---*/ + +var stack = new AsyncDisposableStack(); +assert.throws(TypeError, function() { + stack.adopt(null, null); +}, 'null'); + +assert.throws(TypeError, function() { + stack.adopt(null, undefined); +}, 'undefined'); + +assert.throws(TypeError, function() { + stack.adopt(null, true); +}, 'true'); + +assert.throws(TypeError, function() { + stack.adopt(null, false); +}, 'false'); + +assert.throws(TypeError, function() { + stack.adopt(null, 1); +}, 'number'); + +assert.throws(TypeError, function() { + stack.adopt(null, 'object'); +}, 'string'); + +assert.throws(TypeError, function() { + stack.adopt(null, {}); +}, 'object'); + +var s = Symbol(); +assert.throws(TypeError, function() { + stack.adopt(null, s); +}, 'symbol'); diff --git a/test/built-ins/AsyncDisposableStack/prototype/constructor.js b/test/built-ins/AsyncDisposableStack/prototype/constructor.js new file mode 100644 index 00000000000..8194fc4b60f --- /dev/null +++ b/test/built-ins/AsyncDisposableStack/prototype/constructor.js @@ -0,0 +1,20 @@ +// Copyright (C) 2023 Ron Buckton. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-properties-of-the-asyncdisposablestack-prototype-object +description: AsyncDisposableStack.prototype.constructor +info: | + The initial value of AsyncDisposableStack.prototype.constructor is the intrinsic object %AsyncDisposableStack%. + + 17 ECMAScript Standard Built-in Objects +includes: [propertyHelper.js] +features: [explicit-resource-management] +---*/ + +verifyProperty(AsyncDisposableStack.prototype, 'constructor', { + value: AsyncDisposableStack, + writable: true, + enumerable: false, + configurable: true +}); diff --git a/test/built-ins/AsyncDisposableStack/prototype/defer/adds-onDisposeAsync.js b/test/built-ins/AsyncDisposableStack/prototype/defer/adds-onDisposeAsync.js new file mode 100644 index 00000000000..a40729fe8b5 --- /dev/null +++ b/test/built-ins/AsyncDisposableStack/prototype/defer/adds-onDisposeAsync.js @@ -0,0 +1,38 @@ +// Copyright (C) 2023 Ron Buckton. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-asyncdisposablestack.prototype.defer +description: Adds an onDisposeAsync callback to the stack +info: | + AsyncDisposableStack.prototype.defer ( onDisposeAsync ) + + 1. Let asyncDisposableStack be the this value. + 2. Perform ? RequireInternalSlot(asyncDisposableStack, [[AsyncDisposableState]]). + 3. If asyncDisposableStack.[[AsyncDisposableState]] is disposed, throw a ReferenceError exception. + 4. If IsCallable(onDisposeAsync) is false, throw a TypeError exception. + 5. Perform ? AddDisposableResource(asyncDisposableStack.[[DisposeCapability]], undefined, async-dispose, onDisposeAsync). + ... + + AddDisposableResource ( disposeCapability, V, hint [, method ] ) + + 1. If method is not present then, + ... + 2. Else, + a. Assert: V is undefined. + b. Let resource be ? CreateDisposableResource(undefined, hint, method). + 3. Append resource to disposeCapability.[[DisposableResourceStack]]. + 4. Return unused. + +flags: [async] +includes: [asyncHelpers.js] +features: [explicit-resource-management] +---*/ + +asyncTest(async function () { + var stack = new AsyncDisposableStack(); + var disposed = false; + stack.defer(async () => { disposed = true }); + await stack.disposeAsync(); + assert.sameValue(disposed, true, 'Expected callback to have been called'); +}); diff --git a/test/built-ins/AsyncDisposableStack/prototype/defer/puts-onDisposeAsync-on-top-of-stack.js b/test/built-ins/AsyncDisposableStack/prototype/defer/puts-onDisposeAsync-on-top-of-stack.js new file mode 100644 index 00000000000..10c096b4396 --- /dev/null +++ b/test/built-ins/AsyncDisposableStack/prototype/defer/puts-onDisposeAsync-on-top-of-stack.js @@ -0,0 +1,43 @@ +// Copyright (C) 2023 Ron Buckton. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-asyncdisposablestack.prototype.defer +description: Adds a disposable resource to the stack +info: | + AsyncDisposableStack.prototype.defer ( onDisposeAsync ) + + 1. Let asyncDisposableStack be the this value. + 2. Perform ? RequireInternalSlot(asyncDisposableStack, [[AsyncDisposableState]]). + 3. If asyncDisposableStack.[[AsyncDisposableState]] is disposed, throw a ReferenceError exception. + 4. If IsCallable(onDisposeAsync) is false, throw a TypeError exception. + 5. Perform ? AddDisposableResource(asyncDisposableStack.[[DisposeCapability]], undefined, async-dispose, onDisposeAsync). + ... + + AddDisposableResource ( disposeCapability, V, hint [, method ] ) + + 1. If method is not present then, + ... + 2. Else, + a. Assert: V is undefined. + b. Let resource be ? CreateDisposableResource(undefined, hint, method). + 3. Append resource to disposeCapability.[[DisposableResourceStack]]. + 4. Return unused. + +flags: [async] +includes: [asyncHelpers.js] +features: [explicit-resource-management] +---*/ + +asyncTest(async function () { + var stack = new AsyncDisposableStack(); + var disposed = []; + async function dispose1() { disposed.push(dispose1); } + function dispose2() { disposed.push(dispose2); } + stack.defer(dispose1); + stack.defer(dispose2); + await stack.disposeAsync(); + assert.sameValue(2, disposed.length); + assert.sameValue(disposed[0], dispose2, 'Expected dispose2 to be the first onDisposeAsync invoked'); + assert.sameValue(disposed[1], dispose1, 'Expected dispose1 to be the second onDisposeAsync invoked'); +}); diff --git a/test/built-ins/AsyncDisposableStack/prototype/defer/returns-undefined.js b/test/built-ins/AsyncDisposableStack/prototype/defer/returns-undefined.js new file mode 100644 index 00000000000..f99873f8387 --- /dev/null +++ b/test/built-ins/AsyncDisposableStack/prototype/defer/returns-undefined.js @@ -0,0 +1,17 @@ +// Copyright (C) 2023 Ron Buckton. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-asyncdisposablestack.prototype.defer +description: Returns undefined. +info: | + AsyncDisposableStack.prototype.defer ( onDisposeAsync ) + + ... + 6. Return undefined. + +features: [explicit-resource-management] +---*/ + +var stack = new AsyncDisposableStack(); +assert.sameValue(stack.defer(_ => {}), undefined); diff --git a/test/built-ins/AsyncDisposableStack/prototype/defer/throws-if-disposed.js b/test/built-ins/AsyncDisposableStack/prototype/defer/throws-if-disposed.js new file mode 100644 index 00000000000..840e5855155 --- /dev/null +++ b/test/built-ins/AsyncDisposableStack/prototype/defer/throws-if-disposed.js @@ -0,0 +1,23 @@ +// Copyright (C) 2026 Ron Buckton. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-asyncdisposablestack.prototype.defer +description: Throws a ReferenceError if this is disposed. +info: | + AsyncDisposableStack.prototype.defer ( onDisposeAsync ) + + 1. Let asyncDisposableStack be the this value. + 2. Perform ? RequireInternalSlot(asyncDisposableStack, [[AsyncDisposableState]]). + 3. If asyncDisposableStack.[[AsyncDisposableState]] is disposed, throw a ReferenceError exception. + ... + +features: [explicit-resource-management] +---*/ + +var stack = new AsyncDisposableStack(); +stack.disposeAsync(); + +assert.throws(ReferenceError, function() { + stack.defer(async _ => {}); +}); diff --git a/test/built-ins/AsyncDisposableStack/prototype/defer/throws-if-onDisposeAsync-not-callable.js b/test/built-ins/AsyncDisposableStack/prototype/defer/throws-if-onDisposeAsync-not-callable.js new file mode 100644 index 00000000000..5286b36c3ac --- /dev/null +++ b/test/built-ins/AsyncDisposableStack/prototype/defer/throws-if-onDisposeAsync-not-callable.js @@ -0,0 +1,49 @@ +// Copyright (C) 2023 Ron Buckton. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-asyncdisposablestack.prototype.defer +description: Adds a callback to the stack +info: | + AsyncDisposableStack.prototype.defer ( onDisposeAsync ) + + ... + 4. If IsCallable(onDisposeAsync) is false, throw a TypeError exception. + ... + +features: [explicit-resource-management] +---*/ + +var stack = new AsyncDisposableStack(); +assert.throws(TypeError, function() { + stack.defer(null); +}, 'null'); + +assert.throws(TypeError, function() { + stack.defer(undefined); +}, 'undefined'); + +assert.throws(TypeError, function() { + stack.defer(true); +}, 'true'); + +assert.throws(TypeError, function() { + stack.defer(false); +}, 'false'); + +assert.throws(TypeError, function() { + stack.defer(1); +}, 'number'); + +assert.throws(TypeError, function() { + stack.defer('object'); +}, 'string'); + +assert.throws(TypeError, function() { + stack.defer({}); +}, 'object'); + +var s = Symbol(); +assert.throws(TypeError, function() { + stack.defer(s); +}, 'symbol'); diff --git a/test/built-ins/AsyncDisposableStack/prototype/disposeAsync/Symbol.asyncDispose-method-not-async.js b/test/built-ins/AsyncDisposableStack/prototype/disposeAsync/Symbol.asyncDispose-method-not-async.js new file mode 100644 index 00000000000..2b66f4439e5 --- /dev/null +++ b/test/built-ins/AsyncDisposableStack/prototype/disposeAsync/Symbol.asyncDispose-method-not-async.js @@ -0,0 +1,66 @@ +// Copyright (C) 2023 Ron Buckton. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-asyncdisposablestack.prototype.disposeAsync +description: Disposal succeeds even if [Symbol.disposeAsync] does not return a Promise. +info: | + AsyncDisposableStack.prototype.disposeAsync ( ) + + 1. Let asyncDisposableStack be the this value. + 2. Let promiseCapability be ! NewPromiseCapability(%Promise%). + 3. If asyncDisposableStack does not have an [[AsyncDisposableState]] internal slot, then + a. Perform ! Call(promiseCapability.[[Reject]], undefined, « a newly created TypeError object »). + b. Return promiseCapability.[[Promise]]. + 4. If asyncDisposableStack.[[AsyncDisposableState]] is disposed, then + a. Perform ! Call(promiseCapability.[[Resolve]], undefined, « undefined »). + b. Return promiseCapability.[[Promise]]. + 5. Set asyncDisposableStack.[[AsyncDisposableState]] to disposed. + 6. Let result be DisposeResources(asyncDisposableStack.[[DisposeCapability]], NormalCompletion(undefined)). + 7. IfAbruptRejectPromise(result, promiseCapability). + 8. Perform ! Call(promiseCapability.[[Resolve]], undefined, « result »). + 9. Return promiseCapability.[[Promise]]. + + DisposeResources ( disposeCapability, completion ) + + 1. For each resource of disposeCapability.[[DisposableResourceStack]], in reverse list order, do + a. Let result be Dispose(resource.[[ResourceValue]], resource.[[Hint]], resource.[[DisposeMethod]]). + b. If result.[[Type]] is throw, then + i. If completion.[[Type]] is throw, then + 1. Set result to result.[[Value]]. + 2. Let suppressed be completion.[[Value]]. + 3. Let error be a newly created SuppressedError object. + 4. Perform ! CreateNonEnumerableDataPropertyOrThrow(error, "error", result). + 5. Perform ! CreateNonEnumerableDataPropertyOrThrow(error, "suppressed", suppressed). + 6. Set completion to ThrowCompletion(error). + ii. Else, + 1. Set completion to result. + 2. Return completion. + + Dispose ( V, hint, method ) + + 1. If method is undefined, let result be undefined. + 2. Else, let result be ? Call(method, V). + 3. If hint is async-dispose, then + a. Perform ? Await(result). + 4. Return undefined. + +flags: [async] +includes: [asyncHelpers.js] +features: [explicit-resource-management] +---*/ + +asyncTest(async function () { + var resource = { + disposed: false, + [Symbol.asyncDispose]() { + this.disposed = true; + } + }; + + var stack = new AsyncDisposableStack(); + stack.use(resource); + await stack.disposeAsync(); + + assert.sameValue(resource.disposed, true, 'Expected resource to have been disposed'); +}); diff --git a/test/built-ins/AsyncDisposableStack/prototype/disposeAsync/disposes-resources-in-reverse-order.js b/test/built-ins/AsyncDisposableStack/prototype/disposeAsync/disposes-resources-in-reverse-order.js new file mode 100644 index 00000000000..860dac8636f --- /dev/null +++ b/test/built-ins/AsyncDisposableStack/prototype/disposeAsync/disposes-resources-in-reverse-order.js @@ -0,0 +1,72 @@ +// Copyright (C) 2023 Ron Buckton. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-asyncdisposablestack.prototype.disposeAsync +description: Added resources are disposed in reverse order +info: | + AsyncDisposableStack.prototype.disposeAsync ( ) + + 1. Let asyncDisposableStack be the this value. + 2. Let promiseCapability be ! NewPromiseCapability(%Promise%). + 3. If asyncDisposableStack does not have an [[AsyncDisposableState]] internal slot, then + a. Perform ! Call(promiseCapability.[[Reject]], undefined, « a newly created TypeError object »). + b. Return promiseCapability.[[Promise]]. + 4. If asyncDisposableStack.[[AsyncDisposableState]] is disposed, then + a. Perform ! Call(promiseCapability.[[Resolve]], undefined, « undefined »). + b. Return promiseCapability.[[Promise]]. + 5. Set asyncDisposableStack.[[AsyncDisposableState]] to disposed. + 6. Let result be DisposeResources(asyncDisposableStack.[[DisposeCapability]], NormalCompletion(undefined)). + 7. IfAbruptRejectPromise(result, promiseCapability). + 8. Perform ! Call(promiseCapability.[[Resolve]], undefined, « result »). + 9. Return promiseCapability.[[Promise]]. + + DisposeResources ( disposeCapability, completion ) + + 1. For each resource of disposeCapability.[[DisposableResourceStack]], in reverse list order, do + a. Let result be Dispose(resource.[[ResourceValue]], resource.[[Hint]], resource.[[DisposeMethod]]). + b. If result.[[Type]] is throw, then + i. If completion.[[Type]] is throw, then + 1. Set result to result.[[Value]]. + 2. Let suppressed be completion.[[Value]]. + 3. Let error be a newly created SuppressedError object. + 4. Perform ! CreateNonEnumerableDataPropertyOrThrow(error, "error", result). + 5. Perform ! CreateNonEnumerableDataPropertyOrThrow(error, "suppressed", suppressed). + 6. Set completion to ThrowCompletion(error). + ii. Else, + 1. Set completion to result. + 2. Return completion. + + Dispose ( V, hint, method ) + + 1. If method is undefined, let result be undefined. + 2. Else, let result be ? Call(method, V). + 3. If hint is async-dispose, then + a. Perform ? Await(result). + 4. Return undefined. + +flags: [async] +includes: [asyncHelpers.js] +features: [explicit-resource-management] +---*/ + +asyncTest(async function () { + var stack = new AsyncDisposableStack(); + var disposed = []; + var resource1 = { async [Symbol.asyncDispose]() { disposed.push(this); } }; + var resource2 = { [Symbol.dispose]() { disposed.push(this); } }; + var resource3 = {}; + async function dispose3(res) { disposed.push(res); } + var resource4 = {}; + function dispose4(res) { disposed.push(res); } + async function dispose5() { disposed.push(dispose5); } + function dispose6() { disposed.push(dispose6); } + stack.use(resource1); + stack.use(resource2); + stack.adopt(resource3, dispose3); + stack.adopt(resource4, dispose4); + stack.defer(dispose5); + stack.defer(dispose6); + await stack.disposeAsync(); + assert.compareArray(disposed, [dispose6, dispose5, resource4, resource3, resource2, resource1]) +}); diff --git a/test/built-ins/AsyncDisposableStack/prototype/disposeAsync/does-not-reinvoke-disposers-if-already-disposed.js b/test/built-ins/AsyncDisposableStack/prototype/disposeAsync/does-not-reinvoke-disposers-if-already-disposed.js new file mode 100644 index 00000000000..e9bca1e05d7 --- /dev/null +++ b/test/built-ins/AsyncDisposableStack/prototype/disposeAsync/does-not-reinvoke-disposers-if-already-disposed.js @@ -0,0 +1,61 @@ +// Copyright (C) 2023 Ron Buckton. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-asyncdisposablestack.prototype.disposeAsync +description: Does not re-invoke disposal on resources after stack has already been disposed. +info: | + AsyncDisposableStack.prototype.disposeAsync ( ) + + 1. Let asyncDisposableStack be the this value. + 2. Perform ? RequireInternalSlot(asyncDisposableStack, [[AsyncDisposableState]]). + 3. If asyncDisposableStack.[[AsyncDisposableState]] is disposed, return undefined. + 4. Set asyncDisposableStack.[[AsyncDisposableState]] to disposed. + 5. Return DisposeResources(asyncDisposableStack.[[DisposeCapability]], NormalCompletion(undefined)). + + DisposeResources ( disposeCapability, completion ) + + 1. For each resource of disposeCapability.[[DisposableResourceStack]], in reverse list order, do + a. Let result be Dispose(resource.[[ResourceValue]], resource.[[Hint]], resource.[[DisposeMethod]]). + b. If result.[[Type]] is throw, then + i. If completion.[[Type]] is throw, then + 1. Set result to result.[[Value]]. + 2. Let suppressed be completion.[[Value]]. + 3. Let error be a newly created SuppressedError object. + 4. Perform ! CreateNonEnumerableDataPropertyOrThrow(error, "error", result). + 5. Perform ! CreateNonEnumerableDataPropertyOrThrow(error, "suppressed", suppressed). + 6. Set completion to ThrowCompletion(error). + ii. Else, + 1. Set completion to result. + 2. Return completion. + + Dispose ( V, hint, method ) + + 1. If method is undefined, let result be undefined. + 2. Else, let result be ? Call(method, V). + 3. If hint is async-dispose, then + a. Perform ? Await(result). + 4. Return undefined. + +flags: [async] +includes: [asyncHelpers.js] +features: [explicit-resource-management] +---*/ + +asyncTest(async function () { + var stack = new AsyncDisposableStack(); + var useCount = 0; + var adoptCount = 0; + var deferCount = 0; + stack.use({ async [Symbol.asyncDispose]() { useCount++; } }); + stack.adopt({}, _ => { adoptCount++; }); + stack.defer(() => { deferCount++; }); + await stack.disposeAsync(); + assert.sameValue(useCount, 1); + assert.sameValue(adoptCount, 1); + assert.sameValue(deferCount, 1); + await stack.disposeAsync(); + assert.sameValue(useCount, 1); + assert.sameValue(adoptCount, 1); + assert.sameValue(deferCount, 1); +}); diff --git a/test/built-ins/AsyncDisposableStack/prototype/disposeAsync/does-not-reinvoke-disposers-if-dispose-already-started.js b/test/built-ins/AsyncDisposableStack/prototype/disposeAsync/does-not-reinvoke-disposers-if-dispose-already-started.js new file mode 100644 index 00000000000..2041fec5cf2 --- /dev/null +++ b/test/built-ins/AsyncDisposableStack/prototype/disposeAsync/does-not-reinvoke-disposers-if-dispose-already-started.js @@ -0,0 +1,59 @@ +// Copyright (C) 2023 Ron Buckton. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-asyncdisposablestack.prototype.disposeAsync +description: Does not re-invoke disposal on resources after stack has already been disposed. +info: | + AsyncDisposableStack.prototype.disposeAsync ( ) + + 1. Let asyncDisposableStack be the this value. + 2. Perform ? RequireInternalSlot(asyncDisposableStack, [[AsyncDisposableState]]). + 3. If asyncDisposableStack.[[AsyncDisposableState]] is disposed, return undefined. + 4. Set asyncDisposableStack.[[AsyncDisposableState]] to disposed. + 5. Return DisposeResources(asyncDisposableStack.[[DisposeCapability]], NormalCompletion(undefined)). + + DisposeResources ( disposeCapability, completion ) + + 1. For each resource of disposeCapability.[[DisposableResourceStack]], in reverse list order, do + a. Let result be Dispose(resource.[[ResourceValue]], resource.[[Hint]], resource.[[DisposeMethod]]). + b. If result.[[Type]] is throw, then + i. If completion.[[Type]] is throw, then + 1. Set result to result.[[Value]]. + 2. Let suppressed be completion.[[Value]]. + 3. Let error be a newly created SuppressedError object. + 4. Perform ! CreateNonEnumerableDataPropertyOrThrow(error, "error", result). + 5. Perform ! CreateNonEnumerableDataPropertyOrThrow(error, "suppressed", suppressed). + 6. Set completion to ThrowCompletion(error). + ii. Else, + 1. Set completion to result. + 2. Return completion. + + Dispose ( V, hint, method ) + + 1. If method is undefined, let result be undefined. + 2. Else, let result be ? Call(method, V). + 3. If hint is async-dispose, then + a. Perform ? Await(result). + 4. Return undefined. + +flags: [async] +includes: [asyncHelpers.js] +features: [explicit-resource-management] +---*/ + +asyncTest(async function () { + var stack = new AsyncDisposableStack(); + var useCount = 0; + var adoptCount = 0; + var deferCount = 0; + stack.use({ async [Symbol.asyncDispose]() { useCount++; } }); + stack.adopt({}, _ => { adoptCount++; }); + stack.defer(() => { deferCount++; }); + var p1 = stack.disposeAsync(); + var p2 = stack.disposeAsync(); + await Promise.all([p1, p2]); + assert.sameValue(useCount, 1); + assert.sameValue(adoptCount, 1); + assert.sameValue(deferCount, 1); +}); diff --git a/test/built-ins/AsyncDisposableStack/prototype/disposeAsync/does-not-reject-if-already-disposed.js b/test/built-ins/AsyncDisposableStack/prototype/disposeAsync/does-not-reject-if-already-disposed.js new file mode 100644 index 00000000000..352ecbf48d7 --- /dev/null +++ b/test/built-ins/AsyncDisposableStack/prototype/disposeAsync/does-not-reject-if-already-disposed.js @@ -0,0 +1,54 @@ +// Copyright (C) 2023 Ron Buckton. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-asyncdisposablestack.prototype.disposeAsync +description: Does not throw if already disposed. +info: | + AsyncDisposableStack.prototype.disposeAsync ( ) + + 1. Let asyncDisposableStack be the this value. + 2. Perform ? RequireInternalSlot(asyncDisposableStack, [[AsyncDisposableState]]). + 3. If asyncDisposableStack.[[AsyncDisposableState]] is disposed, return undefined. + 4. Set asyncDisposableStack.[[AsyncDisposableState]] to disposed. + 5. Return DisposeResources(asyncDisposableStack.[[DisposeCapability]], NormalCompletion(undefined)). + + DisposeResources ( disposeCapability, completion ) + + 1. For each resource of disposeCapability.[[DisposableResourceStack]], in reverse list order, do + a. Let result be Dispose(resource.[[ResourceValue]], resource.[[Hint]], resource.[[DisposeMethod]]). + b. If result.[[Type]] is throw, then + i. If completion.[[Type]] is throw, then + 1. Set result to result.[[Value]]. + 2. Let suppressed be completion.[[Value]]. + 3. Let error be a newly created SuppressedError object. + 4. Perform ! CreateNonEnumerableDataPropertyOrThrow(error, "error", result). + 5. Perform ! CreateNonEnumerableDataPropertyOrThrow(error, "suppressed", suppressed). + 6. Set completion to ThrowCompletion(error). + ii. Else, + 1. Set completion to result. + 2. Return completion. + + Dispose ( V, hint, method ) + + 1. If method is undefined, let result be undefined. + 2. Else, let result be ? Call(method, V). + 3. If hint is async-dispose, then + a. Perform ? Await(result). + 4. Return undefined. + +flags: [async] +includes: [asyncHelpers.js] +features: [explicit-resource-management] +---*/ + +asyncTest(async function () { + var stack = new AsyncDisposableStack(); + var p = stack.disposeAsync(); + var r1 = await stack.disposeAsync(); + var r2 = await p; + var r3 = await stack.disposeAsync(); + assert.sameValue(r1, undefined); + assert.sameValue(r2, undefined); + assert.sameValue(r3, undefined); +}); diff --git a/test/built-ins/AsyncDisposableStack/prototype/disposeAsync/explicit-await-for-null.js b/test/built-ins/AsyncDisposableStack/prototype/disposeAsync/explicit-await-for-null.js new file mode 100644 index 00000000000..0265c4b76e8 --- /dev/null +++ b/test/built-ins/AsyncDisposableStack/prototype/disposeAsync/explicit-await-for-null.js @@ -0,0 +1,48 @@ +// Copyright (C) 2026 Mike Pennisi. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-asyncdisposablestack.prototype.disposeAsync +description: Awaits even when only null values are disposed. +info: | + AsyncDisposableStack.prototype.disposeAsync ( ) + + [...] + 6. Let result be DisposeResources(asyncDisposableStack.[[DisposeCapability]], NormalCompletion(undefined)). + [...] + + DisposeResources ( disposeCapability, completion ) + + 1. Let needsAwait be false. + 2. Let hasAwaited be false. + 3. For each element resource of disposeCapability.[[DisposableResourceStack]], in reverse list order, do + [...] + f. Else, + i. Assert: hint is async-dispose. + ii. Set needsAwait to true. + iii. NOTE: This can only indicate a case where either null or undefined was the initialized value of an await using declaration. + 4. If needsAwait is true and hasAwaited is false, then + a. Perform ! Await(undefined). + +flags: [async] +includes: [asyncHelpers.js] +features: [explicit-resource-management] +---*/ + +asyncTest(async function () { + var stack = new AsyncDisposableStack(); + var sequence = []; + + stack.use(null); + + await Promise.all([ + Promise.resolve() + .then(() => 0) + .then(() => { sequence.push('job 1'); }), + stack.disposeAsync().then(() => { sequence.push('dispose'); }), + Promise.resolve() + .then(() => 0) + .then(() => { sequence.push('job 2'); }) + ]); + assert.compareArray(sequence, ['job 1', 'dispose', 'job 2']); +}); diff --git a/test/built-ins/AsyncDisposableStack/prototype/disposeAsync/explicit-await-for-undefined.js b/test/built-ins/AsyncDisposableStack/prototype/disposeAsync/explicit-await-for-undefined.js new file mode 100644 index 00000000000..a65b37dca98 --- /dev/null +++ b/test/built-ins/AsyncDisposableStack/prototype/disposeAsync/explicit-await-for-undefined.js @@ -0,0 +1,48 @@ +// Copyright (C) 2026 Mike Pennisi. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-asyncdisposablestack.prototype.disposeAsync +description: Awaits even when only undefined values are disposed. +info: | + AsyncDisposableStack.prototype.disposeAsync ( ) + + [...] + 6. Let result be DisposeResources(asyncDisposableStack.[[DisposeCapability]], NormalCompletion(undefined)). + [...] + + DisposeResources ( disposeCapability, completion ) + + 1. Let needsAwait be false. + 2. Let hasAwaited be false. + 3. For each element resource of disposeCapability.[[DisposableResourceStack]], in reverse list order, do + [...] + f. Else, + i. Assert: hint is async-dispose. + ii. Set needsAwait to true. + iii. NOTE: This can only indicate a case where either null or undefined was the initialized value of an await using declaration. + 4. If needsAwait is true and hasAwaited is false, then + a. Perform ! Await(undefined). + +flags: [async] +includes: [asyncHelpers.js] +features: [explicit-resource-management] +---*/ + +asyncTest(async function () { + var stack = new AsyncDisposableStack(); + var sequence = []; + + stack.use(undefined); + + await Promise.all([ + Promise.resolve() + .then(() => 0) + .then(() => { sequence.push('job 1'); }), + stack.disposeAsync().then(() => { sequence.push('dispose'); }), + Promise.resolve() + .then(() => 0) + .then(() => { sequence.push('job 2'); }) + ]); + assert.compareArray(sequence, ['job 1', 'dispose', 'job 2']); +}); diff --git a/test/built-ins/AsyncDisposableStack/prototype/disposeAsync/explicit-await-skipped-when-empty.js b/test/built-ins/AsyncDisposableStack/prototype/disposeAsync/explicit-await-skipped-when-empty.js new file mode 100644 index 00000000000..481dc791863 --- /dev/null +++ b/test/built-ins/AsyncDisposableStack/prototype/disposeAsync/explicit-await-skipped-when-empty.js @@ -0,0 +1,40 @@ +// Copyright (C) 2026 Mike Pennisi. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-asyncdisposablestack.prototype.disposeAsync +description: Does not await when the stack is empty. +info: | + AsyncDisposableStack.prototype.disposeAsync ( ) + + [...] + 6. Let result be DisposeResources(asyncDisposableStack.[[DisposeCapability]], NormalCompletion(undefined)). + [...] + + DisposeResources ( disposeCapability, completion ) + + 1. Let needsAwait be false. + 2. Let hasAwaited be false. + 3. For each element resource of disposeCapability.[[DisposableResourceStack]], in reverse list order, do + [...] + 4. If needsAwait is true and hasAwaited is false, then + a. Perform ! Await(undefined). + +flags: [async] +includes: [asyncHelpers.js] +features: [explicit-resource-management] +---*/ + +asyncTest(async function () { + var stack = new AsyncDisposableStack(); + var sequence = []; + + await Promise.all([ + Promise.resolve() + .then(() => { sequence.push('job 1'); }), + stack.disposeAsync().then(() => { sequence.push('dispose'); }), + Promise.resolve() + .then(() => { sequence.push('job 2'); }) + ]); + assert.compareArray(sequence, ['job 1', 'dispose', 'job 2']); +}); diff --git a/test/built-ins/AsyncDisposableStack/prototype/disposeAsync/rejects-with-error-as-is-if-only-one-error-during-disposal.js b/test/built-ins/AsyncDisposableStack/prototype/disposeAsync/rejects-with-error-as-is-if-only-one-error-during-disposal.js new file mode 100644 index 00000000000..3238d5f7efe --- /dev/null +++ b/test/built-ins/AsyncDisposableStack/prototype/disposeAsync/rejects-with-error-as-is-if-only-one-error-during-disposal.js @@ -0,0 +1,53 @@ +// Copyright (C) 2023 Ron Buckton. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-asyncdisposablestack.prototype.disposeAsync +description: Rethrows an error from a disposal as-is if it is the only error. +info: | + AsyncDisposableStack.prototype.disposeAsync ( ) + + 1. Let asyncDisposableStack be the this value. + 2. Perform ? RequireInternalSlot(asyncDisposableStack, [[AsyncDisposableState]]). + 3. If asyncDisposableStack.[[AsyncDisposableState]] is disposed, return undefined. + 4. Set asyncDisposableStack.[[AsyncDisposableState]] to disposed. + 5. Return DisposeResources(asyncDisposableStack.[[DisposeCapability]], NormalCompletion(undefined)). + + DisposeResources ( disposeCapability, completion ) + + 1. For each resource of disposeCapability.[[DisposableResourceStack]], in reverse list order, do + a. Let result be Dispose(resource.[[ResourceValue]], resource.[[Hint]], resource.[[DisposeMethod]]). + b. If result.[[Type]] is throw, then + i. If completion.[[Type]] is throw, then + 1. Set result to result.[[Value]]. + 2. Let suppressed be completion.[[Value]]. + 3. Let error be a newly created SuppressedError object. + 4. Perform ! CreateNonEnumerableDataPropertyOrThrow(error, "error", result). + 5. Perform ! CreateNonEnumerableDataPropertyOrThrow(error, "suppressed", suppressed). + 6. Set completion to ThrowCompletion(error). + ii. Else, + 1. Set completion to result. + 2. Return completion. + + Dispose ( V, hint, method ) + + 1. If method is undefined, let result be undefined. + 2. Else, let result be ? Call(method, V). + 3. If hint is async-dispose, then + a. Perform ? Await(result). + 4. Return undefined. + +flags: [async] +includes: [asyncHelpers.js] +features: [explicit-resource-management] +---*/ + +asyncTest(async function () { + class MyError extends Error {} + var stack = new AsyncDisposableStack(); + stack.defer(async function () { throw new MyError(); }); + stack.defer(function () {}); + await assert.throwsAsync(MyError, async function () { + await stack.disposeAsync(); + }); +}); diff --git a/test/built-ins/AsyncDisposableStack/prototype/disposeAsync/rejects-with-suppressederror-if-multiple-errors-during-disposal.js b/test/built-ins/AsyncDisposableStack/prototype/disposeAsync/rejects-with-suppressederror-if-multiple-errors-during-disposal.js new file mode 100644 index 00000000000..257c423af23 --- /dev/null +++ b/test/built-ins/AsyncDisposableStack/prototype/disposeAsync/rejects-with-suppressederror-if-multiple-errors-during-disposal.js @@ -0,0 +1,66 @@ +// Copyright (C) 2023 Ron Buckton. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-asyncdisposablestack.prototype.disposeAsync +description: Throws multiple errors from a disposal nested in one or more SuppressedError instances. +info: | + AsyncDisposableStack.prototype.disposeAsync ( ) + + 1. Let asyncDisposableStack be the this value. + 2. Perform ? RequireInternalSlot(asyncDisposableStack, [[AsyncDisposableState]]). + 3. If asyncDisposableStack.[[AsyncDisposableState]] is disposed, return undefined. + 4. Set asyncDisposableStack.[[AsyncDisposableState]] to disposed. + 5. Return DisposeResources(asyncDisposableStack.[[DisposeCapability]], NormalCompletion(undefined)). + + DisposeResources ( disposeCapability, completion ) + + 1. For each resource of disposeCapability.[[DisposableResourceStack]], in reverse list order, do + a. Let result be Dispose(resource.[[ResourceValue]], resource.[[Hint]], resource.[[DisposeMethod]]). + b. If result.[[Type]] is throw, then + i. If completion.[[Type]] is throw, then + 1. Set result to result.[[Value]]. + 2. Let suppressed be completion.[[Value]]. + 3. Let error be a newly created SuppressedError object. + 4. Perform ! CreateNonEnumerableDataPropertyOrThrow(error, "error", result). + 5. Perform ! CreateNonEnumerableDataPropertyOrThrow(error, "suppressed", suppressed). + 6. Set completion to ThrowCompletion(error). + ii. Else, + 1. Set completion to result. + 2. Return completion. + + Dispose ( V, hint, method ) + + 1. If method is undefined, let result be undefined. + 2. Else, let result be ? Call(method, V). + 3. If hint is async-dispose, then + a. Perform ? Await(result). + 4. Return undefined. + +flags: [async] +includes: [asyncHelpers.js] +features: [explicit-resource-management] +---*/ + +asyncTest(async function () { + class MyError extends Error {} + var error1 = new MyError(); + var error2 = new MyError(); + var error3 = new MyError(); + var stack = new AsyncDisposableStack(); + stack.defer(function () { throw error1; }); + stack.defer(function () { throw error2; }); + stack.defer(function () { throw error3; }); + var promise = stack.disposeAsync(); + try { + await promise; + assert(false, 'Expected await stack.disposeAsync() to have thrown an error.'); + } + catch (e) { + assert.sameValue(Object.getPrototypeOf(e), SuppressedError.prototype, "Expected await stack.disposeAsync() to have thrown a SuppressedError"); + assert.sameValue(e.error, error1, "Expected the outermost suppressing error to have been 'error1'"); + assert.sameValue(Object.getPrototypeOf(e.suppressed), SuppressedError.prototype, "Expected the outermost suppressed error to have been a SuppressedError"); + assert.sameValue(e.suppressed.error, error2, "Expected the innermost suppressing error to have been 'error2'"); + assert.sameValue(e.suppressed.suppressed, error3, "Expected the innermost suppressed error to have been 'error3'"); + } +}); diff --git a/test/built-ins/AsyncDisposableStack/prototype/disposeAsync/resolves-to-undefined.js b/test/built-ins/AsyncDisposableStack/prototype/disposeAsync/resolves-to-undefined.js new file mode 100644 index 00000000000..096a095a5db --- /dev/null +++ b/test/built-ins/AsyncDisposableStack/prototype/disposeAsync/resolves-to-undefined.js @@ -0,0 +1,53 @@ +// Copyright (C) 2023 Ron Buckton. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-asyncdisposablestack.prototype.disposeAsync +description: Return value of dispose is undefined +info: | + AsyncDisposableStack.prototype.disposeAsync ( ) + + 1. Let asyncDisposableStack be the this value. + 2. Perform ? RequireInternalSlot(asyncDisposableStack, [[AsyncDisposableState]]). + 3. If asyncDisposableStack.[[AsyncDisposableState]] is disposed, return undefined. + 4. Set asyncDisposableStack.[[AsyncDisposableState]] to disposed. + 5. Return DisposeResources(asyncDisposableStack.[[DisposeCapability]], NormalCompletion(undefined)). + + DisposeResources ( disposeCapability, completion ) + + 1. For each resource of disposeCapability.[[DisposableResourceStack]], in reverse list order, do + a. Let result be Dispose(resource.[[ResourceValue]], resource.[[Hint]], resource.[[DisposeMethod]]). + b. If result.[[Type]] is throw, then + i. If completion.[[Type]] is throw, then + 1. Set result to result.[[Value]]. + 2. Let suppressed be completion.[[Value]]. + 3. Let error be a newly created SuppressedError object. + 4. Perform ! CreateNonEnumerableDataPropertyOrThrow(error, "error", result). + 5. Perform ! CreateNonEnumerableDataPropertyOrThrow(error, "suppressed", suppressed). + 6. Set completion to ThrowCompletion(error). + ii. Else, + 1. Set completion to result. + 2. Return completion. + + Dispose ( V, hint, method ) + + 1. If method is undefined, let result be undefined. + 2. Else, let result be ? Call(method, V). + 3. If hint is async-dispose, then + a. Perform ? Await(result). + 4. Return undefined. + +flags: [async] +includes: [asyncHelpers.js] +features: [explicit-resource-management] +---*/ + +asyncTest(async function () { + var stack1 = new AsyncDisposableStack(); + assert.sameValue(await stack1.disposeAsync(), undefined); + + var stack2 = new AsyncDisposableStack(); + stack2.defer(async () => 1); + stack2.defer(() => 2); + assert.sameValue(await stack2.disposeAsync(), undefined); +}); diff --git a/test/built-ins/AsyncDisposableStack/prototype/disposeAsync/returns-promise.js b/test/built-ins/AsyncDisposableStack/prototype/disposeAsync/returns-promise.js new file mode 100644 index 00000000000..dcbc0477c83 --- /dev/null +++ b/test/built-ins/AsyncDisposableStack/prototype/disposeAsync/returns-promise.js @@ -0,0 +1,44 @@ +// Copyright (C) 2023 Ron Buckton. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-asyncdisposablestack.prototype.disposeAsync +description: Return value of dispose is undefined +info: | + AsyncDisposableStack.prototype.disposeAsync ( ) + + 1. Let asyncDisposableStack be the this value. + 2. Perform ? RequireInternalSlot(asyncDisposableStack, [[AsyncDisposableState]]). + 3. If asyncDisposableStack.[[AsyncDisposableState]] is disposed, return undefined. + 4. Set asyncDisposableStack.[[AsyncDisposableState]] to disposed. + 5. Return DisposeResources(asyncDisposableStack.[[DisposeCapability]], NormalCompletion(undefined)). + + DisposeResources ( disposeCapability, completion ) + + 1. For each resource of disposeCapability.[[DisposableResourceStack]], in reverse list order, do + a. Let result be Dispose(resource.[[ResourceValue]], resource.[[Hint]], resource.[[DisposeMethod]]). + b. If result.[[Type]] is throw, then + i. If completion.[[Type]] is throw, then + 1. Set result to result.[[Value]]. + 2. Let suppressed be completion.[[Value]]. + 3. Let error be a newly created SuppressedError object. + 4. Perform ! CreateNonEnumerableDataPropertyOrThrow(error, "error", result). + 5. Perform ! CreateNonEnumerableDataPropertyOrThrow(error, "suppressed", suppressed). + 6. Set completion to ThrowCompletion(error). + ii. Else, + 1. Set completion to result. + 2. Return completion. + + Dispose ( V, hint, method ) + + 1. If method is undefined, let result be undefined. + 2. Else, let result be ? Call(method, V). + 3. If hint is async-dispose, then + a. Perform ? Await(result). + 4. Return undefined. + +features: [explicit-resource-management] +---*/ + +var stack = new AsyncDisposableStack(); +assert.sameValue(Object.getPrototypeOf(stack.disposeAsync()), Promise.prototype); diff --git a/test/built-ins/AsyncDisposableStack/prototype/disposeAsync/sets-state-to-disposed.js b/test/built-ins/AsyncDisposableStack/prototype/disposeAsync/sets-state-to-disposed.js new file mode 100644 index 00000000000..09fadd22b65 --- /dev/null +++ b/test/built-ins/AsyncDisposableStack/prototype/disposeAsync/sets-state-to-disposed.js @@ -0,0 +1,55 @@ +// Copyright (C) 2023 Ron Buckton. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-asyncdisposablestack.prototype.disposeAsync +description: Sets the [[AsyncDisposableState]] internal slot to disposed +info: | + AsyncDisposableStack.prototype.disposeAsync ( ) + + 1. Let asyncDisposableStack be the this value. + 2. Perform ? RequireInternalSlot(asyncDisposableStack, [[AsyncDisposableState]]). + 3. If asyncDisposableStack.[[AsyncDisposableState]] is disposed, return undefined. + 4. Set asyncDisposableStack.[[AsyncDisposableState]] to disposed. + 5. Return DisposeResources(asyncDisposableStack.[[DisposeCapability]], NormalCompletion(undefined)). + + DisposeResources ( disposeCapability, completion ) + + 1. For each resource of disposeCapability.[[DisposableResourceStack]], in reverse list order, do + a. Let result be Dispose(resource.[[ResourceValue]], resource.[[Hint]], resource.[[DisposeMethod]]). + b. If result.[[Type]] is throw, then + i. If completion.[[Type]] is throw, then + 1. Set result to result.[[Value]]. + 2. Let suppressed be completion.[[Value]]. + 3. Let error be a newly created SuppressedError object. + 4. Perform ! CreateNonEnumerableDataPropertyOrThrow(error, "error", result). + 5. Perform ! CreateNonEnumerableDataPropertyOrThrow(error, "suppressed", suppressed). + 6. Set completion to ThrowCompletion(error). + ii. Else, + 1. Set completion to result. + 2. Return completion. + + Dispose ( V, hint, method ) + + 1. If method is undefined, let result be undefined. + 2. Else, let result be ? Call(method, V). + 3. If hint is async-dispose, then + a. Perform ? Await(result). + 4. Return undefined. + +flags: [async] +includes: [asyncHelpers.js] +features: [explicit-resource-management] +---*/ + +asyncTest(async function () { + var stack = new AsyncDisposableStack(); + var wasDisposed = stack.disposed; + var p = stack.disposeAsync(); + var wasDisposedBeforeAwait = stack.disposed; + await p; + var isDisposedAfterAwait = stack.disposed; + assert.sameValue(wasDisposed, false); + assert.sameValue(wasDisposedBeforeAwait, true); + assert.sameValue(isDisposedAfterAwait, true); +}); diff --git a/test/built-ins/AsyncDisposableStack/prototype/disposed/returns-false-when-not-disposed.js b/test/built-ins/AsyncDisposableStack/prototype/disposed/returns-false-when-not-disposed.js new file mode 100644 index 00000000000..37341afa529 --- /dev/null +++ b/test/built-ins/AsyncDisposableStack/prototype/disposed/returns-false-when-not-disposed.js @@ -0,0 +1,19 @@ +// Copyright (C) 2023 Ron Buckton. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. +/*--- +esid: sec-get-asyncdisposablestack.prototype.disposed +description: > + Returns `false` when the AsyncDisposableStack has not yet been disposed. +info: | + get AsyncDisposableStack.prototype.disposed + + ... + 3. If asyncDisposableStack.[[AsyncDisposableState]] is disposed, return true. + 4. Otherwise, return false. + +features: [explicit-resource-management] +---*/ + +var stack = new AsyncDisposableStack(); + +assert.sameValue(stack.disposed, false); diff --git a/test/built-ins/AsyncDisposableStack/prototype/disposed/returns-true-when-disposed.js b/test/built-ins/AsyncDisposableStack/prototype/disposed/returns-true-when-disposed.js new file mode 100644 index 00000000000..ae0a6e90824 --- /dev/null +++ b/test/built-ins/AsyncDisposableStack/prototype/disposed/returns-true-when-disposed.js @@ -0,0 +1,19 @@ +// Copyright (C) 2023 Ron Buckton. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. +/*--- +esid: sec-get-asyncdisposablestack.prototype.disposed +description: > + Returns `true` after the AsyncDisposableStack has been disposed. +info: | + get AsyncDisposableStack.prototype.disposed + + ... + 3. If asyncDisposableStack.[[AsyncDisposableState]] is disposed, return true. + 4. Otherwise, return false. + +features: [explicit-resource-management] +---*/ + +var stack = new AsyncDisposableStack(); +stack.disposeAsync(); +assert.sameValue(stack.disposed, true); diff --git a/test/built-ins/AsyncDisposableStack/prototype/move/does-not-dispose-resources.js b/test/built-ins/AsyncDisposableStack/prototype/move/does-not-dispose-resources.js new file mode 100644 index 00000000000..8830d3cf0da --- /dev/null +++ b/test/built-ins/AsyncDisposableStack/prototype/move/does-not-dispose-resources.js @@ -0,0 +1,27 @@ +// Copyright (C) 2023 Ron Buckton. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-asyncdisposablestack.prototype.move +description: Resources in the stack are not disposed when move is called. +info: | + AsyncDisposableStack.prototype.move ( ) + + 1. Let asyncDisposableStack be the this value. + 2. Perform ? RequireInternalSlot(asyncDisposableStack, [[AsyncDisposableState]]). + 3. If asyncDisposableStack.[[AsyncDisposableState]] is disposed, throw a ReferenceError exception. + 4. Let newAsyncDisposableStack be ? OrdinaryCreateFromConstructor(%AsyncDisposableStack%, "%AsyncDisposableStack.prototype%", « [[AsyncDisposableState]], [[DisposeCapability]] »). + 5. Set newAsyncDisposableStack.[[AsyncDisposableState]] to pending. + 6. Set newAsyncDisposableStack.[[DisposeCapability]] to asyncDisposableStack.[[DisposeCapability]]. + 7. Set asyncDisposableStack.[[DisposeCapability]] to NewDisposeCapability(). + 8. Set asyncDisposableStack.[[AsyncDisposableState]] to disposed. + 9. Return newAsyncDisposableStack. + +features: [explicit-resource-management] +---*/ + +var stack = new AsyncDisposableStack(); +var disposed = false; +stack.defer(() => { disposed = true; }); +stack.move(); +assert.sameValue(disposed, false); diff --git a/test/built-ins/AsyncDisposableStack/prototype/move/returns-new-asyncdisposablestack-that-contains-moved-resources.js b/test/built-ins/AsyncDisposableStack/prototype/move/returns-new-asyncdisposablestack-that-contains-moved-resources.js new file mode 100644 index 00000000000..d1c052efc90 --- /dev/null +++ b/test/built-ins/AsyncDisposableStack/prototype/move/returns-new-asyncdisposablestack-that-contains-moved-resources.js @@ -0,0 +1,40 @@ +// Copyright (C) 2023 Ron Buckton. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-asyncdisposablestack.prototype.move +description: Returns a new AsyncDisposableStack that contains the resources originally contained in this stack. +info: | + AsyncDisposableStack.prototype.move ( ) + + 1. Let asyncDisposableStack be the this value. + 2. Perform ? RequireInternalSlot(asyncDisposableStack, [[AsyncDisposableState]]). + 3. If asyncDisposableStack.[[AsyncDisposableState]] is disposed, throw a ReferenceError exception. + 4. Let newAsyncDisposableStack be ? OrdinaryCreateFromConstructor(%AsyncDisposableStack%, "%AsyncDisposableStack.prototype%", « [[AsyncDisposableState]], [[DisposeCapability]] »). + 5. Set newAsyncDisposableStack.[[AsyncDisposableState]] to pending. + 6. Set newAsyncDisposableStack.[[DisposeCapability]] to asyncDisposableStack.[[DisposeCapability]]. + 7. Set asyncDisposableStack.[[DisposeCapability]] to NewDisposeCapability(). + 8. Set asyncDisposableStack.[[AsyncDisposableState]] to disposed. + 9. Return newAsyncDisposableStack. + +flags: [async] +includes: [asyncHelpers.js, deepEqual.js] +features: [explicit-resource-management] +---*/ + +asyncTest(async function () { + + var stack1 = new AsyncDisposableStack(); + var disposed = []; + stack1.defer(async () => { disposed.push(1); }); + stack1.defer(() => { disposed.push(2); }); + + var stack2 = stack1.move(); + + var wasDisposed = disposed.slice(); + await stack2.disposeAsync(); + var isDisposed = disposed.slice(); + + assert.deepEqual(wasDisposed, []); + assert.deepEqual(isDisposed, [2, 1]); +}); diff --git a/test/built-ins/AsyncDisposableStack/prototype/move/returns-new-asyncdisposablestack-that-is-still-pending.js b/test/built-ins/AsyncDisposableStack/prototype/move/returns-new-asyncdisposablestack-that-is-still-pending.js new file mode 100644 index 00000000000..4c475d6999f --- /dev/null +++ b/test/built-ins/AsyncDisposableStack/prototype/move/returns-new-asyncdisposablestack-that-is-still-pending.js @@ -0,0 +1,25 @@ +// Copyright (C) 2023 Ron Buckton. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-asyncdisposablestack.prototype.move +description: Returns a new AsyncDisposableStack that is still pending +info: | + AsyncDisposableStack.prototype.move ( ) + + 1. Let asyncDisposableStack be the this value. + 2. Perform ? RequireInternalSlot(asyncDisposableStack, [[AsyncDisposableState]]). + 3. If asyncDisposableStack.[[AsyncDisposableState]] is disposed, throw a ReferenceError exception. + 4. Let newAsyncDisposableStack be ? OrdinaryCreateFromConstructor(%AsyncDisposableStack%, "%AsyncDisposableStack.prototype%", « [[AsyncDisposableState]], [[DisposeCapability]] »). + 5. Set newAsyncDisposableStack.[[AsyncDisposableState]] to pending. + 6. Set newAsyncDisposableStack.[[DisposeCapability]] to asyncDisposableStack.[[DisposeCapability]]. + 7. Set asyncDisposableStack.[[DisposeCapability]] to NewDisposeCapability(). + 8. Set asyncDisposableStack.[[AsyncDisposableState]] to disposed. + 9. Return newAsyncDisposableStack. + +features: [explicit-resource-management] +---*/ + +var stack1 = new AsyncDisposableStack(); +var stack2 = stack1.move(); +assert.sameValue(stack2.disposed, false); diff --git a/test/built-ins/AsyncDisposableStack/prototype/move/returns-new-asyncdisposablestack.js b/test/built-ins/AsyncDisposableStack/prototype/move/returns-new-asyncdisposablestack.js new file mode 100644 index 00000000000..8ecbd97511d --- /dev/null +++ b/test/built-ins/AsyncDisposableStack/prototype/move/returns-new-asyncdisposablestack.js @@ -0,0 +1,26 @@ +// Copyright (C) 2023 Ron Buckton. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-asyncdisposablestack.prototype.move +description: Returns a new AsyncDisposableStack +info: | + AsyncDisposableStack.prototype.move ( ) + + 1. Let asyncDisposableStack be the this value. + 2. Perform ? RequireInternalSlot(asyncDisposableStack, [[AsyncDisposableState]]). + 3. If asyncDisposableStack.[[AsyncDisposableState]] is disposed, throw a ReferenceError exception. + 4. Let newAsyncDisposableStack be ? OrdinaryCreateFromConstructor(%AsyncDisposableStack%, "%AsyncDisposableStack.prototype%", « [[AsyncDisposableState]], [[DisposeCapability]] »). + 5. Set newAsyncDisposableStack.[[AsyncDisposableState]] to pending. + 6. Set newAsyncDisposableStack.[[DisposeCapability]] to asyncDisposableStack.[[DisposeCapability]]. + 7. Set asyncDisposableStack.[[DisposeCapability]] to NewDisposeCapability(). + 8. Set asyncDisposableStack.[[AsyncDisposableState]] to disposed. + 9. Return newAsyncDisposableStack. + +features: [explicit-resource-management] +---*/ + +var stack1 = new AsyncDisposableStack(); +var stack2 = stack1.move(); +assert(stack2 !== stack1, 'Expected stack2 to not be the same reference as stack1'); +assert(stack2 instanceof AsyncDisposableStack, 'Expected stack2 to be an instance of AsyncDisposableStack'); diff --git a/test/built-ins/AsyncDisposableStack/prototype/move/sets-state-to-disposed.js b/test/built-ins/AsyncDisposableStack/prototype/move/sets-state-to-disposed.js new file mode 100644 index 00000000000..df62bde690d --- /dev/null +++ b/test/built-ins/AsyncDisposableStack/prototype/move/sets-state-to-disposed.js @@ -0,0 +1,28 @@ +// Copyright (C) 2023 Ron Buckton. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-asyncdisposablestack.prototype.move +description: The stack's [[AsyncDisposableState]] internal slot is set to disposed. +info: | + AsyncDisposableStack.prototype.move ( ) + + 1. Let asyncDisposableStack be the this value. + 2. Perform ? RequireInternalSlot(asyncDisposableStack, [[AsyncDisposableState]]). + 3. If asyncDisposableStack.[[AsyncDisposableState]] is disposed, throw a ReferenceError exception. + 4. Let newAsyncDisposableStack be ? OrdinaryCreateFromConstructor(%AsyncDisposableStack%, "%AsyncDisposableStack.prototype%", « [[AsyncDisposableState]], [[DisposeCapability]] »). + 5. Set newAsyncDisposableStack.[[AsyncDisposableState]] to pending. + 6. Set newAsyncDisposableStack.[[DisposeCapability]] to asyncDisposableStack.[[DisposeCapability]]. + 7. Set asyncDisposableStack.[[DisposeCapability]] to NewDisposeCapability(). + 8. Set asyncDisposableStack.[[AsyncDisposableState]] to disposed. + 9. Return newAsyncDisposableStack. + +features: [explicit-resource-management] +---*/ + +var stack = new AsyncDisposableStack(); +var wasDisposed = stack.disposed; +stack.move(); +var isDisposed = stack.disposed; +assert.sameValue(wasDisposed, false); +assert.sameValue(isDisposed, true); diff --git a/test/built-ins/AsyncDisposableStack/prototype/move/still-returns-new-asyncdisposablestack-when-subclassed.js b/test/built-ins/AsyncDisposableStack/prototype/move/still-returns-new-asyncdisposablestack-when-subclassed.js new file mode 100644 index 00000000000..67caad8c500 --- /dev/null +++ b/test/built-ins/AsyncDisposableStack/prototype/move/still-returns-new-asyncdisposablestack-when-subclassed.js @@ -0,0 +1,28 @@ +// Copyright (C) 2023 Ron Buckton. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-asyncdisposablestack.prototype.move +description: Returns a new %AsyncDisposableStack%, even when subclassed +info: | + AsyncDisposableStack.prototype.move ( ) + + 1. Let asyncDisposableStack be the this value. + 2. Perform ? RequireInternalSlot(asyncDisposableStack, [[AsyncDisposableState]]). + 3. If asyncDisposableStack.[[AsyncDisposableState]] is disposed, throw a ReferenceError exception. + 4. Let newAsyncDisposableStack be ? OrdinaryCreateFromConstructor(%AsyncDisposableStack%, "%AsyncDisposableStack.prototype%", « [[AsyncDisposableState]], [[DisposeCapability]] »). + 5. Set newAsyncDisposableStack.[[AsyncDisposableState]] to pending. + 6. Set newAsyncDisposableStack.[[DisposeCapability]] to asyncDisposableStack.[[DisposeCapability]]. + 7. Set asyncDisposableStack.[[DisposeCapability]] to NewDisposeCapability(). + 8. Set asyncDisposableStack.[[AsyncDisposableState]] to disposed. + 9. Return newAsyncDisposableStack. + +features: [explicit-resource-management] +---*/ + +class MyAsyncDisposableStack extends AsyncDisposableStack {} + +var stack1 = new MyAsyncDisposableStack(); +var stack2 = stack1.move(); +assert(stack2 instanceof AsyncDisposableStack, 'Expected stack2 to be an instance of AsyncDisposableStack'); +assert(!(stack2 instanceof MyAsyncDisposableStack), 'Expected stack2 to not be an instance of MyAsyncDisposableStack'); diff --git a/test/built-ins/AsyncDisposableStack/prototype/move/throws-if-disposed.js b/test/built-ins/AsyncDisposableStack/prototype/move/throws-if-disposed.js new file mode 100644 index 00000000000..bd0a8db3796 --- /dev/null +++ b/test/built-ins/AsyncDisposableStack/prototype/move/throws-if-disposed.js @@ -0,0 +1,23 @@ +// Copyright (C) 2023 Ron Buckton. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-asyncdisposablestack.prototype.move +description: Throws a ReferenceError if this is disposed. +info: | + AsyncDisposableStack.prototype.move ( value ) + + 1. Let asyncDisposableStack be the this value. + 2. Perform ? RequireInternalSlot(asyncDisposableStack, [[AsyncDisposableState]]). + 3. If asyncDisposableStack.[[AsyncDisposableState]] is disposed, throw a ReferenceError exception. + ... + +features: [explicit-resource-management] +---*/ + +var stack = new AsyncDisposableStack(); +stack.disposeAsync(); + +assert.throws(ReferenceError, function() { + stack.move(); +}); diff --git a/test/built-ins/AsyncDisposableStack/prototype/use/Symbol.asyncDispose-getter.js b/test/built-ins/AsyncDisposableStack/prototype/use/Symbol.asyncDispose-getter.js new file mode 100644 index 00000000000..58549d7abf9 --- /dev/null +++ b/test/built-ins/AsyncDisposableStack/prototype/use/Symbol.asyncDispose-getter.js @@ -0,0 +1,68 @@ +// Copyright (C) 2023 Ron Buckton. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-asyncdisposablestack.prototype.use +description: Invokes getter for `[Symbol.asyncDispose]`. +info: | + AsyncDisposableStack.prototype.use ( value ) + + 1. Let asyncDisposableStack be the this value. + 2. Perform ? RequireInternalSlot(asyncDisposableStack, [[AsyncDisposableState]]). + 3. If asyncDisposableStack.[[AsyncDisposableState]] is disposed, throw a ReferenceError exception. + 4. Perform ? AddDisposableResource(asyncDisposableStack.[[DisposeCapability]], value, async-dispose). + ... + + AddDisposableResource ( disposeCapability, V, hint [, method ] ) + + 1. If method is not present then, + a. If V is either null or undefined and hint is sync-dispose, then + i. Return unused + b. Let resource be ? CreateDisposableResource(V, hint). + ... + + CreateDisposableResource ( V, hint [ , method ] ) + + 1. If method is not present, then + a. If V is either null or undefined, then + i. Set V to undefined + ii. Set method to undefined + b. Else, + i. If Type(V) is not Object, throw a TypeError exception. + ii. Set method to ? GetDisposeMethod(V, hint). + iii. If method is undefined, throw a TypeError exception. + 2. Else, + a. ... + 3. Return the DisposableResource Record { [[ResourceValue]]: V, [[Hint]]: hint, [[DisposeMethod]]: method }. + + GetDisposeMethod ( V, hint ) + + 1. If hint is async-dispose, then + a. Let method be ? GetMethod(V, @@asyncDispose). + b. If method is undefined, then + i. Set method to ? GetMethod(V, @@dispose). + 2. Else, + a. Let method be ? GetMethod(V, @@dispose). + 3. Return method. + +flags: [async] +includes: [asyncHelpers.js] +features: [explicit-resource-management] +---*/ + +asyncTest(async function () { + var resource = { + disposed: false, + get [Symbol.asyncDispose]() { + return async function () { + this.disposed = true; + }; + } + }; + + var stack = new AsyncDisposableStack(); + stack.use(resource); + await stack.disposeAsync(); + + assert.sameValue(resource.disposed, true, 'Expected resource to have been disposed'); +}); diff --git a/test/built-ins/AsyncDisposableStack/prototype/use/Symbol.dispose-getter.js b/test/built-ins/AsyncDisposableStack/prototype/use/Symbol.dispose-getter.js new file mode 100644 index 00000000000..faefb5f12de --- /dev/null +++ b/test/built-ins/AsyncDisposableStack/prototype/use/Symbol.dispose-getter.js @@ -0,0 +1,68 @@ +// Copyright (C) 2023 Ron Buckton. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-asyncdisposablestack.prototype.use +description: Invokes getter for `[Symbol.dispose]`. +info: | + AsyncDisposableStack.prototype.use ( value ) + + 1. Let asyncDisposableStack be the this value. + 2. Perform ? RequireInternalSlot(asyncDisposableStack, [[AsyncDisposableState]]). + 3. If asyncDisposableStack.[[AsyncDisposableState]] is disposed, throw a ReferenceError exception. + 4. Perform ? AddDisposableResource(asyncDisposableStack.[[DisposeCapability]], value, async-dispose). + ... + + AddDisposableResource ( disposeCapability, V, hint [, method ] ) + + 1. If method is not present then, + a. If V is either null or undefined and hint is sync-dispose, then + i. Return unused + b. Let resource be ? CreateDisposableResource(V, hint). + ... + + CreateDisposableResource ( V, hint [ , method ] ) + + 1. If method is not present, then + a. If V is either null or undefined, then + i. Set V to undefined + ii. Set method to undefined + b. Else, + i. If Type(V) is not Object, throw a TypeError exception. + ii. Set method to ? GetDisposeMethod(V, hint). + iii. If method is undefined, throw a TypeError exception. + 2. Else, + a. ... + 3. Return the DisposableResource Record { [[ResourceValue]]: V, [[Hint]]: hint, [[DisposeMethod]]: method }. + + GetDisposeMethod ( V, hint ) + + 1. If hint is async-dispose, then + a. Let method be ? GetMethod(V, @@asyncDispose). + b. If method is undefined, then + i. Set method to ? GetMethod(V, @@dispose). + 2. Else, + a. Let method be ? GetMethod(V, @@dispose). + 3. Return method. + +flags: [async] +includes: [asyncHelpers.js] +features: [explicit-resource-management] +---*/ + +asyncTest(async function () { + var resource = { + disposed: false, + get [Symbol.dispose]() { + return function() { + this.disposed = true; + }; + } + }; + + var stack = new AsyncDisposableStack(); + stack.use(resource); + await stack.disposeAsync(); + + assert.sameValue(resource.disposed, true, 'Expected resource to have been disposed'); +}); diff --git a/test/built-ins/AsyncDisposableStack/prototype/use/adds-async-disposable-value.js b/test/built-ins/AsyncDisposableStack/prototype/use/adds-async-disposable-value.js new file mode 100644 index 00000000000..bcb9151ddcc --- /dev/null +++ b/test/built-ins/AsyncDisposableStack/prototype/use/adds-async-disposable-value.js @@ -0,0 +1,43 @@ +// Copyright (C) 2023 Ron Buckton. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-asyncdisposablestack.prototype.use +description: Adds an asynchronously disposable resource to the stack +info: | + AsyncDisposableStack.prototype.use ( value ) + + 1. Let asyncDisposableStack be the this value. + 2. Perform ? RequireInternalSlot(asyncDisposableStack, [[AsyncDisposableState]]). + 3. If asyncDisposableStack.[[AsyncDisposableState]] is disposed, throw a ReferenceError exception. + 4. Perform ? AddDisposableResource(asyncDisposableStack.[[DisposeCapability]], value, async-dispose). + ... + + AddDisposableResource ( disposeCapability, V, hint [, method ] ) + + 1. If method is not present then, + a. If V is either null or undefined and hint is sync-dispose, then + i. Return unused + b. Let resource be ? CreateDisposableResource(V, hint). + 2. Else, + ... + 3. Append resource to disposeCapability.[[DisposableResourceStack]]. + 4. Return unused. + +flags: [async] +includes: [asyncHelpers.js] +features: [explicit-resource-management] +---*/ + +asyncTest(async function () { + var stack = new AsyncDisposableStack(); + var resource = { + disposed: false, + async [Symbol.asyncDispose]() { + this.disposed = true; + } + }; + stack.use(resource); + await stack.disposeAsync(); + assert.sameValue(resource.disposed, true, 'Expected resource to have been disposed'); +}); diff --git a/test/built-ins/AsyncDisposableStack/prototype/use/adds-sync-disposable-value.js b/test/built-ins/AsyncDisposableStack/prototype/use/adds-sync-disposable-value.js new file mode 100644 index 00000000000..26ecb302966 --- /dev/null +++ b/test/built-ins/AsyncDisposableStack/prototype/use/adds-sync-disposable-value.js @@ -0,0 +1,43 @@ +// Copyright (C) 2023 Ron Buckton. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-asyncdisposablestack.prototype.use +description: Adds a synchronously disposable resource to the stack +info: | + AsyncDisposableStack.prototype.use ( value ) + + 1. Let asyncDisposableStack be the this value. + 2. Perform ? RequireInternalSlot(asyncDisposableStack, [[AsyncDisposableState]]). + 3. If asyncDisposableStack.[[AsyncDisposableState]] is disposed, throw a ReferenceError exception. + 4. Perform ? AddDisposableResource(asyncDisposableStack.[[DisposeCapability]], value, async-dispose). + ... + + AddDisposableResource ( disposeCapability, V, hint [, method ] ) + + 1. If method is not present then, + a. If V is either null or undefined and hint is sync-dispose, then + i. Return unused + b. Let resource be ? CreateDisposableResource(V, hint). + 2. Else, + ... + 3. Append resource to disposeCapability.[[DisposableResourceStack]]. + 4. Return unused. + +flags: [async] +includes: [asyncHelpers.js] +features: [explicit-resource-management] +---*/ + +asyncTest(async function () { + var stack = new AsyncDisposableStack(); + var resource = { + disposed: false, + [Symbol.dispose]() { + this.disposed = true; + } + }; + stack.use(resource); + await stack.disposeAsync(); + assert.sameValue(resource.disposed, true, 'Expected resource to have been disposed'); +}); diff --git a/test/built-ins/AsyncDisposableStack/prototype/use/allows-null-value.js b/test/built-ins/AsyncDisposableStack/prototype/use/allows-null-value.js new file mode 100644 index 00000000000..de707c3736a --- /dev/null +++ b/test/built-ins/AsyncDisposableStack/prototype/use/allows-null-value.js @@ -0,0 +1,33 @@ +// Copyright (C) 2023 Ron Buckton. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-asyncdisposablestack.prototype.use +description: Does not throw when argument is 'null' +info: | + AsyncDisposableStack.prototype.use ( value ) + + 1. Let asyncDisposableStack be the this value. + 2. Perform ? RequireInternalSlot(asyncDisposableStack, [[AsyncDisposableState]]). + 3. If asyncDisposableStack.[[AsyncDisposableState]] is disposed, throw a ReferenceError exception. + 4. Perform ? AddDisposableResource(asyncDisposableStack.[[DisposeCapability]], value, async-dispose). + ... + + AddDisposableResource ( disposeCapability, V, hint [, method ] ) + + 1. If method is not present then, + a. If V is either null or undefined and hint is sync-dispose, then + i. Return unused + ... + ... + +features: [explicit-resource-management] +flags: [async] +includes: [asyncHelpers.js] +---*/ + +asyncTest(async function () { + var stack = new AsyncDisposableStack(); + stack.use(null); + await stack.disposeAsync(); +}); diff --git a/test/built-ins/AsyncDisposableStack/prototype/use/allows-undefined-value.js b/test/built-ins/AsyncDisposableStack/prototype/use/allows-undefined-value.js new file mode 100644 index 00000000000..0925aa2b60b --- /dev/null +++ b/test/built-ins/AsyncDisposableStack/prototype/use/allows-undefined-value.js @@ -0,0 +1,33 @@ +// Copyright (C) 2023 Ron Buckton. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-asyncdisposablestack.prototype.use +description: Does not throw when argument is 'undefined' +info: | + AsyncDisposableStack.prototype.use ( value ) + + 1. Let asyncDisposableStack be the this value. + 2. Perform ? RequireInternalSlot(asyncDisposableStack, [[AsyncDisposableState]]). + 3. If asyncDisposableStack.[[AsyncDisposableState]] is disposed, throw a ReferenceError exception. + 4. Perform ? AddDisposableResource(asyncDisposableStack.[[DisposeCapability]], value, async-dispose). + ... + + AddDisposableResource ( disposeCapability, V, hint [, method ] ) + + 1. If method is not present then, + a. If V is either null or undefined and hint is sync-dispose, then + i. Return unused + ... + ... + +features: [explicit-resource-management] +flags: [async] +includes: [asyncHelpers.js] +---*/ + +asyncTest(async function () { + var stack = new AsyncDisposableStack(); + stack.use(undefined); + await stack.disposeAsync(); +}); diff --git a/test/built-ins/AsyncDisposableStack/prototype/use/gets-value-Symbol.asyncDispose-property-once.js b/test/built-ins/AsyncDisposableStack/prototype/use/gets-value-Symbol.asyncDispose-property-once.js new file mode 100644 index 00000000000..02b6c0f8f73 --- /dev/null +++ b/test/built-ins/AsyncDisposableStack/prototype/use/gets-value-Symbol.asyncDispose-property-once.js @@ -0,0 +1,68 @@ +// Copyright (C) 2023 Ron Buckton. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-asyncdisposablestack.prototype.use +description: Only reads `[Symbol.asyncDispose]` method once, when added. +info: | + AsyncDisposableStack.prototype.use ( value ) + + 1. Let asyncDisposableStack be the this value. + 2. Perform ? RequireInternalSlot(asyncDisposableStack, [[AsyncDisposableState]]). + 3. If asyncDisposableStack.[[AsyncDisposableState]] is disposed, throw a ReferenceError exception. + 4. Perform ? AddDisposableResource(asyncDisposableStack.[[DisposeCapability]], value, async-dispose). + ... + + AddDisposableResource ( disposeCapability, V, hint [, method ] ) + + 1. If method is not present then, + a. If V is either null or undefined and hint is sync-dispose, then + i. Return unused + b. Let resource be ? CreateDisposableResource(V, hint). + ... + + CreateDisposableResource ( V, hint [ , method ] ) + + 1. If method is not present, then + a. If V is either null or undefined, then + i. Set V to undefined + ii. Set method to undefined + b. Else, + i. If Type(V) is not Object, throw a TypeError exception. + ii. Set method to ? GetDisposeMethod(V, hint). + iii. If method is undefined, throw a TypeError exception. + 2. Else, + a. ... + 3. Return the DisposableResource Record { [[ResourceValue]]: V, [[Hint]]: hint, [[DisposeMethod]]: method }. + + GetDisposeMethod ( V, hint ) + + 1. If hint is async-dispose, then + a. Let method be ? GetMethod(V, @@asyncDispose). + b. If method is undefined, then + i. Set method to ? GetMethod(V, @@dispose). + 2. Else, + a. Let method be ? GetMethod(V, @@dispose). + 3. Return method. + +flags: [async] +includes: [asyncHelpers.js] +features: [explicit-resource-management] +---*/ + +asyncTest(async function () { + var stack = new AsyncDisposableStack(); + var resource = { + disposeReadCount: 0, + get [Symbol.asyncDispose]() { + this.disposeReadCount++; + return async function() { }; + } + }; + stack.use(resource); + var countAfterUse = resource.disposeReadCount; + await stack.disposeAsync(); + var countAfterDispose = resource.disposeReadCount; + assert.sameValue(countAfterUse, 1, 'Expected [Symbol.asyncDispose] to have been read only once'); + assert.sameValue(countAfterDispose, 1, 'Expected [Symbol.asyncDispose] to have been read only once'); +}); diff --git a/test/built-ins/AsyncDisposableStack/prototype/use/gets-value-Symbol.dispose-property-after-trying-Symbol.asyncDispose.js b/test/built-ins/AsyncDisposableStack/prototype/use/gets-value-Symbol.dispose-property-after-trying-Symbol.asyncDispose.js new file mode 100644 index 00000000000..7bf3804b695 --- /dev/null +++ b/test/built-ins/AsyncDisposableStack/prototype/use/gets-value-Symbol.dispose-property-after-trying-Symbol.asyncDispose.js @@ -0,0 +1,69 @@ +// Copyright (C) 2023 Ron Buckton. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-asyncdisposablestack.prototype.use +description: Only reads `[Symbol.dispose]` after trying `[Symbol.asyncDispose]` frist +info: | + AsyncDisposableStack.prototype.use ( value ) + + 1. Let asyncDisposableStack be the this value. + 2. Perform ? RequireInternalSlot(asyncDisposableStack, [[AsyncDisposableState]]). + 3. If asyncDisposableStack.[[AsyncDisposableState]] is disposed, throw a ReferenceError exception. + 4. Perform ? AddDisposableResource(asyncDisposableStack.[[DisposeCapability]], value, async-dispose). + ... + + AddDisposableResource ( disposeCapability, V, hint [, method ] ) + + 1. If method is not present then, + a. If V is either null or undefined and hint is sync-dispose, then + i. Return unused + b. Let resource be ? CreateDisposableResource(V, hint). + ... + + CreateDisposableResource ( V, hint [ , method ] ) + + 1. If method is not present, then + a. If V is either null or undefined, then + i. Set V to undefined + ii. Set method to undefined + b. Else, + i. If Type(V) is not Object, throw a TypeError exception. + ii. Set method to ? GetDisposeMethod(V, hint). + iii. If method is undefined, throw a TypeError exception. + 2. Else, + a. ... + 3. Return the DisposableResource Record { [[ResourceValue]]: V, [[Hint]]: hint, [[DisposeMethod]]: method }. + + GetDisposeMethod ( V, hint ) + + 1. If hint is async-dispose, then + a. Let method be ? GetMethod(V, @@asyncDispose). + b. If method is undefined, then + i. Set method to ? GetMethod(V, @@dispose). + 2. Else, + a. Let method be ? GetMethod(V, @@dispose). + 3. Return method. + +includes: [deepEqual.js, asyncHelpers.js] +features: [explicit-resource-management] +flags: [async] +---*/ + +asyncTest(async function () { + var stack = new AsyncDisposableStack(); + var order = []; + var resource = { + get [Symbol.asyncDispose]() { + order.push('Symbol.asyncDispose'); + return undefined; + }, + get [Symbol.dispose]() { + order.push('Symbol.dispose'); + return function() {}; + }, + }; + stack.use(resource); + await stack.disposeAsync(); + assert.deepEqual(order, ['Symbol.asyncDispose', 'Symbol.dispose']); +}); diff --git a/test/built-ins/AsyncDisposableStack/prototype/use/gets-value-Symbol.dispose-property-once.js b/test/built-ins/AsyncDisposableStack/prototype/use/gets-value-Symbol.dispose-property-once.js new file mode 100644 index 00000000000..b278604eae6 --- /dev/null +++ b/test/built-ins/AsyncDisposableStack/prototype/use/gets-value-Symbol.dispose-property-once.js @@ -0,0 +1,68 @@ +// Copyright (C) 2023 Ron Buckton. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-asyncdisposablestack.prototype.use +description: Only reads `[Symbol.dispose]` method once, when added. +info: | + AsyncDisposableStack.prototype.use ( value ) + + 1. Let asyncDisposableStack be the this value. + 2. Perform ? RequireInternalSlot(asyncDisposableStack, [[AsyncDisposableState]]). + 3. If asyncDisposableStack.[[AsyncDisposableState]] is disposed, throw a ReferenceError exception. + 4. Perform ? AddDisposableResource(asyncDisposableStack.[[DisposeCapability]], value, async-dispose). + ... + + AddDisposableResource ( disposeCapability, V, hint [, method ] ) + + 1. If method is not present then, + a. If V is either null or undefined and hint is sync-dispose, then + i. Return unused + b. Let resource be ? CreateDisposableResource(V, hint). + ... + + CreateDisposableResource ( V, hint [ , method ] ) + + 1. If method is not present, then + a. If V is either null or undefined, then + i. Set V to undefined + ii. Set method to undefined + b. Else, + i. If Type(V) is not Object, throw a TypeError exception. + ii. Set method to ? GetDisposeMethod(V, hint). + iii. If method is undefined, throw a TypeError exception. + 2. Else, + a. ... + 3. Return the DisposableResource Record { [[ResourceValue]]: V, [[Hint]]: hint, [[DisposeMethod]]: method }. + + GetDisposeMethod ( V, hint ) + + 1. If hint is async-dispose, then + a. Let method be ? GetMethod(V, @@asyncDispose). + b. If method is undefined, then + i. Set method to ? GetMethod(V, @@dispose). + 2. Else, + a. Let method be ? GetMethod(V, @@dispose). + 3. Return method. + +flags: [async] +includes: [asyncHelpers.js] +features: [explicit-resource-management] +---*/ + +asyncTest(async function () { + var stack = new AsyncDisposableStack(); + var resource = { + disposeReadCount: 0, + get [Symbol.dispose]() { + this.disposeReadCount++; + return function() { }; + } + }; + stack.use(resource); + var countAfterUse = resource.disposeReadCount; + await stack.disposeAsync(); + var countAfterDispose = resource.disposeReadCount; + assert.sameValue(countAfterUse, 1, 'Expected [Symbol.dispose] to have been read only once'); + assert.sameValue(countAfterDispose, 1, 'Expected [Symbol.dispose] to have been read only once'); +}); diff --git a/test/built-ins/AsyncDisposableStack/prototype/use/puts-value-on-top-of-stack.js b/test/built-ins/AsyncDisposableStack/prototype/use/puts-value-on-top-of-stack.js new file mode 100644 index 00000000000..16584dc7d0d --- /dev/null +++ b/test/built-ins/AsyncDisposableStack/prototype/use/puts-value-on-top-of-stack.js @@ -0,0 +1,51 @@ +// Copyright (C) 2023 Ron Buckton. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-asyncdisposablestack.prototype.use +description: Puts value on the top of the dispose stack +info: | + AsyncDisposableStack.prototype.use ( value ) + + 1. Let asyncDisposableStack be the this value. + 2. Perform ? RequireInternalSlot(asyncDisposableStack, [[AsyncDisposableState]]). + 3. If asyncDisposableStack.[[AsyncDisposableState]] is disposed, throw a ReferenceError exception. + 4. Perform ? AddDisposableResource(asyncDisposableStack.[[DisposeCapability]], value, async-dispose). + ... + + AddDisposableResource ( disposeCapability, V, hint [, method ] ) + + 1. If method is not present then, + a. If V is either null or undefined and hint is sync-dispose, then + i. Return unused + b. Let resource be ? CreateDisposableResource(V, hint). + 2. Else, + ... + 3. Append resource to disposeCapability.[[DisposableResourceStack]]. + 4. Return unused. + +flags: [async] +includes: [asyncHelpers.js] +features: [explicit-resource-management] +---*/ + +asyncTest(async function () { + var stack = new AsyncDisposableStack(); + var disposed = []; + var resource1 = { + async [Symbol.asyncDispose]() { + disposed.push(this); + } + }; + var resource2 = { + [Symbol.dispose]() { + disposed.push(this); + } + }; + stack.use(resource1); + stack.use(resource2); + await stack.disposeAsync(); + assert.sameValue(2, disposed.length); + assert.sameValue(disposed[0], resource2, 'Expected resource2 to be the first disposed resource'); + assert.sameValue(disposed[1], resource1, 'Expected resource1 to be the second disposed resource'); +}); diff --git a/test/built-ins/AsyncDisposableStack/prototype/use/returns-value.js b/test/built-ins/AsyncDisposableStack/prototype/use/returns-value.js new file mode 100644 index 00000000000..26a3ef16b44 --- /dev/null +++ b/test/built-ins/AsyncDisposableStack/prototype/use/returns-value.js @@ -0,0 +1,22 @@ +// Copyright (C) 2023 Ron Buckton. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-asyncdisposablestack.prototype.use +description: Returns the argument provided. +info: | + AsyncDisposableStack.prototype.use ( value ) + + ... + 5. Return value. + +features: [explicit-resource-management] +---*/ + +var stack = new AsyncDisposableStack(); +var resource1 = { async [Symbol.asyncDispose]() { } }; +var resource2 = { [Symbol.dispose]() { } }; +assert.sameValue(stack.use(resource1), resource1); +assert.sameValue(stack.use(resource2), resource2); +assert.sameValue(stack.use(null), null); +assert.sameValue(stack.use(undefined), undefined); diff --git a/test/built-ins/AsyncDisposableStack/prototype/use/throws-if-disposed.js b/test/built-ins/AsyncDisposableStack/prototype/use/throws-if-disposed.js new file mode 100644 index 00000000000..8a8a7d27f98 --- /dev/null +++ b/test/built-ins/AsyncDisposableStack/prototype/use/throws-if-disposed.js @@ -0,0 +1,60 @@ +// Copyright (C) 2023 Ron Buckton. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-asyncdisposablestack.prototype.use +description: Throws a ReferenceError if this is disposed. +info: | + AsyncDisposableStack.prototype.use ( value ) + + 1. Let asyncDisposableStack be the this value. + 2. Perform ? RequireInternalSlot(asyncDisposableStack, [[AsyncDisposableState]]). + 3. If asyncDisposableStack.[[AsyncDisposableState]] is disposed, throw a ReferenceError exception. + ... + +features: [explicit-resource-management] +---*/ + +var stack = new AsyncDisposableStack(); +stack.disposeAsync(); + +assert.throws(ReferenceError, function() { + stack.use(undefined); +}, 'undefined'); + +assert.throws(ReferenceError, function() { + stack.use(null); +}, 'null'); + +assert.throws(ReferenceError, function() { + stack.use(true); +}, 'true'); + +assert.throws(ReferenceError, function() { + stack.use(false); +}, 'false'); + +assert.throws(ReferenceError, function() { + stack.use(1); +}, 'number'); + +assert.throws(ReferenceError, function() { + stack.use('object'); +}, 'string'); + +var s = Symbol(); +assert.throws(ReferenceError, function() { + stack.use(s); +}, 'symbol'); + +assert.throws(ReferenceError, function() { + stack.use({}); +}, 'non disposable object'); + +assert.throws(ReferenceError, function() { + stack.use({ async [Symbol.asyncDispose]() {} }); +}, 'async disposable object'); + +assert.throws(ReferenceError, function() { + stack.use({ [Symbol.dispose]() {} }); +}, 'disposable object'); diff --git a/test/built-ins/AsyncDisposableStack/prototype/use/throws-if-value-Symbol.asyncDispose-property-is-null-or-undefined.js b/test/built-ins/AsyncDisposableStack/prototype/use/throws-if-value-Symbol.asyncDispose-property-is-null-or-undefined.js new file mode 100644 index 00000000000..13c851c63a2 --- /dev/null +++ b/test/built-ins/AsyncDisposableStack/prototype/use/throws-if-value-Symbol.asyncDispose-property-is-null-or-undefined.js @@ -0,0 +1,61 @@ +// Copyright (C) 2023 Ron Buckton. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-asyncdisposablestack.prototype.use +description: Throws if the argument has a null-valued Symbol.asyncDispose property. +info: | + AsyncDisposableStack.prototype.use ( value ) + + 1. Let asyncDisposableStack be the this value. + 2. Perform ? RequireInternalSlot(asyncDisposableStack, [[AsyncDisposableState]]). + 3. If asyncDisposableStack.[[AsyncDisposableState]] is disposed, throw a ReferenceError exception. + 4. Perform ? AddDisposableResource(asyncDisposableStack.[[DisposeCapability]], value, async-dispose). + ... + + AddDisposableResource ( disposeCapability, V, hint [, method ] ) + + 1. If method is not present then, + a. If V is either null or undefined and hint is sync-dispose, then + i. Return unused + b. Let resource be ? CreateDisposableResource(V, hint). + ... + + CreateDisposableResource ( V, hint [ , method ] ) + + 1. If method is not present, then + a. If V is either null or undefined, then + i. Set V to undefined + ii. Set method to undefined + b. Else, + i. If Type(V) is not Object, throw a TypeError exception. + ii. Set method to ? GetDisposeMethod(V, hint). + iii. If method is undefined, throw a TypeError exception. + ... + + GetDisposeMethod ( V, hint ) + + 1. If hint is async-dispose, then + a. Let method be ? GetMethod(V, @@asyncDispose). + b. If method is undefined, then + i. Set method to ? GetMethod(V, @@dispose). + 2. Else, + a. Let method be ? GetMethod(V, @@dispose). + 3. Return method. + + GetMethod ( V, P ) + + 1. Let func be ? GetV(V, P). + 2. If func is either undefined or null, return undefined. + 3. ... + +features: [explicit-resource-management] +---*/ + +var stack = new AsyncDisposableStack(); +assert.throws(TypeError, function() { + stack.use({ [Symbol.asyncDispose]: null }); +}, 'null'); +assert.throws(TypeError, function() { + stack.use({ [Symbol.asyncDispose]: undefined }); +}, 'undefined'); diff --git a/test/built-ins/AsyncDisposableStack/prototype/use/throws-if-value-Symbol.asyncDispose-property-not-callable.js b/test/built-ins/AsyncDisposableStack/prototype/use/throws-if-value-Symbol.asyncDispose-property-not-callable.js new file mode 100644 index 00000000000..495ead4d4b0 --- /dev/null +++ b/test/built-ins/AsyncDisposableStack/prototype/use/throws-if-value-Symbol.asyncDispose-property-not-callable.js @@ -0,0 +1,78 @@ +// Copyright (C) 2023 Ron Buckton. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-asyncdisposablestack.prototype.use +description: Throws if the argument has a non-null, non-undefined, non-callable Symbol.asyncDispose property. +info: | + AsyncDisposableStack.prototype.use ( value ) + + 1. Let asyncDisposableStack be the this value. + 2. Perform ? RequireInternalSlot(asyncDisposableStack, [[AsyncDisposableState]]). + 3. If asyncDisposableStack.[[AsyncDisposableState]] is disposed, throw a ReferenceError exception. + 4. Perform ? AddDisposableResource(asyncDisposableStack.[[DisposeCapability]], value, async-dispose). + ... + + AddDisposableResource ( disposeCapability, V, hint [, method ] ) + + 1. If method is not present then, + a. If V is either null or undefined and hint is sync-dispose, then + i. Return unused + b. Let resource be ? CreateDisposableResource(V, hint). + ... + + CreateDisposableResource ( V, hint [ , method ] ) + + 1. If method is not present, then + a. If V is either null or undefined, then + i. Set V to undefined + ii. Set method to undefined + b. Else, + i. If Type(V) is not Object, throw a TypeError exception. + ii. Set method to ? GetDisposeMethod(V, hint). + iii. If method is undefined, throw a TypeError exception. + 2. Else, + a. ... + 3. Return the DisposableResource Record { [[ResourceValue]]: V, [[Hint]]: hint, [[DisposeMethod]]: method }. + + GetDisposeMethod ( V, hint ) + + 1. If hint is async-dispose, then + a. Let method be ? GetMethod(V, @@asyncDispose). + b. If method is undefined, then + i. Set method to ? GetMethod(V, @@dispose). + 2. Else, + a. Let method be ? GetMethod(V, @@dispose). + ... + + GetMethod ( V, P ) + + 1. Let func be ? GetV(V, P). + 2. If func is either undefined or null, return undefined. + 3. If IsCallable(func) is false, throw a TypeError exception. + ... + +features: [explicit-resource-management] +---*/ + +var stack = new AsyncDisposableStack(); +assert.throws(TypeError, function() { + stack.use({ [Symbol.asyncDispose]: true }); +}, 'true'); + +assert.throws(TypeError, function() { + stack.use({ [Symbol.asyncDispose]: false }); +}, 'false'); + +assert.throws(TypeError, function() { + stack.use({ [Symbol.asyncDispose]: 1 }); +}, 'number'); + +assert.throws(TypeError, function() { + stack.use({ [Symbol.asyncDispose]: 'object' }); +}, 'string'); + +var s = Symbol(); +assert.throws(TypeError, function() { + stack.use({ [Symbol.asyncDispose]: s }); +}, 'symbol'); diff --git a/test/built-ins/AsyncDisposableStack/prototype/use/throws-if-value-Symbol.dispose-property-is-null-or-undefined.js b/test/built-ins/AsyncDisposableStack/prototype/use/throws-if-value-Symbol.dispose-property-is-null-or-undefined.js new file mode 100644 index 00000000000..7f205913524 --- /dev/null +++ b/test/built-ins/AsyncDisposableStack/prototype/use/throws-if-value-Symbol.dispose-property-is-null-or-undefined.js @@ -0,0 +1,61 @@ +// Copyright (C) 2023 Ron Buckton. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-asyncdisposablestack.prototype.use +description: Throws if the argument has a null-valued Symbol.dispose property. +info: | + AsyncDisposableStack.prototype.use ( value ) + + 1. Let asyncDisposableStack be the this value. + 2. Perform ? RequireInternalSlot(asyncDisposableStack, [[AsyncDisposableState]]). + 3. If asyncDisposableStack.[[AsyncDisposableState]] is disposed, throw a ReferenceError exception. + 4. Perform ? AddDisposableResource(asyncDisposableStack.[[DisposeCapability]], value, async-dispose). + ... + + AddDisposableResource ( disposeCapability, V, hint [, method ] ) + + 1. If method is not present then, + a. If V is either null or undefined and hint is sync-dispose, then + i. Return unused + b. Let resource be ? CreateDisposableResource(V, hint). + ... + + CreateDisposableResource ( V, hint [ , method ] ) + + 1. If method is not present, then + a. If V is either null or undefined, then + i. Set V to undefined + ii. Set method to undefined + b. Else, + i. If Type(V) is not Object, throw a TypeError exception. + ii. Set method to ? GetDisposeMethod(V, hint). + iii. If method is undefined, throw a TypeError exception. + ... + + GetDisposeMethod ( V, hint ) + + 1. If hint is async-dispose, then + a. Let method be ? GetMethod(V, @@asyncDispose). + b. If method is undefined, then + i. Set method to ? GetMethod(V, @@dispose). + 2. Else, + a. Let method be ? GetMethod(V, @@dispose). + 3. Return method. + + GetMethod ( V, P ) + + 1. Let func be ? GetV(V, P). + 2. If func is either undefined or null, return undefined. + 3. ... + +features: [explicit-resource-management] +---*/ + +var stack = new AsyncDisposableStack(); +assert.throws(TypeError, function() { + stack.use({ [Symbol.dispose]: null }); +}, 'null'); +assert.throws(TypeError, function() { + stack.use({ [Symbol.dispose]: undefined }); +}, 'undefined'); diff --git a/test/built-ins/AsyncDisposableStack/prototype/use/throws-if-value-Symbol.dispose-property-not-callable.js b/test/built-ins/AsyncDisposableStack/prototype/use/throws-if-value-Symbol.dispose-property-not-callable.js new file mode 100644 index 00000000000..c7a62948ea7 --- /dev/null +++ b/test/built-ins/AsyncDisposableStack/prototype/use/throws-if-value-Symbol.dispose-property-not-callable.js @@ -0,0 +1,78 @@ +// Copyright (C) 2023 Ron Buckton. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-asyncdisposablestack.prototype.use +description: Throws if the argument has a non-null, non-undefined, non-callable Symbol.asyncDispose property. +info: | + AsyncDisposableStack.prototype.use ( value ) + + 1. Let asyncDisposableStack be the this value. + 2. Perform ? RequireInternalSlot(asyncDisposableStack, [[AsyncDisposableState]]). + 3. If asyncDisposableStack.[[AsyncDisposableState]] is disposed, throw a ReferenceError exception. + 4. Perform ? AddDisposableResource(asyncDisposableStack.[[DisposeCapability]], value, async-dispose). + ... + + AddDisposableResource ( disposeCapability, V, hint [, method ] ) + + 1. If method is not present then, + a. If V is either null or undefined and hint is sync-dispose, then + i. Return unused + b. Let resource be ? CreateDisposableResource(V, hint). + ... + + CreateDisposableResource ( V, hint [ , method ] ) + + 1. If method is not present, then + a. If V is either null or undefined, then + i. Set V to undefined + ii. Set method to undefined + b. Else, + i. If Type(V) is not Object, throw a TypeError exception. + ii. Set method to ? GetDisposeMethod(V, hint). + iii. If method is undefined, throw a TypeError exception. + 2. Else, + a. ... + 3. Return the DisposableResource Record { [[ResourceValue]]: V, [[Hint]]: hint, [[DisposeMethod]]: method }. + + GetDisposeMethod ( V, hint ) + + 1. If hint is async-dispose, then + a. Let method be ? GetMethod(V, @@asyncDispose). + b. If method is undefined, then + i. Set method to ? GetMethod(V, @@dispose). + 2. Else, + a. Let method be ? GetMethod(V, @@dispose). + ... + + GetMethod ( V, P ) + + 1. Let func be ? GetV(V, P). + 2. If func is either undefined or null, return undefined. + 3. If IsCallable(func) is false, throw a TypeError exception. + ... + +features: [explicit-resource-management] +---*/ + +var stack = new AsyncDisposableStack(); +assert.throws(TypeError, function() { + stack.use({ [Symbol.dispose]: true }); +}, 'true'); + +assert.throws(TypeError, function() { + stack.use({ [Symbol.dispose]: false }); +}, 'false'); + +assert.throws(TypeError, function() { + stack.use({ [Symbol.dispose]: 1 }); +}, 'number'); + +assert.throws(TypeError, function() { + stack.use({ [Symbol.dispose]: 'object' }); +}, 'string'); + +var s = Symbol(); +assert.throws(TypeError, function() { + stack.use({ [Symbol.dispose]: s }); +}, 'symbol'); diff --git a/test/built-ins/AsyncDisposableStack/prototype/use/throws-if-value-missing-Symbol.asyncDispose-and-Symbol.dispose.js b/test/built-ins/AsyncDisposableStack/prototype/use/throws-if-value-missing-Symbol.asyncDispose-and-Symbol.dispose.js new file mode 100644 index 00000000000..2995c1abc14 --- /dev/null +++ b/test/built-ins/AsyncDisposableStack/prototype/use/throws-if-value-missing-Symbol.asyncDispose-and-Symbol.dispose.js @@ -0,0 +1,59 @@ +// Copyright (C) 2023 Ron Buckton. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-asyncdisposablestack.prototype.use +description: Throws if the argument is an object that does not have a Symbol.asyncDispose property. +info: | + AsyncDisposableStack.prototype.use ( value ) + + 1. Let asyncDisposableStack be the this value. + 2. Perform ? RequireInternalSlot(asyncDisposableStack, [[AsyncDisposableState]]). + 3. If asyncDisposableStack.[[AsyncDisposableState]] is disposed, throw a ReferenceError exception. + 4. Perform ? AddDisposableResource(asyncDisposableStack.[[DisposeCapability]], value, async-dispose). + ... + + AddDisposableResource ( disposeCapability, V, hint [, method ] ) + + 1. If method is not present then, + a. If V is either null or undefined and hint is sync-dispose, then + i. Return unused + b. Let resource be ? CreateDisposableResource(V, hint). + ... + + CreateDisposableResource ( V, hint [ , method ] ) + + 1. If method is not present, then + a. If V is either null or undefined, then + i. Set V to undefined + ii. Set method to undefined + b. Else, + i. If Type(V) is not Object, throw a TypeError exception. + ii. Set method to ? GetDisposeMethod(V, hint). + iii. If method is undefined, throw a TypeError exception. + ... + + GetDisposeMethod ( V, hint ) + + 1. If hint is async-dispose, then + a. Let method be ? GetMethod(V, @@asyncDispose). + b. If method is undefined, then + i. Set method to ? GetMethod(V, @@dispose). + 2. Else, + a. Let method be ? GetMethod(V, @@dispose). + 3. Return method. + + GetMethod ( V, P ) + + 1. Let func be ? GetV(V, P). + 2. If func is either undefined or null, return undefined. + 3. If IsCallable(func) is false, throw a TypeError exception. + ... + +features: [explicit-resource-management] +---*/ + +var stack = new AsyncDisposableStack(); +assert.throws(TypeError, function() { + stack.use({ }); +}, 'true'); diff --git a/test/built-ins/AsyncDisposableStack/prototype/use/throws-if-value-not-object.js b/test/built-ins/AsyncDisposableStack/prototype/use/throws-if-value-not-object.js index e9232bd1659..88e4a331f5f 100644 --- a/test/built-ins/AsyncDisposableStack/prototype/use/throws-if-value-not-object.js +++ b/test/built-ins/AsyncDisposableStack/prototype/use/throws-if-value-not-object.js @@ -8,8 +8,8 @@ info: | AsyncDisposableStack.prototype.use ( value ) 1. Let asyncDisposableStack be the this value. - 2. Perform ? RequireInternalSlot(asyncDisposableStack, [[DisposableState]]). - 3. If asyncDisposableStack.[[DisposableState]] is disposed, throw a ReferenceError exception. + 2. Perform ? RequireInternalSlot(asyncDisposableStack, [[AsyncDisposableState]]). + 3. If asyncDisposableStack.[[AsyncDisposableState]] is disposed, throw a ReferenceError exception. 4. Perform ? AddDisposableResource(asyncDisposableStack.[[DisposeCapability]], value, async-dispose). ... diff --git a/test/built-ins/DisposableStack/prototype/adopt/throws-if-disposed.js b/test/built-ins/DisposableStack/prototype/adopt/throws-if-disposed.js new file mode 100644 index 00000000000..11bd8ae1ae7 --- /dev/null +++ b/test/built-ins/DisposableStack/prototype/adopt/throws-if-disposed.js @@ -0,0 +1,23 @@ +// Copyright (C) 2026 Ron Buckton. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-disposablestack.prototype.adopt +description: Throws a ReferenceError if this is disposed. +info: | + DisposableStack.prototype.adopt ( value, onDispose ) + + 1. Let disposableStack be the this value. + 2. Perform ? RequireInternalSlot(disposableStack, [[DisposableState]]). + 3. If disposableStack.[[DisposableState]] is disposed, throw a ReferenceError exception. + ... + +features: [explicit-resource-management] +---*/ + +var stack = new DisposableStack(); +stack.dispose(); + +assert.throws(ReferenceError, function() { + stack.adopt(null, _ => {}); +}); diff --git a/test/built-ins/DisposableStack/prototype/constructor.js b/test/built-ins/DisposableStack/prototype/constructor.js index 60c4a36a03f..d5cb253c9fc 100644 --- a/test/built-ins/DisposableStack/prototype/constructor.js +++ b/test/built-ins/DisposableStack/prototype/constructor.js @@ -5,27 +5,16 @@ esid: sec-properties-of-the-disposablestack-prototype-object description: DisposableStack.prototype.constructor info: | - DisposableStack.prototype.constructor - - Normative Optional - The initial value of DisposableStack.prototype.constructor is the intrinsic object %DisposableStack%. - This property has the attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }. - - This section is to be treated identically to the "Annex B" of ECMA-262, but to be written in-line with the main specification. + 17 ECMAScript Standard Built-in Objects includes: [propertyHelper.js] features: [explicit-resource-management] ---*/ -var actual = Object.prototype.hasOwnProperty.call(DisposableStack, 'constructor'); - -// If implemented, it should conform to the spec text -if (actual) { - verifyProperty(DisposableStack.prototype, 'constructor', { - value: DisposableStack, - writable: true, - enumerable: false, - configurable: true - }); -} +verifyProperty(DisposableStack.prototype, 'constructor', { + value: DisposableStack, + writable: true, + enumerable: false, + configurable: true +}); diff --git a/test/built-ins/DisposableStack/prototype/defer/throws-if-disposed.js b/test/built-ins/DisposableStack/prototype/defer/throws-if-disposed.js new file mode 100644 index 00000000000..ab5b0a96e2f --- /dev/null +++ b/test/built-ins/DisposableStack/prototype/defer/throws-if-disposed.js @@ -0,0 +1,23 @@ +// Copyright (C) 2026 Ron Buckton. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-disposablestack.prototype.defer +description: Throws a ReferenceError if this is disposed. +info: | + DisposableStack.prototype.defer ( onDispose ) + + 1. Let disposableStack be the this value. + 2. Perform ? RequireInternalSlot(disposableStack, [[DisposableState]]). + 3. If disposableStack.[[DisposableState]] is disposed, throw a ReferenceError exception. + ... + +features: [explicit-resource-management] +---*/ + +var stack = new DisposableStack(); +stack.dispose(); + +assert.throws(ReferenceError, function() { + stack.defer(() => {}); +});