diff --git a/package.json b/package.json index 842d95bfdc1..8f602dd713c 100644 --- a/package.json +++ b/package.json @@ -24,6 +24,7 @@ "test:diff:v8": "test262-harness -t 8 --hostType=d8 --hostPath=v8 $(npm run --silent diff)", "test:diff:spidermonkey": "test262-harness -t 8 --hostType=jsshell --hostPath=spidermonkey $(npm run --silent diff)", "test:diff:javascriptcore": "test262-harness -t 8 --hostType=jsc --hostPath=javascriptcore $(npm run --silent diff)", - "test:diff:xs": "test262-harness -t 8 --hostType=xs --hostPath=xs $(npm run --silent diff)" + "test:diff:xs": "test262-harness -t 8 --hostType=xs --hostPath=xs $(npm run --silent diff)", + "test:diff:engine262": "test262-harness -t 8 --hostType=engine262 --hostPath=engine262 $(npm run --silent diff)" } } 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..9ef677203d2 --- /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, [[DisposableState]]). + 3. If asyncDisposableStack.[[DisposableState]] 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..f12d9faa027 --- /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, [[DisposableState]]). + 3. If asyncDisposableStack.[[DisposableState]] 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..2364785d504 --- /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, [[DisposableState]]). + 3. If asyncDisposableStack.[[DisposableState]] 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-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..2e9bdca8e66 --- /dev/null +++ b/test/built-ins/AsyncDisposableStack/prototype/constructor.js @@ -0,0 +1,31 @@ +// 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: | + AsyncDisposableStack.prototype.constructor + + Normative Optional + + The initial value of AsyncDisposableStack.prototype.constructor is the intrinsic object %AsyncDisposableStack%. + + 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. +includes: [propertyHelper.js] +features: [explicit-resource-management] +---*/ + +var actual = AsyncDisposableStack.prototype.hasOwnProperty('constructor'); + +// If implemented, it should conform to the spec text +if (actual) { + 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..2a41d632171 --- /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, [[DisposableState]]). + 3. If asyncDisposableStack.[[DisposableState]] 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..cc450aafdde --- /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, [[DisposableState]]). + 3. If asyncDisposableStack.[[DisposableState]] 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..75b29a87b08 --- /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 the argument provided. +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-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..112e4b71674 --- /dev/null +++ b/test/built-ins/AsyncDisposableStack/prototype/disposeAsync/disposes-resources-in-reverse-order.js @@ -0,0 +1,77 @@ +// 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.sameValue(disposed[0], dispose6); + assert.sameValue(disposed[1], dispose5); + assert.sameValue(disposed[2], resource4); + assert.sameValue(disposed[3], resource3); + assert.sameValue(disposed[4], resource2); + assert.sameValue(disposed[5], 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..2b4f51b0fe7 --- /dev/null +++ b/test/built-ins/AsyncDisposableStack/prototype/disposeAsync/does-not-reinvoke-disposers-if-already-disposed.js @@ -0,0 +1,58 @@ +// 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(); + 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..ea62f98857e --- /dev/null +++ b/test/built-ins/AsyncDisposableStack/prototype/disposeAsync/does-not-reject-if-already-disposed.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.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(); + await stack.disposeAsync(); + await p; + await stack.disposeAsync(); +}); 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..99f37ed34c6 --- /dev/null +++ b/test/built-ins/AsyncDisposableStack/prototype/disposeAsync/rejects-with-suppressederror-if-multiple-errors-during-disposal.js @@ -0,0 +1,65 @@ +// 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; }); + try { + await stack.disposeAsync(); + assert(false, 'Expected await stack.disposeAsync() to have thrown an error.'); + } + catch (e) { + assert(e instanceof SuppressedError, "Expected await stack.disposeAsync() to have thrown a SuppressedError"); + assert.sameValue(e.error, error1, "Expected the outermost suppressing error to have been 'error1'"); + assert(e.suppressed instanceof SuppressedError, "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..a7505ab9b76 --- /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, [[DisposableState]]). + 3. If asyncDisposableStack.[[DisposableState]] is disposed, return undefined. + 4. Set asyncDisposableStack.[[DisposableState]] 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..37f2cb6171e --- /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, [[DisposableState]]). + 3. If asyncDisposableStack.[[DisposableState]] is disposed, return undefined. + 4. Set asyncDisposableStack.[[DisposableState]] 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(stack.disposeAsync() instanceof Promise); 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..026b228660d --- /dev/null +++ b/test/built-ins/AsyncDisposableStack/prototype/move/returns-new-asyncdisposablestack-that-contains-moved-resources.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.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..62d546dba1d --- /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, [[DisposableState]]). + 3. If asyncDisposableStack.[[DisposableState]] 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..85d66128a47 --- /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, [[DisposableState]]). + 3. If asyncDisposableStack.[[DisposableState]] 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..50ebac44e0d --- /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, [[DisposableState]]). + 3. If asyncDisposableStack.[[DisposableState]] 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..68dd8ecb433 --- /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, [[DisposableState]]). + 3. If asyncDisposableStack.[[DisposableState]] 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..70df7e47fa8 --- /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, [[DisposableState]]). + 3. If asyncDisposableStack.[[DisposableState]] 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..7006589ee80 --- /dev/null +++ b/test/built-ins/AsyncDisposableStack/prototype/use/allows-null-value.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.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, [[DisposableState]]). + 3. If asyncDisposableStack.[[DisposableState]] 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] +---*/ + +var stack = new AsyncDisposableStack(); +stack.use(null); 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..6ad7ea0eab4 --- /dev/null +++ b/test/built-ins/AsyncDisposableStack/prototype/use/allows-undefined-value.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.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, [[DisposableState]]). + 3. If asyncDisposableStack.[[DisposableState]] 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] +---*/ + +var stack = new AsyncDisposableStack(); +stack.use(undefined); 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..85764fa73c5 --- /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, [[DisposableState]]). + 3. If asyncDisposableStack.[[DisposableState]] 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..4e041f54991 --- /dev/null +++ b/test/built-ins/AsyncDisposableStack/prototype/use/gets-value-Symbol.dispose-property-after-trying-Symbol.asyncDispose.js @@ -0,0 +1,65 @@ +// 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, [[DisposableState]]). + 3. If asyncDisposableStack.[[DisposableState]] 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] +features: [explicit-resource-management] +---*/ + +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); +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..1a98b7b8d5e --- /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, [[DisposableState]]). + 3. If asyncDisposableStack.[[DisposableState]] 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..cab72a447ce --- /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, [[DisposableState]]). + 3. If asyncDisposableStack.[[DisposableState]] 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..bae2327ad16 --- /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, [[DisposableState]]). + 3. If asyncDisposableStack.[[DisposableState]] 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.js b/test/built-ins/AsyncDisposableStack/prototype/use/throws-if-value-Symbol.asyncDispose-property-is-null.js new file mode 100644 index 00000000000..57d11850ca6 --- /dev/null +++ b/test/built-ins/AsyncDisposableStack/prototype/use/throws-if-value-Symbol.asyncDispose-property-is-null.js @@ -0,0 +1,58 @@ +// 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, [[DisposableState]]). + 3. If asyncDisposableStack.[[DisposableState]] 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 }); +}, 'true'); diff --git a/test/built-ins/AsyncDisposableStack/prototype/use/throws-if-value-Symbol.asyncDispose-property-is-undefined.js b/test/built-ins/AsyncDisposableStack/prototype/use/throws-if-value-Symbol.asyncDispose-property-is-undefined.js new file mode 100644 index 00000000000..b74d296b0d9 --- /dev/null +++ b/test/built-ins/AsyncDisposableStack/prototype/use/throws-if-value-Symbol.asyncDispose-property-is-undefined.js @@ -0,0 +1,58 @@ +// 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 an undefined-valued Symbol.asyncDispose property. +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. + 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]: undefined }); +}, 'true'); 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..d7929bf6eb6 --- /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, [[DisposableState]]). + 3. If asyncDisposableStack.[[DisposableState]] 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.js b/test/built-ins/AsyncDisposableStack/prototype/use/throws-if-value-Symbol.dispose-property-is-null.js new file mode 100644 index 00000000000..68193c5a74d --- /dev/null +++ b/test/built-ins/AsyncDisposableStack/prototype/use/throws-if-value-Symbol.dispose-property-is-null.js @@ -0,0 +1,58 @@ +// 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, [[DisposableState]]). + 3. If asyncDisposableStack.[[DisposableState]] 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 }); +}, 'true'); diff --git a/test/built-ins/AsyncDisposableStack/prototype/use/throws-if-value-Symbol.dispose-property-is-undefined.js b/test/built-ins/AsyncDisposableStack/prototype/use/throws-if-value-Symbol.dispose-property-is-undefined.js new file mode 100644 index 00000000000..0b911318ecf --- /dev/null +++ b/test/built-ins/AsyncDisposableStack/prototype/use/throws-if-value-Symbol.dispose-property-is-undefined.js @@ -0,0 +1,58 @@ +// 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 an undefined-valued Symbol.dispose property. +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. + 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]: undefined }); +}, 'true'); 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..51ccf41c63d --- /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, [[DisposableState]]). + 3. If asyncDisposableStack.[[DisposableState]] 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..c6e139680dd --- /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, [[DisposableState]]). + 3. If asyncDisposableStack.[[DisposableState]] 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/DisposableStack/prototype/Symbol.toStringTag.js b/test/built-ins/DisposableStack/prototype/Symbol.toStringTag.js new file mode 100644 index 00000000000..b7b27c997d0 --- /dev/null +++ b/test/built-ins/DisposableStack/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-disposablestack.prototype-@@toStringTag +description: > + `Symbol.toStringTag` property descriptor +info: | + The initial value of the @@toStringTag property is the String value + 'DisposableStack'. + + This property has the attributes { [[Writable]]: false, [[Enumerable]]: + false, [[Configurable]]: true }. +includes: [propertyHelper.js] +features: [explicit-resource-management, Symbol, Symbol.toStringTag] +---*/ + +verifyProperty(DisposableStack.prototype, Symbol.toStringTag, { + value: 'DisposableStack', + writable: false, + enumerable: false, + configurable: true +}); diff --git a/test/built-ins/DisposableStack/prototype/adopt/adds-value-onDispose.js b/test/built-ins/DisposableStack/prototype/adopt/adds-value-onDispose.js new file mode 100644 index 00000000000..6f32e4c8aec --- /dev/null +++ b/test/built-ins/DisposableStack/prototype/adopt/adds-value-onDispose.js @@ -0,0 +1,37 @@ +// Copyright (C) 2023 Ron Buckton. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-disposablestack.prototype.adopt +description: Adds a disposable resource to the stack +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. + 4. If IsCallable(onDispose) is false, throw a TypeError exception. + 5. Let closure be a new Abstract Closure with no parameters that captures value and onDispose and performs the following steps when called: + a. Perform ? Call(onDispose, undefined, « value »). + 6. Let F be CreateBuiltinFunction(closure, 0, "", « »). + 7. Perform ? AddDisposableResource(disposableStack.[[DisposeCapability]], undefined, sync-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. + +features: [explicit-resource-management] +---*/ + +var stack = new DisposableStack(); +var resource = { disposed: false }; +stack.adopt(resource, r => { r.disposed = true }); +stack.dispose(); +assert.sameValue(resource.disposed, true, 'Expected resource to have been disposed'); diff --git a/test/built-ins/DisposableStack/prototype/adopt/allows-any-value.js b/test/built-ins/DisposableStack/prototype/adopt/allows-any-value.js new file mode 100644 index 00000000000..2071285e6a2 --- /dev/null +++ b/test/built-ins/DisposableStack/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-disposablestack.prototype.adopt +description: Allows any 'value' +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. + 4. If IsCallable(onDispose) is false, throw a TypeError exception. + 5. Let closure be a new Abstract Closure with no parameters that captures value and onDispose and performs the following steps when called: + a. Perform ? Call(onDispose, undefined, « value »). + 6. Let F be CreateBuiltinFunction(closure, 0, "", « »). + 7. Perform ? AddDisposableResource(disposableStack.[[DisposeCapability]], undefined, sync-dispose, F). + ... + +features: [explicit-resource-management] +---*/ + +var stack = new DisposableStack(); +stack.adopt(null, _ => {}); +stack.adopt(undefined, _ => {}); +stack.adopt({}, _ => {}); +stack.adopt({ [Symbol.dispose]() {} }, _ => {}); +stack.adopt(() => {}, _ => {}); +stack.adopt(true, _ => {}); +stack.adopt(false, _ => {}); +stack.adopt(1, _ => {}); +stack.adopt('object', _ => {}); +stack.adopt(Symbol(), _ => {}); diff --git a/test/built-ins/DisposableStack/prototype/adopt/puts-value-onDispose-on-top-of-stack.js b/test/built-ins/DisposableStack/prototype/adopt/puts-value-onDispose-on-top-of-stack.js new file mode 100644 index 00000000000..80022163bab --- /dev/null +++ b/test/built-ins/DisposableStack/prototype/adopt/puts-value-onDispose-on-top-of-stack.js @@ -0,0 +1,46 @@ +// Copyright (C) 2023 Ron Buckton. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-disposablestack.prototype.adopt +description: Adds a disposable resource to the stack +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. + 4. If IsCallable(onDispose) is false, throw a TypeError exception. + 5. Let closure be a new Abstract Closure with no parameters that captures value and onDispose and performs the following steps when called: + a. Perform ? Call(onDispose, undefined, « value »). + 6. Let F be CreateBuiltinFunction(closure, 0, "", « »). + 7. Perform ? AddDisposableResource(disposableStack.[[DisposeCapability]], undefined, sync-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. + +features: [explicit-resource-management] +---*/ + +var stack = new DisposableStack(); +var disposed = []; +var resource1 = {}; +function dispose1(res) { disposed.push([res, dispose1]); } +var resource2 = {}; +function dispose2(res) { disposed.push([res, dispose2]); } +stack.adopt(resource1, dispose1); +stack.adopt(resource2, dispose2); +stack.dispose(); +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/DisposableStack/prototype/adopt/returns-value.js b/test/built-ins/DisposableStack/prototype/adopt/returns-value.js new file mode 100644 index 00000000000..60057b61922 --- /dev/null +++ b/test/built-ins/DisposableStack/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-disposablestack.prototype.adopt +description: Returns the argument provided. +info: | + DisposableStack.prototype.adopt ( value, onDispose ) + + ... + 8. Return value. + +features: [explicit-resource-management] +---*/ + +var stack = new DisposableStack(); +var resource = {}; +assert.sameValue(stack.adopt(resource, _ => {}), resource); +assert.sameValue(stack.adopt(null, _ => {}), null); +assert.sameValue(stack.adopt(undefined, _ => {}), undefined); diff --git a/test/built-ins/DisposableStack/prototype/adopt/throws-if-onDispose-not-callable.js b/test/built-ins/DisposableStack/prototype/adopt/throws-if-onDispose-not-callable.js new file mode 100644 index 00000000000..127168b31c8 --- /dev/null +++ b/test/built-ins/DisposableStack/prototype/adopt/throws-if-onDispose-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-disposablestack.prototype.adopt +description: Throws if onDispose argument not callable +info: | + DisposableStack.prototype.adopt ( value, onDispose ) + + ... + 4. If IsCallable(onDispose) is false, throw a TypeError exception. + ... + +features: [explicit-resource-management] +---*/ + +var stack = new DisposableStack(); +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/DisposableStack/prototype/constructor.js b/test/built-ins/DisposableStack/prototype/constructor.js new file mode 100644 index 00000000000..e093b36bc61 --- /dev/null +++ b/test/built-ins/DisposableStack/prototype/constructor.js @@ -0,0 +1,31 @@ +// 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-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. +includes: [propertyHelper.js] +features: [explicit-resource-management] +---*/ + +var actual = DisposableStack.prototype.hasOwnProperty('constructor'); + +// If implemented, it should conform to the spec text +if (actual) { + verifyProperty(DisposableStack.prototype, 'constructor', { + value: DisposableStack, + writable: true, + enumerable: false, + configurable: true + }); +} diff --git a/test/built-ins/DisposableStack/prototype/defer/adds-onDispose.js b/test/built-ins/DisposableStack/prototype/defer/adds-onDispose.js new file mode 100644 index 00000000000..df4156f03ba --- /dev/null +++ b/test/built-ins/DisposableStack/prototype/defer/adds-onDispose.js @@ -0,0 +1,34 @@ +// Copyright (C) 2023 Ron Buckton. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-disposablestack.prototype.defer +description: Adds an onDispose callback to the stack +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. + 4. If IsCallable(onDispose) is false, throw a TypeError exception. + 5. Perform ? AddDisposableResource(disposableStack.[[DisposeCapability]], undefined, sync-dispose, onDispose). + ... + + 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. + +features: [explicit-resource-management] +---*/ + +var stack = new DisposableStack(); +var disposed = false; +stack.defer(() => { disposed = true }); +stack.dispose(); +assert.sameValue(disposed, true, 'Expected callback to have been called'); diff --git a/test/built-ins/DisposableStack/prototype/defer/puts-onDispose-on-top-of-stack.js b/test/built-ins/DisposableStack/prototype/defer/puts-onDispose-on-top-of-stack.js new file mode 100644 index 00000000000..4658c4cfe6c --- /dev/null +++ b/test/built-ins/DisposableStack/prototype/defer/puts-onDispose-on-top-of-stack.js @@ -0,0 +1,39 @@ +// Copyright (C) 2023 Ron Buckton. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-disposablestack.prototype.defer +description: Adds a disposable resource to the stack +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. + 4. If IsCallable(onDispose) is false, throw a TypeError exception. + 5. Perform ? AddDisposableResource(disposableStack.[[DisposeCapability]], undefined, sync-dispose, onDispose). + ... + + 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. + +features: [explicit-resource-management] +---*/ + +var stack = new DisposableStack(); +var disposed = []; +function dispose1() { disposed.push(dispose1); } +function dispose2() { disposed.push(dispose2); } +stack.defer(dispose1); +stack.defer(dispose2); +stack.dispose(); +assert.sameValue(2, disposed.length); +assert.sameValue(disposed[0], dispose2, 'Expected dispose2 to be the first onDispose invoked'); +assert.sameValue(disposed[1], dispose1, 'Expected dispose1 to be the second onDispose invoked'); diff --git a/test/built-ins/DisposableStack/prototype/defer/returns-undefined.js b/test/built-ins/DisposableStack/prototype/defer/returns-undefined.js new file mode 100644 index 00000000000..3b5a2a11a34 --- /dev/null +++ b/test/built-ins/DisposableStack/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-disposablestack.prototype.defer +description: Returns the argument provided. +info: | + DisposableStack.prototype.defer ( onDispose ) + + ... + 6. Return undefined. + +features: [explicit-resource-management] +---*/ + +var stack = new DisposableStack(); +assert.sameValue(stack.defer(_ => {}), undefined); diff --git a/test/built-ins/DisposableStack/prototype/defer/throws-if-onDispose-not-callable.js b/test/built-ins/DisposableStack/prototype/defer/throws-if-onDispose-not-callable.js new file mode 100644 index 00000000000..80fbbbbebad --- /dev/null +++ b/test/built-ins/DisposableStack/prototype/defer/throws-if-onDispose-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-disposablestack.prototype.defer +description: Adds a callback to the stack +info: | + DisposableStack.prototype.defer ( onDispose ) + + ... + 4. If IsCallable(onDispose) is false, throw a TypeError exception. + ... + +features: [explicit-resource-management] +---*/ + +var stack = new DisposableStack(); +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/DisposableStack/prototype/dispose/disposes-resources-in-reverse-order.js b/test/built-ins/DisposableStack/prototype/dispose/disposes-resources-in-reverse-order.js new file mode 100644 index 00000000000..4ea5aeb9c5d --- /dev/null +++ b/test/built-ins/DisposableStack/prototype/dispose/disposes-resources-in-reverse-order.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-disposablestack.prototype.dispose +description: Added resources are disposed in reverse order +info: | + UsingDeclaration : using BindingList ; + + 1. Perform ? BindingEvaluation of BindingList with argument sync-dispose. + 2. Return empty. + + BindingList : BindingList , LexicalBinding + + 1. Perform ? BindingEvaluation of BindingList with argument hint. + 2. Perform ? BindingEvaluation of LexicalBinding with argument hint. + 3. Return unused. + + LexicalBinding : BindingIdentifier Initializer + + 1. Let bindingId be StringValue of BindingIdentifier. + 2. Let lhs be ? ResolveBinding(bindingId). + 3. If IsAnonymousFunctionDefinition(Initializer) is true, then + a. Let value be NamedEvaluation of Initializer with argument bindingId. + 4. Else, + a. Let rhs be the result of evaluating Initializer. + b. Let value be ? GetValue(rhs). + 5. Return ? InitializeReferencedBinding(lhs, value, hint). + + 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 DisposableStack(); +var disposed = []; +var resource1 = { [Symbol.dispose]() { disposed.push(resource1); } }; +var resource2 = {}; +function dispose2(res) { disposed.push(res); } +function dispose3() { disposed.push(dispose3); } +stack.use(resource1); +stack.adopt(resource2, dispose2); +stack.defer(dispose3); +stack.dispose(); +assert.sameValue(disposed[0], dispose3); +assert.sameValue(disposed[1], resource2); +assert.sameValue(disposed[2], resource1); diff --git a/test/built-ins/DisposableStack/prototype/dispose/does-not-reinvoke-disposers-if-already-disposed.js b/test/built-ins/DisposableStack/prototype/dispose/does-not-reinvoke-disposers-if-already-disposed.js new file mode 100644 index 00000000000..5d5bddb08a0 --- /dev/null +++ b/test/built-ins/DisposableStack/prototype/dispose/does-not-reinvoke-disposers-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-disposablestack.prototype.dispose +description: Does not re-invoke disposal on resources after stack has already been disposed. +info: | + DisposableStack.prototype.dispose ( ) + + 1. Let disposableStack be the this value. + 2. Perform ? RequireInternalSlot(disposableStack, [[DisposableState]]). + 3. If disposableStack.[[DisposableState]] is disposed, return undefined. + 4. Set disposableStack.[[DisposableState]] to disposed. + 5. Return DisposeResources(disposableStack.[[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 DisposableStack(); +var useCount = 0; +var adoptCount = 0; +var deferCount = 0; +stack.use({ [Symbol.dispose]() { useCount++; } }); +stack.adopt({}, _ => { adoptCount++; }); +stack.defer(() => { deferCount++; }); +stack.dispose(); +stack.dispose(); +assert.sameValue(useCount, 1); +assert.sameValue(adoptCount, 1); +assert.sameValue(deferCount, 1); diff --git a/test/built-ins/DisposableStack/prototype/dispose/does-not-throw-if-already-disposed.js b/test/built-ins/DisposableStack/prototype/dispose/does-not-throw-if-already-disposed.js new file mode 100644 index 00000000000..2eb0581a9f8 --- /dev/null +++ b/test/built-ins/DisposableStack/prototype/dispose/does-not-throw-if-already-disposed.js @@ -0,0 +1,45 @@ +// Copyright (C) 2023 Ron Buckton. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-disposablestack.prototype.dispose +description: Does not throw if already disposed. +info: | + DisposableStack.prototype.dispose ( ) + + 1. Let disposableStack be the this value. + 2. Perform ? RequireInternalSlot(disposableStack, [[DisposableState]]). + 3. If disposableStack.[[DisposableState]] is disposed, return undefined. + 4. Set disposableStack.[[DisposableState]] to disposed. + 5. Return DisposeResources(disposableStack.[[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 DisposableStack(); +stack.dispose(); +stack.dispose(); diff --git a/test/built-ins/DisposableStack/prototype/dispose/returns-undefined.js b/test/built-ins/DisposableStack/prototype/dispose/returns-undefined.js new file mode 100644 index 00000000000..f8b1365c0ff --- /dev/null +++ b/test/built-ins/DisposableStack/prototype/dispose/returns-undefined.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-disposablestack.prototype.dispose +description: Return value of dispose is undefined +info: | + DisposableStack.prototype.dispose ( ) + + 1. Let disposableStack be the this value. + 2. Perform ? RequireInternalSlot(disposableStack, [[DisposableState]]). + 3. If disposableStack.[[DisposableState]] is disposed, return undefined. + 4. Set disposableStack.[[DisposableState]] to disposed. + 5. Return DisposeResources(disposableStack.[[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 DisposableStack(); +assert.sameValue(stack.dispose(), undefined); diff --git a/test/built-ins/DisposableStack/prototype/dispose/sets-state-to-disposed.js b/test/built-ins/DisposableStack/prototype/dispose/sets-state-to-disposed.js new file mode 100644 index 00000000000..105f6988a62 --- /dev/null +++ b/test/built-ins/DisposableStack/prototype/dispose/sets-state-to-disposed.js @@ -0,0 +1,48 @@ +// Copyright (C) 2023 Ron Buckton. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-disposablestack.prototype.dispose +description: Sets the [[DisposableState]] internal slot to disposed +info: | + DisposableStack.prototype.dispose ( ) + + 1. Let disposableStack be the this value. + 2. Perform ? RequireInternalSlot(disposableStack, [[DisposableState]]). + 3. If disposableStack.[[DisposableState]] is disposed, return undefined. + 4. Set disposableStack.[[DisposableState]] to disposed. + 5. Return DisposeResources(disposableStack.[[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 DisposableStack(); +var wasDisposed = stack.disposed; +stack.dispose(); +var isDisposed = stack.disposed; +assert.sameValue(wasDisposed, false); +assert.sameValue(isDisposed, true); diff --git a/test/built-ins/DisposableStack/prototype/dispose/throws-error-as-is-if-only-one-error-during-disposal.js b/test/built-ins/DisposableStack/prototype/dispose/throws-error-as-is-if-only-one-error-during-disposal.js new file mode 100644 index 00000000000..4f0e2566b8e --- /dev/null +++ b/test/built-ins/DisposableStack/prototype/dispose/throws-error-as-is-if-only-one-error-during-disposal.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-disposablestack.prototype.dispose +description: Rethrows an error from a disposal as-is if it is the only error. +info: | + DisposableStack.prototype.dispose ( ) + + 1. Let disposableStack be the this value. + 2. Perform ? RequireInternalSlot(disposableStack, [[DisposableState]]). + 3. If disposableStack.[[DisposableState]] is disposed, return undefined. + 4. Set disposableStack.[[DisposableState]] to disposed. + 5. Return DisposeResources(disposableStack.[[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] +---*/ + +class MyError extends Error {} +var stack = new DisposableStack(); +stack.defer(function () { throw new MyError(); }); +stack.defer(function () {}); +assert.throws(MyError, function () { + stack.dispose(); +}); diff --git a/test/built-ins/DisposableStack/prototype/dispose/throws-suppressederror-if-multiple-errors-during-disposal.js b/test/built-ins/DisposableStack/prototype/dispose/throws-suppressederror-if-multiple-errors-during-disposal.js new file mode 100644 index 00000000000..92bf111f062 --- /dev/null +++ b/test/built-ins/DisposableStack/prototype/dispose/throws-suppressederror-if-multiple-errors-during-disposal.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-disposablestack.prototype.dispose +description: Throws multiple errors from a disposal nested in one or more SuppressedError instances. +info: | + DisposableStack.prototype.dispose ( ) + + 1. Let disposableStack be the this value. + 2. Perform ? RequireInternalSlot(disposableStack, [[DisposableState]]). + 3. If disposableStack.[[DisposableState]] is disposed, return undefined. + 4. Set disposableStack.[[DisposableState]] to disposed. + 5. Return DisposeResources(disposableStack.[[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] +---*/ + +class MyError extends Error {} +var error1 = new MyError(); +var error2 = new MyError(); +var error3 = new MyError(); +var stack = new DisposableStack(); +stack.defer(function () { throw error1; }); +stack.defer(function () { throw error2; }); +stack.defer(function () { throw error3; }); +try { + stack.dispose(); + assert(false, 'Expected stack.dispose() to have thrown an error.'); +} +catch (e) { + assert(e instanceof SuppressedError, "Expected stack.dispose() to have thrown a SuppressedError"); + assert.sameValue(e.error, error1, "Expected the outermost suppressing error to have been 'error1'"); + assert(e.suppressed instanceof SuppressedError, "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/DisposableStack/prototype/disposed/returns-false-when-not-disposed.js b/test/built-ins/DisposableStack/prototype/disposed/returns-false-when-not-disposed.js new file mode 100644 index 00000000000..ec81ef40dd4 --- /dev/null +++ b/test/built-ins/DisposableStack/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-disposablestack.prototype.disposed +description: > + Returns `false` when the DisposableStack has not yet been disposed. +info: | + get DisposableStack.prototype.disposed + + ... + 3. If disposableStack.[[DisposableState]] is disposed, return true. + 4. Otherwise, return false. + +features: [explicit-resource-management] +---*/ + +var stack = new DisposableStack(); + +assert.sameValue(stack.disposed, false); diff --git a/test/built-ins/DisposableStack/prototype/disposed/returns-true-when-disposed.js b/test/built-ins/DisposableStack/prototype/disposed/returns-true-when-disposed.js new file mode 100644 index 00000000000..47757d655da --- /dev/null +++ b/test/built-ins/DisposableStack/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-disposablestack.prototype.disposed +description: > + Returns `true` after the DisposableStack has been disposed. +info: | + get DisposableStack.prototype.disposed + + ... + 3. If disposableStack.[[DisposableState]] is disposed, return true. + 4. Otherwise, return false. + +features: [explicit-resource-management] +---*/ + +var stack = new DisposableStack(); +stack.dispose(); +assert.sameValue(stack.disposed, true); diff --git a/test/built-ins/DisposableStack/prototype/move/does-not-dispose-resources.js b/test/built-ins/DisposableStack/prototype/move/does-not-dispose-resources.js new file mode 100644 index 00000000000..892c8032373 --- /dev/null +++ b/test/built-ins/DisposableStack/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-disposablestack.prototype.move +description: Resources in the stack are not disposed when move is called. +info: | + DisposableStack.prototype.move ( ) + + 1. Let disposableStack be the this value. + 2. Perform ? RequireInternalSlot(disposableStack, [[DisposableState]]). + 3. If disposableStack.[[DisposableState]] is disposed, throw a ReferenceError exception. + 4. Let newDisposableStack be ? OrdinaryCreateFromConstructor(%DisposableStack%, "%DisposableStack.prototype%", « [[DisposableState]], [[DisposeCapability]] »). + 5. Set newDisposableStack.[[DisposableState]] to pending. + 6. Set newDisposableStack.[[DisposeCapability]] to disposableStack.[[DisposeCapability]]. + 7. Set disposableStack.[[DisposeCapability]] to NewDisposeCapability(). + 8. Set disposableStack.[[DisposableState]] to disposed. + 9. Return newDisposableStack. + +features: [explicit-resource-management] +---*/ + +var stack = new DisposableStack(); +var disposed = false; +stack.defer(() => { disposed = true; }); +stack.move(); +assert.sameValue(disposed, false); diff --git a/test/built-ins/DisposableStack/prototype/move/returns-new-disposablestack-that-contains-moved-resources.js b/test/built-ins/DisposableStack/prototype/move/returns-new-disposablestack-that-contains-moved-resources.js new file mode 100644 index 00000000000..e9135809a3f --- /dev/null +++ b/test/built-ins/DisposableStack/prototype/move/returns-new-disposablestack-that-contains-moved-resources.js @@ -0,0 +1,36 @@ +// Copyright (C) 2023 Ron Buckton. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-disposablestack.prototype.move +description: Returns a new DisposableStack that contains the resources originally contained in this stack. +info: | + DisposableStack.prototype.move ( ) + + 1. Let disposableStack be the this value. + 2. Perform ? RequireInternalSlot(disposableStack, [[DisposableState]]). + 3. If disposableStack.[[DisposableState]] is disposed, throw a ReferenceError exception. + 4. Let newDisposableStack be ? OrdinaryCreateFromConstructor(%DisposableStack%, "%DisposableStack.prototype%", « [[DisposableState]], [[DisposeCapability]] »). + 5. Set newDisposableStack.[[DisposableState]] to pending. + 6. Set newDisposableStack.[[DisposeCapability]] to disposableStack.[[DisposeCapability]]. + 7. Set disposableStack.[[DisposeCapability]] to NewDisposeCapability(). + 8. Set disposableStack.[[DisposableState]] to disposed. + 9. Return newDisposableStack. + +includes: [deepEqual.js] +features: [explicit-resource-management] +---*/ + +var stack1 = new DisposableStack(); +var disposed = []; +stack1.defer(() => { disposed.push(1); }); +stack1.defer(() => { disposed.push(2); }); + +var stack2 = stack1.move(); + +var wasDisposed = disposed.slice(); +stack2.dispose(); +var isDisposed = disposed.slice(); + +assert.deepEqual(wasDisposed, []); +assert.deepEqual(isDisposed, [2, 1]); diff --git a/test/built-ins/DisposableStack/prototype/move/returns-new-disposablestack-that-is-still-pending.js b/test/built-ins/DisposableStack/prototype/move/returns-new-disposablestack-that-is-still-pending.js new file mode 100644 index 00000000000..cec6eb586fa --- /dev/null +++ b/test/built-ins/DisposableStack/prototype/move/returns-new-disposablestack-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-disposablestack.prototype.move +description: Returns a new DisposableStack that is still pending +info: | + DisposableStack.prototype.move ( ) + + 1. Let disposableStack be the this value. + 2. Perform ? RequireInternalSlot(disposableStack, [[DisposableState]]). + 3. If disposableStack.[[DisposableState]] is disposed, throw a ReferenceError exception. + 4. Let newDisposableStack be ? OrdinaryCreateFromConstructor(%DisposableStack%, "%DisposableStack.prototype%", « [[DisposableState]], [[DisposeCapability]] »). + 5. Set newDisposableStack.[[DisposableState]] to pending. + 6. Set newDisposableStack.[[DisposeCapability]] to disposableStack.[[DisposeCapability]]. + 7. Set disposableStack.[[DisposeCapability]] to NewDisposeCapability(). + 8. Set disposableStack.[[DisposableState]] to disposed. + 9. Return newDisposableStack. + +features: [explicit-resource-management] +---*/ + +var stack1 = new DisposableStack(); +var stack2 = stack1.move(); +assert.sameValue(stack2.disposed, false); diff --git a/test/built-ins/DisposableStack/prototype/move/returns-new-disposablestack.js b/test/built-ins/DisposableStack/prototype/move/returns-new-disposablestack.js new file mode 100644 index 00000000000..fdaeb89d578 --- /dev/null +++ b/test/built-ins/DisposableStack/prototype/move/returns-new-disposablestack.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-disposablestack.prototype.move +description: Returns a new DisposableStack +info: | + DisposableStack.prototype.move ( ) + + 1. Let disposableStack be the this value. + 2. Perform ? RequireInternalSlot(disposableStack, [[DisposableState]]). + 3. If disposableStack.[[DisposableState]] is disposed, throw a ReferenceError exception. + 4. Let newDisposableStack be ? OrdinaryCreateFromConstructor(%DisposableStack%, "%DisposableStack.prototype%", « [[DisposableState]], [[DisposeCapability]] »). + 5. Set newDisposableStack.[[DisposableState]] to pending. + 6. Set newDisposableStack.[[DisposeCapability]] to disposableStack.[[DisposeCapability]]. + 7. Set disposableStack.[[DisposeCapability]] to NewDisposeCapability(). + 8. Set disposableStack.[[DisposableState]] to disposed. + 9. Return newDisposableStack. + +features: [explicit-resource-management] +---*/ + +var stack1 = new DisposableStack(); +var stack2 = stack1.move(); +assert(stack2 !== stack1, 'Expected stack2 to not be the same reference as stack1'); +assert(stack2 instanceof DisposableStack, 'Expected stack2 to be an instance of DisposableStack'); diff --git a/test/built-ins/DisposableStack/prototype/move/sets-state-to-disposed.js b/test/built-ins/DisposableStack/prototype/move/sets-state-to-disposed.js new file mode 100644 index 00000000000..c600eb44448 --- /dev/null +++ b/test/built-ins/DisposableStack/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-disposablestack.prototype.move +description: The stack's [[DisposableState]] internal slot is set to disposed. +info: | + DisposableStack.prototype.move ( ) + + 1. Let disposableStack be the this value. + 2. Perform ? RequireInternalSlot(disposableStack, [[DisposableState]]). + 3. If disposableStack.[[DisposableState]] is disposed, throw a ReferenceError exception. + 4. Let newDisposableStack be ? OrdinaryCreateFromConstructor(%DisposableStack%, "%DisposableStack.prototype%", « [[DisposableState]], [[DisposeCapability]] »). + 5. Set newDisposableStack.[[DisposableState]] to pending. + 6. Set newDisposableStack.[[DisposeCapability]] to disposableStack.[[DisposeCapability]]. + 7. Set disposableStack.[[DisposeCapability]] to NewDisposeCapability(). + 8. Set disposableStack.[[DisposableState]] to disposed. + 9. Return newDisposableStack. + +features: [explicit-resource-management] +---*/ + +var stack = new DisposableStack(); +var wasDisposed = stack.disposed; +stack.move(); +var isDisposed = stack.disposed; +assert.sameValue(wasDisposed, false); +assert.sameValue(isDisposed, true); diff --git a/test/built-ins/DisposableStack/prototype/move/still-returns-new-disposablestack-when-subclassed.js b/test/built-ins/DisposableStack/prototype/move/still-returns-new-disposablestack-when-subclassed.js new file mode 100644 index 00000000000..944b0d9c8c2 --- /dev/null +++ b/test/built-ins/DisposableStack/prototype/move/still-returns-new-disposablestack-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-disposablestack.prototype.move +description: Returns a new %DisposableStack%, even when subclassed +info: | + DisposableStack.prototype.move ( ) + + 1. Let disposableStack be the this value. + 2. Perform ? RequireInternalSlot(disposableStack, [[DisposableState]]). + 3. If disposableStack.[[DisposableState]] is disposed, throw a ReferenceError exception. + 4. Let newDisposableStack be ? OrdinaryCreateFromConstructor(%DisposableStack%, "%DisposableStack.prototype%", « [[DisposableState]], [[DisposeCapability]] »). + 5. Set newDisposableStack.[[DisposableState]] to pending. + 6. Set newDisposableStack.[[DisposeCapability]] to disposableStack.[[DisposeCapability]]. + 7. Set disposableStack.[[DisposeCapability]] to NewDisposeCapability(). + 8. Set disposableStack.[[DisposableState]] to disposed. + 9. Return newDisposableStack. + +features: [explicit-resource-management] +---*/ + +class MyDisposableStack extends DisposableStack {} + +var stack1 = new MyDisposableStack(); +var stack2 = stack1.move(); +assert(stack2 instanceof DisposableStack, 'Expected stack2 to be an instance of DisposableStack'); +assert(!(stack2 instanceof MyDisposableStack), 'Expected stack2 to not be an instance of MyDisposableStack'); diff --git a/test/built-ins/DisposableStack/prototype/move/throws-if-disposed.js b/test/built-ins/DisposableStack/prototype/move/throws-if-disposed.js new file mode 100644 index 00000000000..4d228344635 --- /dev/null +++ b/test/built-ins/DisposableStack/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-disposablestack.prototype.move +description: Throws a ReferenceError if this is disposed. +info: | + DisposableStack.prototype.move ( value ) + + 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.move(); +}, 'undefined'); diff --git a/test/built-ins/DisposableStack/prototype/use/Symbol.dispose-getter.js b/test/built-ins/DisposableStack/prototype/use/Symbol.dispose-getter.js new file mode 100644 index 00000000000..245a0a9f8d7 --- /dev/null +++ b/test/built-ins/DisposableStack/prototype/use/Symbol.dispose-getter.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-disposablestack.prototype.use +description: Invokes [Symbol.dispose] getter +info: | + DisposableStack.prototype.use ( value ) + + 1. Let disposableStack be the this value. + 2. Perform ? RequireInternalSlot(disposableStack, [[DisposableState]]). + 3. If disposableStack.[[DisposableState]] is disposed, throw a ReferenceError exception. + 4. Perform ? AddDisposableResource(disposableStack.[[DisposeCapability]], value, sync-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. + +features: [explicit-resource-management] +---*/ + +var resource = { + disposed: false, + get [Symbol.dispose]() { + return function() { + this.disposed = true; + }; + } +}; + +var stack = new DisposableStack(); +stack.use(resource); +stack.dispose(); + +assert.sameValue(resource.disposed, true, 'Expected resource to have been disposed'); diff --git a/test/built-ins/DisposableStack/prototype/use/adds-value.js b/test/built-ins/DisposableStack/prototype/use/adds-value.js new file mode 100644 index 00000000000..278e050bb92 --- /dev/null +++ b/test/built-ins/DisposableStack/prototype/use/adds-value.js @@ -0,0 +1,39 @@ +// Copyright (C) 2023 Ron Buckton. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-disposablestack.prototype.use +description: Adds a disposable resource to the stack +info: | + DisposableStack.prototype.use ( value ) + + 1. Let disposableStack be the this value. + 2. Perform ? RequireInternalSlot(disposableStack, [[DisposableState]]). + 3. If disposableStack.[[DisposableState]] is disposed, throw a ReferenceError exception. + 4. Perform ? AddDisposableResource(disposableStack.[[DisposeCapability]], value, sync-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. + +features: [explicit-resource-management] +---*/ + +var stack = new DisposableStack(); +var resource = { + disposed: false, + [Symbol.dispose]() { + this.disposed = true; + } +}; +stack.use(resource); +stack.dispose(); +assert.sameValue(resource.disposed, true, 'Expected resource to have been disposed'); diff --git a/test/built-ins/DisposableStack/prototype/use/allows-null-value.js b/test/built-ins/DisposableStack/prototype/use/allows-null-value.js new file mode 100644 index 00000000000..f4c4de6b4a2 --- /dev/null +++ b/test/built-ins/DisposableStack/prototype/use/allows-null-value.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-disposablestack.prototype.use +description: Does not throw when argument is 'null' +info: | + DisposableStack.prototype.use ( value ) + + 1. Let disposableStack be the this value. + 2. Perform ? RequireInternalSlot(disposableStack, [[DisposableState]]). + 3. If disposableStack.[[DisposableState]] is disposed, throw a ReferenceError exception. + 4. Perform ? AddDisposableResource(disposableStack.[[DisposeCapability]], value, sync-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] +---*/ + +var stack = new DisposableStack(); +stack.use(null); diff --git a/test/built-ins/DisposableStack/prototype/use/allows-undefined-value.js b/test/built-ins/DisposableStack/prototype/use/allows-undefined-value.js new file mode 100644 index 00000000000..83bccd60f5a --- /dev/null +++ b/test/built-ins/DisposableStack/prototype/use/allows-undefined-value.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-disposablestack.prototype.use +description: Does not throw when argument is 'undefined' +info: | + DisposableStack.prototype.use ( value ) + + 1. Let disposableStack be the this value. + 2. Perform ? RequireInternalSlot(disposableStack, [[DisposableState]]). + 3. If disposableStack.[[DisposableState]] is disposed, throw a ReferenceError exception. + 4. Perform ? AddDisposableResource(disposableStack.[[DisposeCapability]], value, sync-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] +---*/ + +var stack = new DisposableStack(); +stack.use(undefined); diff --git a/test/built-ins/DisposableStack/prototype/use/gets-value-Symbol.dispose-property-once.js b/test/built-ins/DisposableStack/prototype/use/gets-value-Symbol.dispose-property-once.js new file mode 100644 index 00000000000..2bde6704333 --- /dev/null +++ b/test/built-ins/DisposableStack/prototype/use/gets-value-Symbol.dispose-property-once.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-disposablestack.prototype.use +description: Only reads `[Symbol.dispose]` method once, when added. +info: | + DisposableStack.prototype.use ( value ) + + 1. Let disposableStack be the this value. + 2. Perform ? RequireInternalSlot(disposableStack, [[DisposableState]]). + 3. If disposableStack.[[DisposableState]] is disposed, throw a ReferenceError exception. + 4. Perform ? AddDisposableResource(disposableStack.[[DisposeCapability]], value, sync-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. + +features: [explicit-resource-management] +---*/ + +var stack = new DisposableStack(); +var resource = { + disposeReadCount: 0, + get [Symbol.dispose]() { + this.disposeReadCount++; + return function() { }; + } +}; +stack.use(resource); +stack.dispose(); +assert.sameValue(resource.disposeReadCount, 1, 'Expected [Symbol.dispose] to have been read only once'); diff --git a/test/built-ins/DisposableStack/prototype/use/puts-value-on-top-of-stack.js b/test/built-ins/DisposableStack/prototype/use/puts-value-on-top-of-stack.js new file mode 100644 index 00000000000..bc8472b0133 --- /dev/null +++ b/test/built-ins/DisposableStack/prototype/use/puts-value-on-top-of-stack.js @@ -0,0 +1,47 @@ +// Copyright (C) 2023 Ron Buckton. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-disposablestack.prototype.use +description: Puts value on the top of the dispose stack +info: | + DisposableStack.prototype.use ( value ) + + 1. Let disposableStack be the this value. + 2. Perform ? RequireInternalSlot(disposableStack, [[DisposableState]]). + 3. If disposableStack.[[DisposableState]] is disposed, throw a ReferenceError exception. + 4. Perform ? AddDisposableResource(disposableStack.[[DisposeCapability]], value, sync-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. + +features: [explicit-resource-management] +---*/ + +var stack = new DisposableStack(); +var disposed = []; +var resource1 = { + [Symbol.dispose]() { + disposed.push(this); + } +}; +var resource2 = { + [Symbol.dispose]() { + disposed.push(this); + } +}; +stack.use(resource1); +stack.use(resource2); +stack.dispose(); +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/DisposableStack/prototype/use/returns-value.js b/test/built-ins/DisposableStack/prototype/use/returns-value.js new file mode 100644 index 00000000000..ecd53f279cd --- /dev/null +++ b/test/built-ins/DisposableStack/prototype/use/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-disposablestack.prototype.use +description: Returns the argument provided. +info: | + DisposableStack.prototype.use ( value ) + + ... + 5. Return value. + +features: [explicit-resource-management] +---*/ + +var stack = new DisposableStack(); +var resource = { [Symbol.dispose]() { } }; +assert.sameValue(stack.use(resource), resource); +assert.sameValue(stack.use(null), null); +assert.sameValue(stack.use(undefined), undefined); diff --git a/test/built-ins/DisposableStack/prototype/use/throws-if-disposed.js b/test/built-ins/DisposableStack/prototype/use/throws-if-disposed.js new file mode 100644 index 00000000000..bd4d7a4a6ca --- /dev/null +++ b/test/built-ins/DisposableStack/prototype/use/throws-if-disposed.js @@ -0,0 +1,56 @@ +// Copyright (C) 2023 Ron Buckton. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-disposablestack.prototype.use +description: Throws a ReferenceError if this is disposed. +info: | + DisposableStack.prototype.use ( value ) + + 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.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({ [Symbol.dispose]() {} }); +}, 'disposable object'); diff --git a/test/built-ins/DisposableStack/prototype/use/throws-if-value-Symbol.dispose-property-is-null.js b/test/built-ins/DisposableStack/prototype/use/throws-if-value-Symbol.dispose-property-is-null.js new file mode 100644 index 00000000000..a410ad295a4 --- /dev/null +++ b/test/built-ins/DisposableStack/prototype/use/throws-if-value-Symbol.dispose-property-is-null.js @@ -0,0 +1,58 @@ +// Copyright (C) 2023 Ron Buckton. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-disposablestack.prototype.use +description: Throws if the argument has a null-valued Symbol.dispose property. +info: | + DisposableStack.prototype.use ( value ) + + 1. Let disposableStack be the this value. + 2. Perform ? RequireInternalSlot(disposableStack, [[DisposableState]]). + 3. If disposableStack.[[DisposableState]] is disposed, throw a ReferenceError exception. + 4. Perform ? AddDisposableResource(disposableStack.[[DisposeCapability]], value, sync-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 DisposableStack(); +assert.throws(TypeError, function() { + stack.use({ [Symbol.dispose]: null }); +}, 'true'); diff --git a/test/built-ins/DisposableStack/prototype/use/throws-if-value-Symbol.dispose-property-is-undefined.js b/test/built-ins/DisposableStack/prototype/use/throws-if-value-Symbol.dispose-property-is-undefined.js new file mode 100644 index 00000000000..361e6dff059 --- /dev/null +++ b/test/built-ins/DisposableStack/prototype/use/throws-if-value-Symbol.dispose-property-is-undefined.js @@ -0,0 +1,58 @@ +// Copyright (C) 2023 Ron Buckton. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-disposablestack.prototype.use +description: Throws if the argument has an undefined-valued Symbol.dispose property. +info: | + DisposableStack.prototype.use ( value ) + + 1. Let disposableStack be the this value. + 2. Perform ? RequireInternalSlot(disposableStack, [[DisposableState]]). + 3. If disposableStack.[[DisposableState]] is disposed, throw a ReferenceError exception. + 4. Perform ? AddDisposableResource(disposableStack.[[DisposeCapability]], value, sync-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 DisposableStack(); +assert.throws(TypeError, function() { + stack.use({ [Symbol.dispose]: undefined }); +}, 'true'); diff --git a/test/built-ins/DisposableStack/prototype/use/throws-if-value-Symbol.dispose-property-not-callable.js b/test/built-ins/DisposableStack/prototype/use/throws-if-value-Symbol.dispose-property-not-callable.js new file mode 100644 index 00000000000..2fd597cf4d7 --- /dev/null +++ b/test/built-ins/DisposableStack/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-disposablestack.prototype.use +description: Throws if the argument has a non-null, non-undefined, non-callable Symbol.dispose property. +info: | + DisposableStack.prototype.use ( value ) + + 1. Let disposableStack be the this value. + 2. Perform ? RequireInternalSlot(disposableStack, [[DisposableState]]). + 3. If disposableStack.[[DisposableState]] is disposed, throw a ReferenceError exception. + 4. Perform ? AddDisposableResource(disposableStack.[[DisposeCapability]], value, sync-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 DisposableStack(); +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/DisposableStack/prototype/use/throws-if-value-missing-Symbol.dispose.js b/test/built-ins/DisposableStack/prototype/use/throws-if-value-missing-Symbol.dispose.js new file mode 100644 index 00000000000..45f97346c9a --- /dev/null +++ b/test/built-ins/DisposableStack/prototype/use/throws-if-value-missing-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-disposablestack.prototype.use +description: Throws if the argument is an object that does not have a Symbol.dispose property. +info: | + DisposableStack.prototype.use ( value ) + + 1. Let disposableStack be the this value. + 2. Perform ? RequireInternalSlot(disposableStack, [[DisposableState]]). + 3. If disposableStack.[[DisposableState]] is disposed, throw a ReferenceError exception. + 4. Perform ? AddDisposableResource(disposableStack.[[DisposeCapability]], value, sync-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 DisposableStack(); +assert.throws(TypeError, function() { + stack.use({ }); +}, 'true'); diff --git a/test/built-ins/Symbol/asyncDispose/cross-realm.js b/test/built-ins/Symbol/asyncDispose/cross-realm.js new file mode 100644 index 00000000000..e615f20a144 --- /dev/null +++ b/test/built-ins/Symbol/asyncDispose/cross-realm.js @@ -0,0 +1,14 @@ +// Copyright (C) 2016 the V8 project authors. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. +/*--- +esid: pending +description: Value shared by all realms +info: | + Unless otherwise specified, well-known symbols values are shared by all + realms. +features: [cross-realm, explicit-resource-management] +---*/ + +var OSymbol = $262.createRealm().global.Symbol; + +assert.sameValue(Symbol.asyncDispose, OSymbol.asyncDispose); diff --git a/test/built-ins/Symbol/dispose/cross-realm.js b/test/built-ins/Symbol/dispose/cross-realm.js new file mode 100644 index 00000000000..3e599d42943 --- /dev/null +++ b/test/built-ins/Symbol/dispose/cross-realm.js @@ -0,0 +1,14 @@ +// Copyright (C) 2016 the V8 project authors. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. +/*--- +esid: pending +description: Value shared by all realms +info: | + Unless otherwise specified, well-known symbols values are shared by all + realms. +features: [cross-realm, explicit-resource-management] +---*/ + +var OSymbol = $262.createRealm().global.Symbol; + +assert.sameValue(Symbol.dispose, OSymbol.dispose); diff --git a/test/language/statements/await-using/Symbol.asyncDispose-getter.js b/test/language/statements/await-using/Symbol.asyncDispose-getter.js new file mode 100644 index 00000000000..665e8a9867a --- /dev/null +++ b/test/language/statements/await-using/Symbol.asyncDispose-getter.js @@ -0,0 +1,89 @@ +// Copyright (C) 2023 Ron Buckton. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-let-and-const-declarations-runtime-semantics-evaluation +description: Invokes [Symbol.asyncDispose] getter +info: | + RS: Evaluation + AwaitUsingDeclaration : CoverAwaitExpressionAndAwaitUsingDeclarationHead BindingList ; + + 1. Perform ? BindingEvaluation of BindingList with argument async-dispose. + 2. Return empty. + + RS: BindingEvaluation + LexicalBinding : BindingIdentifier Initializer + + ... + 5. Return ? InitializeReferencedBinding(lhs, value, hint). + + InitializeReferencedBinding ( V, W ) + + ... + 4. Return ? base.InitializeBinding(V.[[ReferencedName]], W). + + InitializeBinding ( N, V, hint ) + + ... + 2. If hint is not normal, perform ? AddDisposableResource(envRec.[[DisposeCapability]], V, hint). + ... + + 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. + + CreateDisposableResource ( V, hint [ , method ] ) + + 1. If method is not present, then + a. If V is either null or undefined, then + ... + b. Else, + i. If V is not an 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. ... + +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; + }; + } + }; + + { + await using _ = resource; + } + + assert.sameValue(resource.disposed, true, 'Expected resource to have been disposed'); +}); diff --git a/test/language/statements/await-using/Symbol.asyncDispose-method-called-with-correct-this.js b/test/language/statements/await-using/Symbol.asyncDispose-method-called-with-correct-this.js new file mode 100644 index 00000000000..3f2ac116377 --- /dev/null +++ b/test/language/statements/await-using/Symbol.asyncDispose-method-called-with-correct-this.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-block-runtime-semantics-evaluation +description: Initialized value is disposed with the correct 'this' value +info: | + RS: Evaluation + Block : { StatementList } + + ... + 5. Let blockValue be the result of evaluating StatementList. + 6. Set blockValue to DisposeResources(blockEnv.[[DisposeCapability]], blockValue). + ... + + 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). + ... + +flags: [async] +includes: [asyncHelpers.js] +features: [explicit-resource-management] +---*/ + +asyncTest(async function () { + var resource = { + disposed: false, + async [Symbol.asyncDispose]() { + assert.sameValue(this, resource); + } + }; + + { + await using _ = resource; + } +}); diff --git a/test/language/statements/await-using/Symbol.asyncDispose-method-not-async.js b/test/language/statements/await-using/Symbol.asyncDispose-method-not-async.js new file mode 100644 index 00000000000..d81b461337b --- /dev/null +++ b/test/language/statements/await-using/Symbol.asyncDispose-method-not-async.js @@ -0,0 +1,58 @@ +// Copyright (C) 2023 Ron Buckton. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-block-runtime-semantics-evaluation +description: Disposal succeeds even if [Symbol.disposeAsync] does not return a Promise. +info: | + RS: Evaluation + Block : { StatementList } + + ... + 5. Let blockValue be the result of evaluating StatementList. + 6. Set blockValue to DisposeResources(blockEnv.[[DisposeCapability]], blockValue). + ... + + 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. ... + 4. Return undefined. + +flags: [async] +includes: [asyncHelpers.js] +features: [explicit-resource-management] +---*/ + +asyncTest(async function () { + var resource = { + disposed: false, + [Symbol.asyncDispose]() { + this.disposed = true; + } + }; + + { + await using _ = resource; + } + + assert.sameValue(resource.disposed, true, 'Expected resource to have been disposed'); +}); diff --git a/test/language/statements/await-using/Symbol.dispose-getter.js b/test/language/statements/await-using/Symbol.dispose-getter.js new file mode 100644 index 00000000000..19f8e3f6f43 --- /dev/null +++ b/test/language/statements/await-using/Symbol.dispose-getter.js @@ -0,0 +1,89 @@ +// Copyright (C) 2023 Ron Buckton. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-let-and-const-declarations-runtime-semantics-evaluation +description: Invokes [Symbol.dispose] getter +info: | + RS: Evaluation + AwaitUsingDeclaration : CoverAwaitExpressionAndAwaitUsingDeclarationHead BindingList ; + + 1. Perform ? BindingEvaluation of BindingList with argument async-dispose. + 2. Return empty. + + RS: BindingEvaluation + LexicalBinding : BindingIdentifier Initializer + + ... + 5. Return ? InitializeReferencedBinding(lhs, value, hint). + + InitializeReferencedBinding ( V, W ) + + ... + 4. Return ? base.InitializeBinding(V.[[ReferencedName]], W). + + InitializeBinding ( N, V, hint ) + + ... + 2. If hint is not normal, perform ? AddDisposableResource(envRec.[[DisposeCapability]], V, hint). + ... + + 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. + + CreateDisposableResource ( V, hint [ , method ] ) + + 1. If method is not present, then + a. If V is either null or undefined, then + ... + b. Else, + i. If V is not an 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. ... + +flags: [async] +includes: [asyncHelpers.js] +features: [explicit-resource-management] +---*/ + +asyncTest(async function () { + var resource = { + disposed: false, + get [Symbol.dispose]() { + return function() { + this.disposed = true; + }; + } + }; + + { + await using _ = resource; + } + + assert.sameValue(resource.disposed, true, 'Expected resource to have been disposed'); +}); diff --git a/test/language/statements/await-using/Symbol.dispose-method-called-with-correct-this.js b/test/language/statements/await-using/Symbol.dispose-method-called-with-correct-this.js new file mode 100644 index 00000000000..71003d2e0a5 --- /dev/null +++ b/test/language/statements/await-using/Symbol.dispose-method-called-with-correct-this.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-block-runtime-semantics-evaluation +description: Initialized value is disposed with the correct 'this' value +info: | + RS: Evaluation + Block : { StatementList } + + ... + 5. Let blockValue be the result of evaluating StatementList. + 6. Set blockValue to DisposeResources(blockEnv.[[DisposeCapability]], blockValue). + ... + + 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). + ... + +flags: [async] +includes: [asyncHelpers.js] +features: [explicit-resource-management] +---*/ + +asyncTest(async function() { + var resource = { + disposed: false, + [Symbol.dispose]() { + assert.sameValue(this, resource); + } + }; + + { + await using _ = resource; + } +}); diff --git a/test/language/statements/await-using/await-using-Symbol.asyncDispose-allows-non-promise-return-value.js b/test/language/statements/await-using/await-using-Symbol.asyncDispose-allows-non-promise-return-value.js new file mode 100644 index 00000000000..1b2c95ca358 --- /dev/null +++ b/test/language/statements/await-using/await-using-Symbol.asyncDispose-allows-non-promise-return-value.js @@ -0,0 +1,47 @@ +// Copyright (C) 2023 Ron Buckton. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-disposeresources +description: > + `await using` allows a non-Promise return value from `[Symbol.asyncDispose]()` +info: | + 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 = { + [Symbol.asyncDispose]() { + } + }; + + { + await using _ = resource; + } +}); diff --git a/test/language/statements/await-using/await-using-Symbol.asyncDispose-allows-promiselike-return-value.js b/test/language/statements/await-using/await-using-Symbol.asyncDispose-allows-promiselike-return-value.js new file mode 100644 index 00000000000..1a4840e8ce6 --- /dev/null +++ b/test/language/statements/await-using/await-using-Symbol.asyncDispose-allows-promiselike-return-value.js @@ -0,0 +1,52 @@ +// Copyright (C) 2023 Ron Buckton. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-disposeresources +description: > + `await using` allows non-native `Promise`-like return value from `[Symbol.asyncDispose]()` +info: | + 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 = { + [Symbol.asyncDispose]() { + return { + then(resolve) { + resolve(); + } + }; + } + }; + + { + await using _ = resource; + } +}); diff --git a/test/language/statements/await-using/await-using-allows-null-initializer.js b/test/language/statements/await-using/await-using-allows-null-initializer.js new file mode 100644 index 00000000000..aff4f9ef5ca --- /dev/null +++ b/test/language/statements/await-using/await-using-allows-null-initializer.js @@ -0,0 +1,46 @@ +// Copyright (C) 2023 Ron Buckton. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-let-and-const-declarations-runtime-semantics-evaluation +description: Allows null in initializer of 'await using' +info: | + RS: Evaluation + AwaitUsingDeclaration : CoverAwaitExpressionAndAwaitUsingDeclarationHead BindingList ; + + 1. Perform ? BindingEvaluation of BindingList with argument async-dispose. + 2. Return empty. + + RS: BindingEvaluation + LexicalBinding : BindingIdentifier Initializer + + ... + 5. Return ? InitializeReferencedBinding(lhs, value, hint). + + InitializeReferencedBinding ( V, W ) + + ... + 4. Return ? base.InitializeBinding(V.[[ReferencedName]], W). + + InitializeBinding ( N, V, hint ) + + ... + 2. If hint is not normal, perform ? AddDisposableResource(envRec.[[DisposeCapability]], V, hint). + ... + + 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. + ... + ... + +flags: [async] +includes: [asyncHelpers.js] +features: [explicit-resource-management] +---*/ + +asyncTest(async function () { + await using x = null; +}); diff --git a/test/language/statements/await-using/await-using-allows-undefined-initializer.js b/test/language/statements/await-using/await-using-allows-undefined-initializer.js new file mode 100644 index 00000000000..ae2b7f822d1 --- /dev/null +++ b/test/language/statements/await-using/await-using-allows-undefined-initializer.js @@ -0,0 +1,46 @@ +// Copyright (C) 2023 Ron Buckton. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-let-and-const-declarations-runtime-semantics-evaluation +description: Allows undefined in initializer of 'await using' +info: | + RS: Evaluation + AwaitUsingDeclaration : CoverAwaitExpressionAndAwaitUsingDeclarationHead BindingList ; + + 1. Perform ? BindingEvaluation of BindingList with argument async-dispose. + 2. Return empty. + + RS: BindingEvaluation + LexicalBinding : BindingIdentifier Initializer + + ... + 5. Return ? InitializeReferencedBinding(lhs, value, hint). + + InitializeReferencedBinding ( V, W ) + + ... + 4. Return ? base.InitializeBinding(V.[[ReferencedName]], W). + + InitializeBinding ( N, V, hint ) + + ... + 2. If hint is not normal, perform ? AddDisposableResource(envRec.[[DisposeCapability]], V, hint). + ... + + 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. + ... + ... + +flags: [async] +includes: [asyncHelpers.js] +features: [explicit-resource-management] +---*/ + +asyncTest(async function () { + using x = undefined; +}); diff --git a/test/language/statements/await-using/await-using-does-not-imply-await-if-not-evaluated.js b/test/language/statements/await-using/await-using-does-not-imply-await-if-not-evaluated.js new file mode 100644 index 00000000000..ae9fc7b369a --- /dev/null +++ b/test/language/statements/await-using/await-using-does-not-imply-await-if-not-evaluated.js @@ -0,0 +1,102 @@ +// Copyright (C) 2023 Ron Buckton. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-let-and-const-declarations-runtime-semantics-evaluation +description: An 'await using' does not imply an Await occurs if the statement is not evaluated +info: | + RS: Evaluation + AwaitUsingDeclaration : CoverAwaitExpressionAndAwaitUsingDeclarationHead BindingList ; + + 1. Perform ? BindingEvaluation of BindingList with argument async-dispose. + 2. Return empty. + + RS: BindingEvaluation + LexicalBinding : BindingIdentifier Initializer + + ... + 5. Return ? InitializeReferencedBinding(lhs, value, hint). + + InitializeReferencedBinding ( V, W ) + + ... + 4. Return ? base.InitializeBinding(V.[[ReferencedName]], W). + + InitializeBinding ( N, V, hint ) + + ... + 2. If hint is not normal, perform ? AddDisposableResource(envRec.[[DisposeCapability]], V, hint). + ... + + 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. + ... + ... + 3. Return the DisposableResource Record { [[ResourceValue]]: V, [[Hint]]: hint, [[DisposeMethod]]: method }. + + 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 BREAK_EARLY = true; + var isRunningInSameMicrotask = true; + var wasStartedInSameMicrotask = false; + var didEvaluatePrecedingBlockStatementsInSameMicrotask = false; + var wasRunningInSameMicrotask = false; + + async function f() { + wasStartedInSameMicrotask = isRunningInSameMicrotask; + outer: { + didEvaluatePrecedingBlockStatementsInSameMicrotask = isRunningInSameMicrotask; + if (BREAK_EARLY) break outer; + await using _ = null; + } + wasRunningInSameMicrotask = isRunningInSameMicrotask; + } + + var p = f(); + isRunningInSameMicrotask = false; + await p; + + assert.sameValue(wasStartedInSameMicrotask, true, 'Expected async function containing `await using` to start in the same microtask'); + assert.sameValue(didEvaluatePrecedingBlockStatementsInSameMicrotask, true, 'Expected block statements preceding `await using` to be evaluated in the same microtask'); + assert.sameValue(wasRunningInSameMicrotask, true, 'Expected statements following the block containing unevaluated `await using` to evaluate in the same microtask'); +}); diff --git a/test/language/statements/await-using/await-using-implies-await-if-evaluated.js b/test/language/statements/await-using/await-using-implies-await-if-evaluated.js new file mode 100644 index 00000000000..354dadfbf3c --- /dev/null +++ b/test/language/statements/await-using/await-using-implies-await-if-evaluated.js @@ -0,0 +1,103 @@ +// Copyright (C) 2023 Ron Buckton. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-let-and-const-declarations-runtime-semantics-evaluation +description: An 'await using' implies an Await occurs if the statement is evaluated, even if all initializers are 'null' or 'undefined' +info: | + RS: Evaluation + AwaitUsingDeclaration : CoverAwaitExpressionAndAwaitUsingDeclarationHead BindingList ; + + 1. Perform ? BindingEvaluation of BindingList with argument async-dispose. + 2. Return empty. + + RS: BindingEvaluation + LexicalBinding : BindingIdentifier Initializer + + ... + 5. Return ? InitializeReferencedBinding(lhs, value, hint). + + InitializeReferencedBinding ( V, W ) + + ... + 4. Return ? base.InitializeBinding(V.[[ReferencedName]], W). + + InitializeBinding ( N, V, hint ) + + ... + 2. If hint is not normal, perform ? AddDisposableResource(envRec.[[DisposeCapability]], V, hint). + ... + + 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. + ... + ... + 3. Return the DisposableResource Record { [[ResourceValue]]: V, [[Hint]]: hint, [[DisposeMethod]]: method }. + + 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 isRunningInSameMicrotask = true; + var wasStartedInSameMicrotask = false; + var didEvaluatePrecedingBlockStatementsInSameMicrotask = false; + var didEvaluateFollowingBlockStatementsInSameMicrotask = false; + var wasRunningInSameMicrotask = false; + + async function f() { + wasStartedInSameMicrotask = isRunningInSameMicrotask; + { + didEvaluatePrecedingBlockStatementsInSameMicrotask = isRunningInSameMicrotask; + await using _ = null; + didEvaluateFollowingBlockStatementsInSameMicrotask = isRunningInSameMicrotask; + } + wasRunningInSameMicrotask = isRunningInSameMicrotask; + } + + var p = f(); + isRunningInSameMicrotask = false; + await p; + + assert.sameValue(wasStartedInSameMicrotask, true, 'Expected async function containing `await using` to start in the same microtask'); + assert.sameValue(didEvaluatePrecedingBlockStatementsInSameMicrotask, true, 'Expected block statements preceding `await using` to be evaluated in the same microtask'); + assert.sameValue(didEvaluateFollowingBlockStatementsInSameMicrotask, true, 'Expected block statements following `await using` to be evaluated in the same microtask'); + assert.sameValue(wasRunningInSameMicrotask, false, 'Expected statements following the block containing evaluated `await using` to evaluate in a different microtask'); +}); diff --git a/test/language/statements/await-using/block-local-closure-get-before-initialization.js b/test/language/statements/await-using/block-local-closure-get-before-initialization.js new file mode 100644 index 00000000000..ac70f432941 --- /dev/null +++ b/test/language/statements/await-using/block-local-closure-get-before-initialization.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-declarative-environment-records-getbindingvalue-n-s +description: > + await using: block local closure [[Get]] before initialization. + (TDZ, Temporal Dead Zone) +flags: [async] +includes: [asyncHelpers.js] +features: [explicit-resource-management] +---*/ + +asyncTest(async function () { + function f() { return x + 1; } + + assert.throws(ReferenceError, function() { + f(); + }); + + await using x = null; +}); diff --git a/test/language/statements/await-using/block-local-use-before-initialization-in-declaration-statement.js b/test/language/statements/await-using/block-local-use-before-initialization-in-declaration-statement.js new file mode 100644 index 00000000000..a059f4b246b --- /dev/null +++ b/test/language/statements/await-using/block-local-use-before-initialization-in-declaration-statement.js @@ -0,0 +1,18 @@ +// Copyright (C) 2023 Ron Buckton. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-declarative-environment-records-getbindingvalue-n-s +description: > + await using: block local use before initialization in declaration statement. + (TDZ, Temporal Dead Zone) +flags: [async] +includes: [asyncHelpers.js] +features: [explicit-resource-management] +---*/ + +asyncTest(async function () { + await assert.throwsAsync(ReferenceError, async function() { + await using x = x + 1; + }); +}); diff --git a/test/language/statements/await-using/block-local-use-before-initialization-in-prior-statement.js b/test/language/statements/await-using/block-local-use-before-initialization-in-prior-statement.js new file mode 100644 index 00000000000..769084b052c --- /dev/null +++ b/test/language/statements/await-using/block-local-use-before-initialization-in-prior-statement.js @@ -0,0 +1,18 @@ +// Copyright (C) 2023 Ron Buckton. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-declarative-environment-records-getbindingvalue-n-s +description: > + await using: block local use before initialization in prior statement. + (TDZ, Temporal Dead Zone) +flags: [async] +includes: [asyncHelpers.js] +features: [explicit-resource-management] +---*/ + +asyncTest(async function () { + await assert.throwsAsync(ReferenceError, async function() { + x; await using x = null; + }); +}); diff --git a/test/language/statements/await-using/fn-name-arrow.js b/test/language/statements/await-using/fn-name-arrow.js new file mode 100644 index 00000000000..b9d10389311 --- /dev/null +++ b/test/language/statements/await-using/fn-name-arrow.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-let-and-const-declarations-runtime-semantics-evaluation +description: Assignment of function `name` attribute (ArrowFunction) +info: | + LexicalBinding : BindingIdentifier Initializer + + ... + 3. If IsAnonymousFunctionDefinition(Initializer) is true, then + a. Let value be NamedEvaluation of Initializer with argument bindingId + +flags: [async] +includes: [propertyHelper.js, asyncHelpers.js] +features: [explicit-resource-management] +---*/ + +// NOTE: only way to verify is to patch `Function.prototype` so as not to trigger a TypeError from AddDisposableResource +Function.prototype[Symbol.dispose] = function () {} +asyncTest(async function () { + await using arrow = () => {}; + + assert.sameValue(arrow.name, 'arrow'); + verifyNotEnumerable(arrow, 'name'); + verifyNotWritable(arrow, 'name'); + verifyConfigurable(arrow, 'name'); +}); diff --git a/test/language/statements/await-using/fn-name-class.js b/test/language/statements/await-using/fn-name-class.js new file mode 100644 index 00000000000..61b862164ac --- /dev/null +++ b/test/language/statements/await-using/fn-name-class.js @@ -0,0 +1,31 @@ +// Copyright (C) 2023 Ron Buckton. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-let-and-const-declarations-runtime-semantics-evaluation +description: Assignment of function `name` attribute (ClassExpression) +info: | + LexicalBinding : BindingIdentifier Initializer + + ... + 3. If IsAnonymousFunctionDefinition(Initializer) is true, then + a. Let value be NamedEvaluation of Initializer with argument bindingId + +flags: [async] +includes: [propertyHelper.js, asyncHelpers.js] +features: [class, explicit-resource-management] +---*/ + +asyncTest(async function () { + await using xCls = class x { static async [Symbol.asyncDispose]() {} }; + await using cls = class { static async [Symbol.asyncDispose]() {} }; + await using xCls2 = class { static name() {} static async [Symbol.asyncDispose]() {} }; + + assert.notSameValue(xCls.name, 'xCls'); + assert.notSameValue(xCls2.name, 'xCls2'); + + assert.sameValue(cls.name, 'cls'); + verifyNotEnumerable(cls, 'name'); + verifyNotWritable(cls, 'name'); + verifyConfigurable(cls, 'name'); +}); diff --git a/test/language/statements/await-using/fn-name-cover.js b/test/language/statements/await-using/fn-name-cover.js new file mode 100644 index 00000000000..224bcd54f34 --- /dev/null +++ b/test/language/statements/await-using/fn-name-cover.js @@ -0,0 +1,32 @@ +// Copyright (C) 2023 Ron Buckton. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-let-and-const-declarations-runtime-semantics-evaluation +description: > + Assignment of function `name` attribute (CoverParenthesizedExpression) +info: | + LexicalBinding : BindingIdentifier Initializer + + ... + 3. If IsAnonymousFunctionDefinition(Initializer) is true, then + a. Let value be NamedEvaluation of Initializer with argument bindingId + +flags: [async] +includes: [propertyHelper.js, asyncHelpers.js] +features: [explicit-resource-management] +---*/ + +// NOTE: only way to verify is to patch `Function.prototype` so as not to trigger a TypeError from AddDisposableResource +Function.prototype[Symbol.dispose] = function () {} +asyncTest(async function () { + await using xCover = (0, function() {}); + await using cover = (function() {}); + + assert(xCover.name !== 'xCover'); + + assert.sameValue(cover.name, 'cover'); + verifyNotEnumerable(cover, 'name'); + verifyNotWritable(cover, 'name'); + verifyConfigurable(cover, 'name'); +}); diff --git a/test/language/statements/await-using/fn-name-fn.js b/test/language/statements/await-using/fn-name-fn.js new file mode 100644 index 00000000000..749c3e2e027 --- /dev/null +++ b/test/language/statements/await-using/fn-name-fn.js @@ -0,0 +1,31 @@ +// Copyright (C) 2023 Ron Buckton. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-let-and-const-declarations-runtime-semantics-evaluation +description: Assignment of function `name` attribute (FunctionExpression) +info: | + LexicalBinding : BindingIdentifier Initializer + + ... + 3. If IsAnonymousFunctionDefinition(Initializer) is true, then + a. Let value be NamedEvaluation of Initializer with argument bindingId + +flags: [async] +includes: [propertyHelper.js, asyncHelpers.js] +features: [explicit-resource-management] +---*/ + +// NOTE: only way to verify is to patch `Function.prototype` so as not to trigger a TypeError from AddDisposableResource +Function.prototype[Symbol.dispose] = function () {} +asyncTest(async function() { + await using xFn = function x() {}; + await using fn = function() {}; + + assert(xFn.name !== 'xFn'); + + assert.sameValue(fn.name, 'fn'); + verifyNotEnumerable(fn, 'name'); + verifyNotWritable(fn, 'name'); + verifyConfigurable(fn, 'name'); +}); diff --git a/test/language/statements/await-using/fn-name-gen.js b/test/language/statements/await-using/fn-name-gen.js new file mode 100644 index 00000000000..929b2bedc1e --- /dev/null +++ b/test/language/statements/await-using/fn-name-gen.js @@ -0,0 +1,31 @@ +// Copyright (C) 2023 Ron Buckton. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-let-and-const-declarations-runtime-semantics-evaluation +description: Assignment of function `name` attribute (GeneratorExpression) +info: | + LexicalBinding : BindingIdentifier Initializer + + ... + 3. If IsAnonymousFunctionDefinition(Initializer) is true, then + a. Let value be NamedEvaluation of Initializer with argument bindingId + +flags: [async] +includes: [propertyHelper.js, asyncHelpers.js] +features: [generators,explicit-resource-management] +---*/ + +// NOTE: only way to verify is to patch `Function.prototype` so as not to trigger a TypeError from AddDisposableResource +Function.prototype[Symbol.dispose] = function () {} +asyncTest(async function() { + await using xGen = function* x() {}; + await using gen = function*() {}; + + assert(xGen.name !== 'xGen'); + + assert.sameValue(gen.name, 'gen'); + verifyNotEnumerable(gen, 'name'); + verifyNotWritable(gen, 'name'); + verifyConfigurable(gen, 'name'); +}); diff --git a/test/language/statements/await-using/function-local-closure-get-before-initialization.js b/test/language/statements/await-using/function-local-closure-get-before-initialization.js new file mode 100644 index 00000000000..f34a7a85f94 --- /dev/null +++ b/test/language/statements/await-using/function-local-closure-get-before-initialization.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-declarative-environment-records-getbindingvalue-n-s +description: > + await using: function local closure [[Get]] before initialization. + (TDZ, Temporal Dead Zone) + +flags: [async] +includes: [asyncHelpers.js] +features: [explicit-resource-management] +---*/ + +asyncTest(async function() { + function f() { return x + 1; } + + assert.throws(ReferenceError, function() { + f(); + }); + + await using x = null; +}); diff --git a/test/language/statements/await-using/function-local-use-before-initialization-in-declaration-statement.js b/test/language/statements/await-using/function-local-use-before-initialization-in-declaration-statement.js new file mode 100644 index 00000000000..c8988e3bef2 --- /dev/null +++ b/test/language/statements/await-using/function-local-use-before-initialization-in-declaration-statement.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-declarative-environment-records-getbindingvalue-n-s +description: > + await using: function local use before initialization in declaration statement. + (TDZ, Temporal Dead Zone) +flags: [async] +includes: [asyncHelpers.js] +features: [explicit-resource-management] +---*/ +asyncTest(async function () { + await assert.throwsAsync(ReferenceError, async function() { + await using x = x + 1; + }); +}); diff --git a/test/language/statements/await-using/function-local-use-before-initialization-in-prior-statement.js b/test/language/statements/await-using/function-local-use-before-initialization-in-prior-statement.js new file mode 100644 index 00000000000..c7081a5e600 --- /dev/null +++ b/test/language/statements/await-using/function-local-use-before-initialization-in-prior-statement.js @@ -0,0 +1,18 @@ +// Copyright (C) 2023 Ron Buckton. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-declarative-environment-records-getbindingvalue-n-s +description: > + await using: function local use before initialization in prior statement. + (TDZ, Temporal Dead Zone) +flags: [async] +includes: [asyncHelpers.js] +features: [explicit-resource-management] +---*/ + +asyncTest(async function () { + await assert.throwsAsync(ReferenceError, async function() { + x; await using x = null; + }); +}); diff --git a/test/language/statements/await-using/gets-initializer-Symbol.asyncDispose-property-once.js b/test/language/statements/await-using/gets-initializer-Symbol.asyncDispose-property-once.js new file mode 100644 index 00000000000..fefd6f35bc5 --- /dev/null +++ b/test/language/statements/await-using/gets-initializer-Symbol.asyncDispose-property-once.js @@ -0,0 +1,88 @@ +// Copyright (C) 2023 Ron Buckton. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-let-and-const-declarations-runtime-semantics-evaluation +description: Only reads `[Symbol.asyncDispose]` method once, when initialized. +info: | + RS: Evaluation + AwaitUsingDeclaration : CoverAwaitExpressionAndAwaitUsingDeclarationHead BindingList ; + + 1. Perform ? BindingEvaluation of BindingList with argument async-dispose. + 2. Return empty. + + RS: BindingEvaluation + BindingList : BindingList , LexicalBinding + + 1. Perform ? BindingEvaluation of BindingList with argument hint. + 2. Perform ? BindingEvaluation of LexicalBinding with argument hint. + 3. Return unused. + + LexicalBinding : BindingIdentifier Initializer + + ... + 5. Return ? InitializeReferencedBinding(lhs, value, hint). + + InitializeReferencedBinding ( V, W ) + + ... + 4. Return ? base.InitializeBinding(V.[[ReferencedName]], W). + + InitializeBinding ( N, V, hint ) + + ... + 2. If hint is not normal, perform ? AddDisposableResource(envRec.[[DisposeCapability]], V, hint). + ... + + 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 = { + asyncDisposeReadCount: 0, + get [Symbol.asyncDispose]() { + this.asyncDisposeReadCount++; + return async function() { }; + } + }; + + { + await using _ = resource; + } + + assert.sameValue(resource.asyncDisposeReadCount, 1, 'Expected [Symbol.asyncDispose] to have been read only once'); +}); diff --git a/test/language/statements/await-using/gets-initializer-Symbol.dispose-after-Symbol.asyncDispose-is-null.js b/test/language/statements/await-using/gets-initializer-Symbol.dispose-after-Symbol.asyncDispose-is-null.js new file mode 100644 index 00000000000..3f7b8e8d46b --- /dev/null +++ b/test/language/statements/await-using/gets-initializer-Symbol.dispose-after-Symbol.asyncDispose-is-null.js @@ -0,0 +1,92 @@ +// Copyright (C) 2023 Ron Buckton. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-let-and-const-declarations-runtime-semantics-evaluation +description: Reads `[Symbol.dispose]` method if `[Symbol.asyncDispose]` is null +info: | + RS: Evaluation + AwaitUsingDeclaration : CoverAwaitExpressionAndAwaitUsingDeclarationHead BindingList ; + + 1. Perform ? BindingEvaluation of BindingList with argument async-dispose. + 2. Return empty. + + RS: BindingEvaluation + BindingList : BindingList , LexicalBinding + + 1. Perform ? BindingEvaluation of BindingList with argument hint. + 2. Perform ? BindingEvaluation of LexicalBinding with argument hint. + 3. Return unused. + + LexicalBinding : BindingIdentifier Initializer + + ... + 5. Return ? InitializeReferencedBinding(lhs, value, hint). + + InitializeReferencedBinding ( V, W ) + + ... + 4. Return ? base.InitializeBinding(V.[[ReferencedName]], W). + + InitializeBinding ( N, V, hint ) + + ... + 2. If hint is not normal, perform ? AddDisposableResource(envRec.[[DisposeCapability]], V, hint). + ... + + 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, deepEqual.js] +features: [explicit-resource-management] +---*/ + +asyncTest(async function () { + var order = []; + var resource = { + get [Symbol.asyncDispose]() { + order.push('Symbol.asyncDispose'); + return null; + }, + get [Symbol.dispose]() { + order.push('Symbol.dispose'); + return function() { }; + } + }; + + { + await using _ = resource; + } + + assert.deepEqual(order, ['Symbol.asyncDispose', 'Symbol.dispose'], 'Expected [Symbol.dispose] to have been read after [Symbol.asyncDispose] returns null'); +}); diff --git a/test/language/statements/await-using/gets-initializer-Symbol.dispose-after-Symbol.asyncDispose-is-undefined.js b/test/language/statements/await-using/gets-initializer-Symbol.dispose-after-Symbol.asyncDispose-is-undefined.js new file mode 100644 index 00000000000..466d17be6d1 --- /dev/null +++ b/test/language/statements/await-using/gets-initializer-Symbol.dispose-after-Symbol.asyncDispose-is-undefined.js @@ -0,0 +1,92 @@ +// Copyright (C) 2023 Ron Buckton. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-let-and-const-declarations-runtime-semantics-evaluation +description: Reads `[Symbol.dispose]` method if `[Symbol.asyncDispose]` is undefined. +info: | + RS: Evaluation + AwaitUsingDeclaration : CoverAwaitExpressionAndAwaitUsingDeclarationHead BindingList ; + + 1. Perform ? BindingEvaluation of BindingList with argument async-dispose. + 2. Return empty. + + RS: BindingEvaluation + BindingList : BindingList , LexicalBinding + + 1. Perform ? BindingEvaluation of BindingList with argument hint. + 2. Perform ? BindingEvaluation of LexicalBinding with argument hint. + 3. Return unused. + + LexicalBinding : BindingIdentifier Initializer + + ... + 5. Return ? InitializeReferencedBinding(lhs, value, hint). + + InitializeReferencedBinding ( V, W ) + + ... + 4. Return ? base.InitializeBinding(V.[[ReferencedName]], W). + + InitializeBinding ( N, V, hint ) + + ... + 2. If hint is not normal, perform ? AddDisposableResource(envRec.[[DisposeCapability]], V, hint). + ... + + 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, deepEqual.js] +features: [explicit-resource-management] +---*/ + +asyncTest(async function () { + var order = []; + var resource = { + get [Symbol.asyncDispose]() { + order.push('Symbol.asyncDispose'); + return undefined; + }, + get [Symbol.dispose]() { + order.push('Symbol.dispose'); + return function() { }; + } + }; + + { + await using _ = resource; + } + + assert.deepEqual(order, ['Symbol.asyncDispose', 'Symbol.dispose'], 'Expected [Symbol.dispose] to have been read after [Symbol.asyncDispose] returns undefined'); +}); diff --git a/test/language/statements/await-using/gets-initializer-Symbol.dispose-property-once.js b/test/language/statements/await-using/gets-initializer-Symbol.dispose-property-once.js new file mode 100644 index 00000000000..4ef0f130512 --- /dev/null +++ b/test/language/statements/await-using/gets-initializer-Symbol.dispose-property-once.js @@ -0,0 +1,88 @@ +// Copyright (C) 2023 Ron Buckton. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-let-and-const-declarations-runtime-semantics-evaluation +description: Only reads `[Symbol.dispose]` method once, when initialized. +info: | + RS: Evaluation + AwaitUsingDeclaration : CoverAwaitExpressionAndAwaitUsingDeclarationHead BindingList ; + + 1. Perform ? BindingEvaluation of BindingList with argument async-dispose. + 2. Return empty. + + RS: BindingEvaluation + BindingList : BindingList , LexicalBinding + + 1. Perform ? BindingEvaluation of BindingList with argument hint. + 2. Perform ? BindingEvaluation of LexicalBinding with argument hint. + 3. Return unused. + + LexicalBinding : BindingIdentifier Initializer + + ... + 5. Return ? InitializeReferencedBinding(lhs, value, hint). + + InitializeReferencedBinding ( V, W ) + + ... + 4. Return ? base.InitializeBinding(V.[[ReferencedName]], W). + + InitializeBinding ( N, V, hint ) + + ... + 2. If hint is not normal, perform ? AddDisposableResource(envRec.[[DisposeCapability]], V, hint). + ... + + 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 = { + disposeReadCount: 0, + get [Symbol.dispose]() { + this.disposeReadCount++; + return function() { }; + } + }; + + { + await using _ = resource; + } + + assert.sameValue(resource.disposeReadCount, 1, 'Expected [Symbol.dispose] to have been read only once'); +}); diff --git a/test/language/statements/await-using/gets-initializer-does-not-read-Symbol.dispose-if-Symbol.asyncDispose-exists.js b/test/language/statements/await-using/gets-initializer-does-not-read-Symbol.dispose-if-Symbol.asyncDispose-exists.js new file mode 100644 index 00000000000..261638c3cb7 --- /dev/null +++ b/test/language/statements/await-using/gets-initializer-does-not-read-Symbol.dispose-if-Symbol.asyncDispose-exists.js @@ -0,0 +1,92 @@ +// Copyright (C) 2023 Ron Buckton. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-let-and-const-declarations-runtime-semantics-evaluation +description: Does not read `[Symbol.dispose]` if `[Symbol.asyncDispose]` is neither null or undefined. +info: | + RS: Evaluation + AwaitUsingDeclaration : CoverAwaitExpressionAndAwaitUsingDeclarationHead BindingList ; + + 1. Perform ? BindingEvaluation of BindingList with argument async-dispose. + 2. Return empty. + + RS: BindingEvaluation + BindingList : BindingList , LexicalBinding + + 1. Perform ? BindingEvaluation of BindingList with argument hint. + 2. Perform ? BindingEvaluation of LexicalBinding with argument hint. + 3. Return unused. + + LexicalBinding : BindingIdentifier Initializer + + ... + 5. Return ? InitializeReferencedBinding(lhs, value, hint). + + InitializeReferencedBinding ( V, W ) + + ... + 4. Return ? base.InitializeBinding(V.[[ReferencedName]], W). + + InitializeBinding ( N, V, hint ) + + ... + 2. If hint is not normal, perform ? AddDisposableResource(envRec.[[DisposeCapability]], V, hint). + ... + + 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, deepEqual.js] +features: [explicit-resource-management] +---*/ + +asyncTest(async function () { + var order = []; + var resource = { + get [Symbol.asyncDispose]() { + order.push('Symbol.asyncDispose'); + return async function() {}; + }, + get [Symbol.dispose]() { + order.push('Symbol.dispose'); + return function() { }; + } + }; + + { + await using _ = resource; + } + + assert.deepEqual(order, ['Symbol.asyncDispose'], 'Expected [Symbol.dispose] to not have been read'); +}); diff --git a/test/language/statements/await-using/global-closure-get-before-initialization.js b/test/language/statements/await-using/global-closure-get-before-initialization.js new file mode 100644 index 00000000000..fd72301de80 --- /dev/null +++ b/test/language/statements/await-using/global-closure-get-before-initialization.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-declarative-environment-records-getbindingvalue-n-s +description: > + await using: global closure [[Get]] before initialization. + (TDZ, Temporal Dead Zone) +flags: [async] +includes: [asyncHelpers.js] +features: [explicit-resource-management] +---*/ + +asyncTest(async function() { + function f() { return x + 1; } + + assert.throws(ReferenceError, function() { + f(); + }); + + await using x = null; +}); diff --git a/test/language/statements/await-using/global-use-before-initialization-in-declaration-statement.js b/test/language/statements/await-using/global-use-before-initialization-in-declaration-statement.js new file mode 100644 index 00000000000..db3a9c53e27 --- /dev/null +++ b/test/language/statements/await-using/global-use-before-initialization-in-declaration-statement.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-declarative-environment-records-getbindingvalue-n-s +description: > + await using: global use before initialization in declaration statement. + (TDZ, Temporal Dead Zone) +negative: + phase: runtime + type: ReferenceError +flags: [async] +includes: [asyncHelpers.js] +features: [explicit-resource-management] +---*/ + +asyncTest(async function () { + await using x = x + 1; +}); diff --git a/test/language/statements/await-using/global-use-before-initialization-in-prior-statement.js b/test/language/statements/await-using/global-use-before-initialization-in-prior-statement.js new file mode 100644 index 00000000000..2ddf9bf6718 --- /dev/null +++ b/test/language/statements/await-using/global-use-before-initialization-in-prior-statement.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-declarative-environment-records-getbindingvalue-n-s +description: > + await using: global use before initialization in prior statement. + (TDZ, Temporal Dead Zone) +negative: + phase: runtime + type: ReferenceError +flags: [async] +includes: [asyncHelpers.js] +features: [explicit-resource-management] +---*/ + +asyncTest(async function() { + x; await using x = null; +}); diff --git a/test/language/statements/await-using/initializer-Symbol.asyncDispose-called-at-end-of-asyncfunctionbody.js b/test/language/statements/await-using/initializer-Symbol.asyncDispose-called-at-end-of-asyncfunctionbody.js new file mode 100644 index 00000000000..ca9cb62130f --- /dev/null +++ b/test/language/statements/await-using/initializer-Symbol.asyncDispose-called-at-end-of-asyncfunctionbody.js @@ -0,0 +1,105 @@ +// Copyright (C) 2023 Ron Buckton. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-asyncblockstart +description: Initialized value is disposed at end of AsyncFunctionBody +info: | + AsyncBlockStart ( promiseCapability, asyncBody, asyncContext ) + + 1. Assert: promiseCapability is a PromiseCapability Record. + 2. Let runningContext be the running execution context. + 3. Let closure be a new Abstract Closure with no parameters that captures promiseCapability and asyncBody and performs the following steps when called: + a. Let acAsyncContext be the running execution context. + b. Let result be Completion(Evaluation of asyncBody). + c. Assert: If we return here, the async function either threw an exception or performed an implicit or explicit return; all awaiting is done. + d. Remove acAsyncContext from the execution context stack and restore the execution context that is at the top of the execution context stack as the running execution context. + e. Let env be acAsyncContext's LexicalEnvironment. + f. Set result to DisposeResources(env.[[DisposeCapability]], result). + g. If result.[[Type]] is normal, then + i. Perform ! Call(promiseCapability.[[Resolve]], undefined, « undefined »). + h. Else if result.[[Type]] is return, then + i. Perform ! Call(promiseCapability.[[Resolve]], undefined, « result.[[Value]] »). + i. Else, + i. Assert: result.[[Type]] is throw. + ii. Perform ! Call(promiseCapability.[[Reject]], undefined, « result.[[Value]] »). + j. Return unused. + 4. Set the code evaluation state of asyncContext such that when evaluation is resumed for that execution context, closure will be called with no arguments. + 5. Push asyncContext onto the execution context stack; asyncContext is now the running execution context. + 6. Resume the suspended evaluation of asyncContext. Let result be the value returned by the resumed computation. + 7. Assert: When we return here, asyncContext has already been removed from the execution context stack and runningContext is the currently running execution context. + 8. Assert: result is a normal completion with a value of unused. The possible sources of this value are Await or, if the async function doesn't await anything, step 3.h above. + 9. Return unused. + + 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, + async [Symbol.asyncDispose]() { + this.disposed = true; + } + }; + + var releaseF1; + var suspendFPromise1 = new Promise(function (resolve) { releaseF1 = resolve; }); + + var releaseBody; + var suspendBodyPromise = new Promise(function (resolve) { releaseBody = resolve; }); + + var releaseF2; + var suspendFPromise2 = new Promise(function (resolve) { releaseF2 = resolve; }); + + async function f() { + await using _ = resource; + await suspendFPromise1; + releaseBody(); + await suspendFPromise2; + } + + var resultPromise = f(); + + var wasDisposedWhileSuspended1 = resource.disposed; + + releaseF1(); + await suspendBodyPromise; + + var wasDisposedWhileSuspended2 = resource.disposed; + + releaseF2(); + await resultPromise; + + var isDisposedAfterCompleted = resource.disposed; + + assert.sameValue(wasDisposedWhileSuspended1, false, 'Expected resource to not have been disposed while async function is suspended during await'); + assert.sameValue(wasDisposedWhileSuspended2, false, 'Expected resource to not have been disposed while async function is suspended during await'); + assert.sameValue(isDisposedAfterCompleted, true, 'Expected resource to have been disposed after async function completed'); +}); diff --git a/test/language/statements/await-using/initializer-Symbol.asyncDispose-called-at-end-of-asyncgeneratorbody.js b/test/language/statements/await-using/initializer-Symbol.asyncDispose-called-at-end-of-asyncgeneratorbody.js new file mode 100644 index 00000000000..f21e7b71022 --- /dev/null +++ b/test/language/statements/await-using/initializer-Symbol.asyncDispose-called-at-end-of-asyncgeneratorbody.js @@ -0,0 +1,107 @@ +// Copyright (C) 2023 Ron Buckton. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-asyncgeneratorstart +description: Initialized value is disposed at end of AsyncGeneratorBody +info: | + AsyncGeneratorStart ( generator, generatorBody ) + + 1. Assert: generator.[[AsyncGeneratorState]] is undefined. + 2. Let genContext be the running execution context. + 3. Set the Generator component of genContext to generator. + 4. Let closure be a new Abstract Closure with no parameters that captures generatorBody and performs the following steps when called: + a. Let acGenContext be the running execution context. + b. Let acGenerator be the Generator component of acGenContext. + c. If generatorBody is a Parse Node, then + i. Let result be Completion(Evaluation of generatorBody). + d. Else, + i. Assert: generatorBody is an Abstract Closure with no parameters. + ii. Let result be Completion(generatorBody()). + e. Assert: If we return here, the async generator either threw an exception or performed either an implicit or explicit return. + f. Remove acGenContext from the execution context stack and restore the execution context that is at the top of the execution context stack as the running execution context. + g. Set acGenerator.[[AsyncGeneratorState]] to completed. + h. Let env be genContext's LexicalEnvironment. + i. If env is not undefined, then + i. Assert: env is a Declarative Environment Record + ii. Set result to DisposeResources(env.[[DisposeCapability]], result). + h. If result.[[Type]] is normal, set result to NormalCompletion(undefined). + i. If result.[[Type]] is return, set result to NormalCompletion(result.[[Value]]). + j. Perform AsyncGeneratorCompleteStep(acGenerator, result, true). + k. Perform AsyncGeneratorDrainQueue(acGenerator). + l. Return undefined. + 5. Set the code evaluation state of genContext such that when evaluation is resumed for that execution context, closure will be called with no arguments. + 6. Set generator.[[AsyncGeneratorContext]] to genContext. + 7. Set generator.[[AsyncGeneratorState]] to suspendedStart. + 8. Set generator.[[AsyncGeneratorQueue]] to a new empty List. + 9. Return unused. + + 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, + async [Symbol.asyncDispose]() { + this.disposed = true; + } + }; + + var releaseF; + var suspendFPromise = new Promise(function (resolve) { releaseF = resolve; }); + + async function * f() { + await using _ = resource; + yield; + await suspendFPromise; + } + + var g = f(); + + var wasDisposedBeforeAsyncGeneratorStarted = resource.disposed; + + await g.next(); + + var wasDisposedWhileSuspendedForYield = resource.disposed; + + var nextPromise = g.next(); + + var wasDisposedWhileSuspendedForAwait = resource.disposed; + + releaseF(); + await nextPromise; + + var isDisposedAfterCompleted = resource.disposed; + + assert.sameValue(wasDisposedBeforeAsyncGeneratorStarted, false, 'Expected resource to not have been disposed prior to async generator start'); + assert.sameValue(wasDisposedWhileSuspendedForYield, false, 'Expected resource to not have been disposed while async generator function is suspended for yield'); + assert.sameValue(wasDisposedWhileSuspendedForAwait, false, 'Expected resource to not have been disposed while async generator function is suspended during await'); + assert.sameValue(isDisposedAfterCompleted, true, 'Expected resource to have been disposed after async generator function completed'); +}); diff --git a/test/language/statements/await-using/initializer-Symbol.asyncDispose-called-at-end-of-block.js b/test/language/statements/await-using/initializer-Symbol.asyncDispose-called-at-end-of-block.js new file mode 100644 index 00000000000..7063accc43e --- /dev/null +++ b/test/language/statements/await-using/initializer-Symbol.asyncDispose-called-at-end-of-block.js @@ -0,0 +1,58 @@ +// Copyright (C) 2023 Ron Buckton. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-block-runtime-semantics-evaluation +description: Initialized value is disposed at end of Block +info: | + RS: Evaluation + Block : { StatementList } + + ... + 5. Let blockValue be the result of evaluating StatementList. + 6. Set blockValue to DisposeResources(blockEnv.[[DisposeCapability]], blockValue). + ... + + 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, + async [Symbol.asyncDispose]() { + this.disposed = true; + } + }; + + { + await using _ = resource; + } + + assert.sameValue(resource.disposed, true, 'Expected resource to have been disposed'); +}); diff --git a/test/language/statements/await-using/initializer-Symbol.asyncDispose-called-at-end-of-each-iteration-of-forofstatement.js b/test/language/statements/await-using/initializer-Symbol.asyncDispose-called-at-end-of-each-iteration-of-forofstatement.js new file mode 100644 index 00000000000..d56df22929f --- /dev/null +++ b/test/language/statements/await-using/initializer-Symbol.asyncDispose-called-at-end-of-each-iteration-of-forofstatement.js @@ -0,0 +1,84 @@ +// Copyright (C) 2023 Ron Buckton. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-runtime-semantics-forin-div-ofbodyevaluation-lhs-stmt-iterator-lhskind-labelset +description: Initialized value is disposed at end of each iteration of ForOfStatement +info: | + ForIn/OfBodyEvaluation ( lhs, stmt, iteratorRecord, iterationKind, lhsKind, labelSet [ , iteratorKind ] ) + + 1. If iteratorKind is not present, set iteratorKind to sync. + 2. Let oldEnv be the running execution context's LexicalEnvironment. + 3. Let V be undefined. + 4. If IsAwaitUsingDeclaration of lhs is true, then + a. Let hint be async-dispose. + 5. Else, if IsUsingDeclaration of lhs is true, then + a. Let hint be sync-dispose. + 6. Else, + a. Let hint be normal. + 7. Let destructuring be IsDestructuring of lhs. + 8. If destructuring is true and if lhsKind is assignment, then + a. Assert: lhs is a LeftHandSideExpression. + b. Let assignmentPattern be the AssignmentPattern that is covered by lhs. + 9. Repeat, + ... + j. Let result be Completion(Evaluation of stmt). + k. If iterationEnv is not undefined, then + i. Set result to Completion(DisposeResources(iterationEnv.[[DisposeCapability]], result)). + ... + + 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, + async [Symbol.asyncDispose]() { + this.disposed = true; + } + }; + + var wasDisposedBeforeBody = false; + var wasDisposedBeforeIteration = false; + var wasDisposedAfterIteration = false; + + function * g() { + wasDisposedBeforeIteration = resource.disposed; + yield resource; + wasDisposedAfterIteration = resource.disposed; + } + + for (await using _ of g()) { + wasDisposedBeforeBody = resource.disposed; + } + + assert.sameValue(wasDisposedBeforeIteration, false, 'Expected resource to not been disposed before the for-of loop has received a value'); + assert.sameValue(wasDisposedBeforeBody, false, 'Expected resource to not been disposed while for-of loop is still iterating'); + assert.sameValue(wasDisposedAfterIteration, true, 'Expected resource to have been disposed after the for-of loop advanced to the next value'); +}); diff --git a/test/language/statements/await-using/initializer-Symbol.asyncDispose-called-at-end-of-forstatement.js b/test/language/statements/await-using/initializer-Symbol.asyncDispose-called-at-end-of-forstatement.js new file mode 100644 index 00000000000..14a65421500 --- /dev/null +++ b/test/language/statements/await-using/initializer-Symbol.asyncDispose-called-at-end-of-forstatement.js @@ -0,0 +1,62 @@ +// Copyright (C) 2023 Ron Buckton. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-runtime-semantics-forloopevaluation +description: Initialized value is disposed at end of ForStatement +info: | + RS: ForLoopEvaluation + ForStatement : for ( LexicalDeclaration Expressionopt ; Expressionopt ) Statement + + ... + 12. Let bodyResult be Completion(ForBodyEvaluation(test, increment, Statement, perIterationLets, labelSet)). + 13. Set bodyResult to Completion(DisposeResources(loopEnv.[[DisposeCapability]], bodyResult)). + 14. Assert: If bodyResult.[[Type]] is normal, then bodyResult.[[Value]] is not empty. + ... + + 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, + async [Symbol.asyncDispose]() { + this.disposed = true; + } + }; + + var i = 0; + var wasDisposedInForStatement; + for (await using _ = resource; i < 1; i++) { + wasDisposedInForStatement = resource.disposed; + } + + assert.sameValue(wasDisposedInForStatement, false, 'Expected resource to not been disposed while for loop is still iterating'); + assert.sameValue(resource.disposed, true, 'Expected resource to have been disposed'); +}); diff --git a/test/language/statements/await-using/initializer-Symbol.asyncDispose-called-at-end-of-switchstatement.js b/test/language/statements/await-using/initializer-Symbol.asyncDispose-called-at-end-of-switchstatement.js new file mode 100644 index 00000000000..0726ca441fd --- /dev/null +++ b/test/language/statements/await-using/initializer-Symbol.asyncDispose-called-at-end-of-switchstatement.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-switch-statement-runtime-semantics-evaluation +description: Initialized value is disposed at end of a SwitchStatement +info: | + RS: Evaluation + SwitchStatement : switch ( Expression ) CaseBlock + + ... + 7. Let R be Completion(CaseBlockEvaluation of CaseBlock with argument switchValue). + 8. Set R to Completion(DisposeResources(blockEnv.[[DisposeCapability]], R)). + 9. Assert: If R.[[Type]] is normal, then R.[[Value]] is not empty. + .. + + 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, + async [Symbol.asyncDispose]() { + this.disposed = true; + } + }; + + switch (0) { + default: + await using _ = resource; + break; + } + + assert.sameValue(resource.disposed, true, 'Expected resource to have been disposed'); +}); diff --git a/test/language/statements/await-using/initializer-Symbol.asyncDispose-called-if-subsequent-initializer-throws-in-forstatement-head.js b/test/language/statements/await-using/initializer-Symbol.asyncDispose-called-if-subsequent-initializer-throws-in-forstatement-head.js new file mode 100644 index 00000000000..19903e1210c --- /dev/null +++ b/test/language/statements/await-using/initializer-Symbol.asyncDispose-called-if-subsequent-initializer-throws-in-forstatement-head.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-runtime-semantics-forloopevaluation +description: Initialized value is disposed at end of FunctionBody +info: | + RS: ForLoopEvaluation + ForStatement : for ( LexicalDeclaration Expressionopt ; Expressionopt ) Statement + + ... + 7. Let forDcl be Completion(Evaluation of LexicalDeclaration). + 8. If forDcl is an abrupt completion, then + a. Set forDcl to Completion(DisposeResources(loopEnv.[[DisposeCapability]], forDcl)). + b. Assert: forDcl is an abrupt completion. + c. Set the running execution context's LexicalEnvironment to oldEnv. + d. Return ? forDcl. + ... + + 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, + async [Symbol.asyncDispose]() { + this.disposed = true; + } + }; + + function getResource() { + throw new Error(); + } + + await assert.throwsAsync(Error, async function () { + var i = 0; + for (await using _1 = resource, _2 = getResource(); i < 1; i++) { + } + }, 'for'); + + assert.sameValue(resource.disposed, true, 'Expected resource to have been disposed'); +}); diff --git a/test/language/statements/await-using/initializer-Symbol.asyncDispose-called-if-subsequent-initializer-throws.js b/test/language/statements/await-using/initializer-Symbol.asyncDispose-called-if-subsequent-initializer-throws.js new file mode 100644 index 00000000000..5864c90653a --- /dev/null +++ b/test/language/statements/await-using/initializer-Symbol.asyncDispose-called-if-subsequent-initializer-throws.js @@ -0,0 +1,62 @@ +// Copyright (C) 2023 Ron Buckton. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-block-runtime-semantics-evaluation +description: Initialized value is disposed even if subsequent initializer throws +info: | + RS: Evaluation + Block : { StatementList } + + ... + 5. Let blockValue be the result of evaluating StatementList. + 6. Set blockValue to DisposeResources(blockEnv.[[DisposeCapability]], blockValue). + ... + + 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, + async [Symbol.asyncDispose]() { + this.disposed = true; + } + }; + + function getResource() { + throw new Error(); + } + + await assert.throwsAsync(Error, async function () { + await using _1 = resource, _2 = getResource(); + }); + + assert.sameValue(resource.disposed, true, 'Expected resource to have been disposed'); +}); diff --git a/test/language/statements/await-using/initializer-Symbol.dispose-called-at-end-of-asyncfunctionbody.js b/test/language/statements/await-using/initializer-Symbol.dispose-called-at-end-of-asyncfunctionbody.js new file mode 100644 index 00000000000..f5ebe03ed42 --- /dev/null +++ b/test/language/statements/await-using/initializer-Symbol.dispose-called-at-end-of-asyncfunctionbody.js @@ -0,0 +1,105 @@ +// Copyright (C) 2023 Ron Buckton. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-asyncblockstart +description: Initialized value is disposed at end of AsyncFunctionBody +info: | + AsyncBlockStart ( promiseCapability, asyncBody, asyncContext ) + + 1. Assert: promiseCapability is a PromiseCapability Record. + 2. Let runningContext be the running execution context. + 3. Let closure be a new Abstract Closure with no parameters that captures promiseCapability and asyncBody and performs the following steps when called: + a. Let acAsyncContext be the running execution context. + b. Let result be Completion(Evaluation of asyncBody). + c. Assert: If we return here, the async function either threw an exception or performed an implicit or explicit return; all awaiting is done. + d. Remove acAsyncContext from the execution context stack and restore the execution context that is at the top of the execution context stack as the running execution context. + e. Let env be acAsyncContext's LexicalEnvironment. + f. Set result to DisposeResources(env.[[DisposeCapability]], result). + g. If result.[[Type]] is normal, then + i. Perform ! Call(promiseCapability.[[Resolve]], undefined, « undefined »). + h. Else if result.[[Type]] is return, then + i. Perform ! Call(promiseCapability.[[Resolve]], undefined, « result.[[Value]] »). + i. Else, + i. Assert: result.[[Type]] is throw. + ii. Perform ! Call(promiseCapability.[[Reject]], undefined, « result.[[Value]] »). + j. Return unused. + 4. Set the code evaluation state of asyncContext such that when evaluation is resumed for that execution context, closure will be called with no arguments. + 5. Push asyncContext onto the execution context stack; asyncContext is now the running execution context. + 6. Resume the suspended evaluation of asyncContext. Let result be the value returned by the resumed computation. + 7. Assert: When we return here, asyncContext has already been removed from the execution context stack and runningContext is the currently running execution context. + 8. Assert: result is a normal completion with a value of unused. The possible sources of this value are Await or, if the async function doesn't await anything, step 3.h above. + 9. Return unused. + + 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.dispose]() { + this.disposed = true; + } + }; + + var releaseF1; + var suspendFPromise1 = new Promise(function (resolve) { releaseF1 = resolve; }); + + var releaseBody; + var suspendBodyPromise = new Promise(function (resolve) { releaseBody = resolve; }); + + var releaseF2; + var suspendFPromise2 = new Promise(function (resolve) { releaseF2 = resolve; }); + + async function f() { + await using _ = resource; + await suspendFPromise1; + releaseBody(); + await suspendFPromise2; + } + + var resultPromise = f(); + + var wasDisposedWhileSuspended1 = resource.disposed; + + releaseF1(); + await suspendBodyPromise; + + var wasDisposedWhileSuspended2 = resource.disposed; + + releaseF2(); + await resultPromise; + + var isDisposedAfterCompleted = resource.disposed; + + assert.sameValue(wasDisposedWhileSuspended1, false, 'Expected resource to not have been disposed while async function is suspended during await'); + assert.sameValue(wasDisposedWhileSuspended2, false, 'Expected resource to not have been disposed while async function is suspended during await'); + assert.sameValue(isDisposedAfterCompleted, true, 'Expected resource to have been disposed after async function completed'); +}); diff --git a/test/language/statements/await-using/initializer-Symbol.dispose-called-at-end-of-asyncgeneratorbody.js b/test/language/statements/await-using/initializer-Symbol.dispose-called-at-end-of-asyncgeneratorbody.js new file mode 100644 index 00000000000..16d33abbd9e --- /dev/null +++ b/test/language/statements/await-using/initializer-Symbol.dispose-called-at-end-of-asyncgeneratorbody.js @@ -0,0 +1,107 @@ +// Copyright (C) 2023 Ron Buckton. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-asyncgeneratorstart +description: Initialized value is disposed at end of AsyncGeneratorBody +info: | + AsyncGeneratorStart ( generator, generatorBody ) + + 1. Assert: generator.[[AsyncGeneratorState]] is undefined. + 2. Let genContext be the running execution context. + 3. Set the Generator component of genContext to generator. + 4. Let closure be a new Abstract Closure with no parameters that captures generatorBody and performs the following steps when called: + a. Let acGenContext be the running execution context. + b. Let acGenerator be the Generator component of acGenContext. + c. If generatorBody is a Parse Node, then + i. Let result be Completion(Evaluation of generatorBody). + d. Else, + i. Assert: generatorBody is an Abstract Closure with no parameters. + ii. Let result be Completion(generatorBody()). + e. Assert: If we return here, the async generator either threw an exception or performed either an implicit or explicit return. + f. Remove acGenContext from the execution context stack and restore the execution context that is at the top of the execution context stack as the running execution context. + g. Set acGenerator.[[AsyncGeneratorState]] to completed. + h. Let env be genContext's LexicalEnvironment. + i. If env is not undefined, then + i. Assert: env is a Declarative Environment Record + ii. Set result to DisposeResources(env.[[DisposeCapability]], result). + h. If result.[[Type]] is normal, set result to NormalCompletion(undefined). + i. If result.[[Type]] is return, set result to NormalCompletion(result.[[Value]]). + j. Perform AsyncGeneratorCompleteStep(acGenerator, result, true). + k. Perform AsyncGeneratorDrainQueue(acGenerator). + l. Return undefined. + 5. Set the code evaluation state of genContext such that when evaluation is resumed for that execution context, closure will be called with no arguments. + 6. Set generator.[[AsyncGeneratorContext]] to genContext. + 7. Set generator.[[AsyncGeneratorState]] to suspendedStart. + 8. Set generator.[[AsyncGeneratorQueue]] to a new empty List. + 9. Return unused. + + 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.dispose]() { + this.disposed = true; + } + }; + + var releaseF; + var suspendFPromise = new Promise(function (resolve) { releaseF = resolve; }); + + async function * f() { + await using _ = resource; + yield; + await suspendFPromise; + } + + var g = f(); + + var wasDisposedBeforeAsyncGeneratorStarted = resource.disposed; + + await g.next(); + + var wasDisposedWhileSuspendedForYield = resource.disposed; + + var nextPromise = g.next(); + + var wasDisposedWhileSuspendedForAwait = resource.disposed; + + releaseF(); + await nextPromise; + + var isDisposedAfterCompleted = resource.disposed; + + assert.sameValue(wasDisposedBeforeAsyncGeneratorStarted, false, 'Expected resource to not have been disposed prior to async generator start'); + assert.sameValue(wasDisposedWhileSuspendedForYield, false, 'Expected resource to not have been disposed while async generator function is suspended for yield'); + assert.sameValue(wasDisposedWhileSuspendedForAwait, false, 'Expected resource to not have been disposed while async generator function is suspended during await'); + assert.sameValue(isDisposedAfterCompleted, true, 'Expected resource to have been disposed after async generator function completed'); +}); diff --git a/test/language/statements/await-using/initializer-Symbol.dispose-called-at-end-of-block.js b/test/language/statements/await-using/initializer-Symbol.dispose-called-at-end-of-block.js new file mode 100644 index 00000000000..6d0e11aa707 --- /dev/null +++ b/test/language/statements/await-using/initializer-Symbol.dispose-called-at-end-of-block.js @@ -0,0 +1,58 @@ +// Copyright (C) 2023 Ron Buckton. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-block-runtime-semantics-evaluation +description: Initialized value is disposed at end of Block +info: | + RS: Evaluation + Block : { StatementList } + + ... + 5. Let blockValue be the result of evaluating StatementList. + 6. Set blockValue to DisposeResources(blockEnv.[[DisposeCapability]], blockValue). + ... + + 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.dispose]() { + this.disposed = true; + } + }; + + { + await using _ = resource; + } + + assert.sameValue(resource.disposed, true, 'Expected resource to have been disposed'); +}); diff --git a/test/language/statements/await-using/initializer-Symbol.dispose-called-at-end-of-each-iteration-of-forofstatement.js b/test/language/statements/await-using/initializer-Symbol.dispose-called-at-end-of-each-iteration-of-forofstatement.js new file mode 100644 index 00000000000..6280f40b949 --- /dev/null +++ b/test/language/statements/await-using/initializer-Symbol.dispose-called-at-end-of-each-iteration-of-forofstatement.js @@ -0,0 +1,84 @@ +// Copyright (C) 2023 Ron Buckton. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-runtime-semantics-forin-div-ofbodyevaluation-lhs-stmt-iterator-lhskind-labelset +description: Initialized value is disposed at end of each iteration of ForOfStatement +info: | + ForIn/OfBodyEvaluation ( lhs, stmt, iteratorRecord, iterationKind, lhsKind, labelSet [ , iteratorKind ] ) + + 1. If iteratorKind is not present, set iteratorKind to sync. + 2. Let oldEnv be the running execution context's LexicalEnvironment. + 3. Let V be undefined. + 4. If IsAwaitUsingDeclaration of lhs is true, then + a. Let hint be async-dispose. + 5. Else, if IsUsingDeclaration of lhs is true, then + a. Let hint be sync-dispose. + 6. Else, + a. Let hint be normal. + 7. Let destructuring be IsDestructuring of lhs. + 8. If destructuring is true and if lhsKind is assignment, then + a. Assert: lhs is a LeftHandSideExpression. + b. Let assignmentPattern be the AssignmentPattern that is covered by lhs. + 9. Repeat, + ... + j. Let result be Completion(Evaluation of stmt). + k. If iterationEnv is not undefined, then + i. Set result to Completion(DisposeResources(iterationEnv.[[DisposeCapability]], result)). + ... + + 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.dispose]() { + this.disposed = true; + } + }; + + var wasDisposedBeforeBody = false; + var wasDisposedBeforeIteration = false; + var wasDisposedAfterIteration = false; + + function * g() { + wasDisposedBeforeIteration = resource.disposed; + yield resource; + wasDisposedAfterIteration = resource.disposed; + } + + for (await using _ of g()) { + wasDisposedBeforeBody = resource.disposed; + } + + assert.sameValue(wasDisposedBeforeIteration, false, 'Expected resource to not been disposed before the for-of loop has received a value'); + assert.sameValue(wasDisposedBeforeBody, false, 'Expected resource to not been disposed while for-of loop is still iterating'); + assert.sameValue(wasDisposedAfterIteration, true, 'Expected resource to have been disposed after the for-of loop advanced to the next value'); +}); diff --git a/test/language/statements/await-using/initializer-Symbol.dispose-called-at-end-of-forstatement.js b/test/language/statements/await-using/initializer-Symbol.dispose-called-at-end-of-forstatement.js new file mode 100644 index 00000000000..14a65421500 --- /dev/null +++ b/test/language/statements/await-using/initializer-Symbol.dispose-called-at-end-of-forstatement.js @@ -0,0 +1,62 @@ +// Copyright (C) 2023 Ron Buckton. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-runtime-semantics-forloopevaluation +description: Initialized value is disposed at end of ForStatement +info: | + RS: ForLoopEvaluation + ForStatement : for ( LexicalDeclaration Expressionopt ; Expressionopt ) Statement + + ... + 12. Let bodyResult be Completion(ForBodyEvaluation(test, increment, Statement, perIterationLets, labelSet)). + 13. Set bodyResult to Completion(DisposeResources(loopEnv.[[DisposeCapability]], bodyResult)). + 14. Assert: If bodyResult.[[Type]] is normal, then bodyResult.[[Value]] is not empty. + ... + + 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, + async [Symbol.asyncDispose]() { + this.disposed = true; + } + }; + + var i = 0; + var wasDisposedInForStatement; + for (await using _ = resource; i < 1; i++) { + wasDisposedInForStatement = resource.disposed; + } + + assert.sameValue(wasDisposedInForStatement, false, 'Expected resource to not been disposed while for loop is still iterating'); + assert.sameValue(resource.disposed, true, 'Expected resource to have been disposed'); +}); diff --git a/test/language/statements/await-using/initializer-Symbol.dispose-called-at-end-of-switchstatement.js b/test/language/statements/await-using/initializer-Symbol.dispose-called-at-end-of-switchstatement.js new file mode 100644 index 00000000000..4e58e2635d6 --- /dev/null +++ b/test/language/statements/await-using/initializer-Symbol.dispose-called-at-end-of-switchstatement.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-switch-statement-runtime-semantics-evaluation +description: Initialized value is disposed at end of a SwitchStatement +info: | + RS: Evaluation + SwitchStatement : switch ( Expression ) CaseBlock + + ... + 7. Let R be Completion(CaseBlockEvaluation of CaseBlock with argument switchValue). + 8. Set R to Completion(DisposeResources(blockEnv.[[DisposeCapability]], R)). + 9. Assert: If R.[[Type]] is normal, then R.[[Value]] is not empty. + .. + + 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.dispose]() { + this.disposed = true; + } + }; + + switch (0) { + default: + await using _ = resource; + break; + } + + assert.sameValue(resource.disposed, true, 'Expected resource to have been disposed'); +}); diff --git a/test/language/statements/await-using/initializer-Symbol.dispose-called-if-subsequent-initializer-throws-in-forstatement-head.js b/test/language/statements/await-using/initializer-Symbol.dispose-called-if-subsequent-initializer-throws-in-forstatement-head.js new file mode 100644 index 00000000000..73c4fba5da5 --- /dev/null +++ b/test/language/statements/await-using/initializer-Symbol.dispose-called-if-subsequent-initializer-throws-in-forstatement-head.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-runtime-semantics-forloopevaluation +description: Initialized value is disposed at end of FunctionBody +info: | + RS: ForLoopEvaluation + ForStatement : for ( LexicalDeclaration Expressionopt ; Expressionopt ) Statement + + ... + 7. Let forDcl be Completion(Evaluation of LexicalDeclaration). + 8. If forDcl is an abrupt completion, then + a. Set forDcl to Completion(DisposeResources(loopEnv.[[DisposeCapability]], forDcl)). + b. Assert: forDcl is an abrupt completion. + c. Set the running execution context's LexicalEnvironment to oldEnv. + d. Return ? forDcl. + ... + + 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.dispose]() { + this.disposed = true; + } + }; + + function getResource() { + throw new Error(); + } + + await assert.throwsAsync(Error, async function () { + var i = 0; + for (await using _1 = resource, _2 = getResource(); i < 1; i++) { + } + }, 'for'); + + assert.sameValue(resource.disposed, true, 'Expected resource to have been disposed'); +}); diff --git a/test/language/statements/await-using/initializer-Symbol.dispose-called-if-subsequent-initializer-throws.js b/test/language/statements/await-using/initializer-Symbol.dispose-called-if-subsequent-initializer-throws.js new file mode 100644 index 00000000000..2872231fd5c --- /dev/null +++ b/test/language/statements/await-using/initializer-Symbol.dispose-called-if-subsequent-initializer-throws.js @@ -0,0 +1,62 @@ +// Copyright (C) 2023 Ron Buckton. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-block-runtime-semantics-evaluation +description: Initialized value is disposed even if subsequent initializer throws +info: | + RS: Evaluation + Block : { StatementList } + + ... + 5. Let blockValue be the result of evaluating StatementList. + 6. Set blockValue to DisposeResources(blockEnv.[[DisposeCapability]], blockValue). + ... + + 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.dispose]() { + this.disposed = true; + } + }; + + function getResource() { + throw new Error(); + } + + await assert.throwsAsync(Error, async function () { + await using _1 = resource, _2 = getResource(); + }); + + assert.sameValue(resource.disposed, true, 'Expected resource to have been disposed'); +}); diff --git a/test/language/statements/await-using/multiple-resources-disposed-in-reverse-order.js b/test/language/statements/await-using/multiple-resources-disposed-in-reverse-order.js new file mode 100644 index 00000000000..f6376b9ab45 --- /dev/null +++ b/test/language/statements/await-using/multiple-resources-disposed-in-reverse-order.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-disposeresources +description: Multiple resources are disposed in the reverse of the order in which they were added +info: | + RS: Evaluation + Block : { StatementList } + + ... + 5. Let blockValue be the result of evaluating StatementList. + 6. Set blockValue to DisposeResources(blockEnv.[[DisposeCapability]], blockValue). + ... + + 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 disposed = []; + var resource1 = { async [Symbol.asyncDispose]() { disposed.push(this); } }; + var resource2 = { [Symbol.dispose]() { disposed.push(this); } }; + var resource3 = { async [Symbol.asyncDispose]() { disposed.push(this); } }; + + { + await using _1 = resource1, _2 = resource2; + await using _3 = resource3; + } + + assert.sameValue(disposed[0], resource3); + assert.sameValue(disposed[1], resource2); + assert.sameValue(disposed[2], resource1); +}); diff --git a/test/language/statements/await-using/puts-initializer-on-top-of-disposableresourcestack-multiple-bindings.js b/test/language/statements/await-using/puts-initializer-on-top-of-disposableresourcestack-multiple-bindings.js new file mode 100644 index 00000000000..61bd3990601 --- /dev/null +++ b/test/language/statements/await-using/puts-initializer-on-top-of-disposableresourcestack-multiple-bindings.js @@ -0,0 +1,73 @@ +// Copyright (C) 2023 Ron Buckton. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-let-and-const-declarations-runtime-semantics-evaluation +description: > + Puts initialized value on the top of the environment's [[DisposableResourceStack]] with multiple lexical bindings + in a single 'await using' declaration +info: | + RS: Evaluation + AwaitUsingDeclaration : CoverAwaitExpressionAndAwaitUsingDeclarationHead BindingList ; + + 1. Perform ? BindingEvaluation of BindingList with argument async-dispose. + 2. Return empty. + + RS: BindingEvaluation + BindingList : BindingList , LexicalBinding + + 1. Perform ? BindingEvaluation of BindingList with argument hint. + 2. Perform ? BindingEvaluation of LexicalBinding with argument hint. + 3. Return unused. + + LexicalBinding : BindingIdentifier Initializer + + ... + 5. Return ? InitializeReferencedBinding(lhs, value, hint). + + InitializeReferencedBinding ( V, W ) + + ... + 4. Return ? base.InitializeBinding(V.[[ReferencedName]], W). + + InitializeBinding ( N, V, hint ) + + ... + 2. If hint is not normal, perform ? AddDisposableResource(envRec.[[DisposeCapability]], V, hint). + ... + + 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). + 3. Else, + ... + 3. Append resource to disposeCapability.[[DisposableResourceStack]]. + 4. Return unused. + +flags: [async] +includes: [asyncHelpers.js] +features: [explicit-resource-management] +---*/ + +asyncTest(async function() { + var disposed = []; + var resource1 = { + async [Symbol.asyncDispose]() { + disposed.push(this); + } + }; + var resource2 = { + [Symbol.dispose]() { + disposed.push(this); + } + }; + { + await using r1 = resource1, r2 = resource2; + } + 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/language/statements/await-using/puts-initializer-on-top-of-disposableresourcestack-subsequent-usings.js b/test/language/statements/await-using/puts-initializer-on-top-of-disposableresourcestack-subsequent-usings.js new file mode 100644 index 00000000000..bb4040a9c9d --- /dev/null +++ b/test/language/statements/await-using/puts-initializer-on-top-of-disposableresourcestack-subsequent-usings.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-let-and-const-declarations-runtime-semantics-evaluation +description: > + Puts initialized value on the top of the environment's [[DisposableResourceStack]] with multiple subsequent 'await using' + declarations in the same block scope +info: | + RS: Evaluation + AwaitUsingDeclaration : CoverAwaitExpressionAndAwaitUsingDeclarationHead BindingList ; + + 1. Perform ? BindingEvaluation of BindingList with argument async-dispose. + 2. Return empty. + + RS: BindingEvaluation + LexicalBinding : BindingIdentifier Initializer + + ... + 5. Return ? InitializeReferencedBinding(lhs, value, hint). + + InitializeReferencedBinding ( V, W ) + + ... + 4. Return ? base.InitializeBinding(V.[[ReferencedName]], W). + + InitializeBinding ( N, V, hint ) + + ... + 2. If hint is not normal, perform ? AddDisposableResource(envRec.[[DisposeCapability]], V, hint). + ... + + 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). + 3. Else, + ... + 3. Append resource to disposeCapability.[[DisposableResourceStack]]. + 4. Return unused. + +flags: [async] +includes: [asyncHelpers.js] +features: [explicit-resource-management] +---*/ + +asyncTest(async function() { + var disposed = []; + var resource1 = { + async [Symbol.asyncDispose]() { + disposed.push(this); + } + }; + var resource2 = { + [Symbol.dispose]() { + disposed.push(this); + } + }; + { + await using r1 = resource1; + await using r2 = resource2; + } + 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/language/statements/await-using/redeclaration-error-from-within-strict-mode-function-await-using.js b/test/language/statements/await-using/redeclaration-error-from-within-strict-mode-function-await-using.js new file mode 100644 index 00000000000..68e843004b6 --- /dev/null +++ b/test/language/statements/await-using/redeclaration-error-from-within-strict-mode-function-await-using.js @@ -0,0 +1,16 @@ +// Copyright (C) 2023 Ron Buckton. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-declarative-environment-records-initializebinding-n-v +description: > + Redeclaration error within strict mode function inside non-strict code. +negative: + phase: parse + type: SyntaxError +flags: [noStrict, explicit-resource-management] +---*/ + +$DONOTEVALUATE(); +(async function() { 'use strict'; { await using f = null; var f; } }) + diff --git a/test/language/statements/await-using/syntax/await-using-allowed-at-top-level-of-module.js b/test/language/statements/await-using/syntax/await-using-allowed-at-top-level-of-module.js new file mode 100644 index 00000000000..f1d21b7b360 --- /dev/null +++ b/test/language/statements/await-using/syntax/await-using-allowed-at-top-level-of-module.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: pending +description: > + await using declarations allowed at the top level of a module +info: | + AwaitUsingDeclaration : CoverAwaitExpressionAndAwaitUsingDeclarationHead BindingList ; + + - It is a Syntax Error if the goal symbol is Script and UsingDeclaration is not contained, either directly or + indirectly, within a Block, CaseBlock, ForStatement, ForInOfStatement, FunctionBody, GeneratorBody, + AsyncGeneratorBody, AsyncFunctionBody, ClassStaticBlockBody, or ClassBody. + +flags: [module] +features: [explicit-resource-management] +---*/ + +await using x = null; diff --git a/test/language/statements/await-using/syntax/await-using-allows-bindingidentifier.js b/test/language/statements/await-using/syntax/await-using-allows-bindingidentifier.js new file mode 100644 index 00000000000..71a90143daf --- /dev/null +++ b/test/language/statements/await-using/syntax/await-using-allows-bindingidentifier.js @@ -0,0 +1,14 @@ +// Copyright (C) 2023 Ron Buckton. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: pending +description: > + 'await using' allows BindingIdentifier in lexical bindings +flags: [async] +includes: [asyncHelpers.js] +features: [explicit-resource-management] +---*/ +asyncTest(async function () { + await using x = null; +}); diff --git a/test/language/statements/await-using/syntax/await-using-allows-multiple-bindings.js b/test/language/statements/await-using/syntax/await-using-allows-multiple-bindings.js new file mode 100644 index 00000000000..9565810c327 --- /dev/null +++ b/test/language/statements/await-using/syntax/await-using-allows-multiple-bindings.js @@ -0,0 +1,13 @@ +// Copyright (C) 2023 Ron Buckton. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: pending +description: > + 'await using' allows multiple lexical bindings +features: [explicit-resource-management] +---*/ + +async function f() { + await using x = null, y = null; +} diff --git a/test/language/statements/await-using/syntax/await-using-declaring-let-split-across-two-lines.js b/test/language/statements/await-using/syntax/await-using-declaring-let-split-across-two-lines.js new file mode 100644 index 00000000000..51aa700ef28 --- /dev/null +++ b/test/language/statements/await-using/syntax/await-using-declaring-let-split-across-two-lines.js @@ -0,0 +1,21 @@ +// Copyright (C) 2023 Ron Buckton. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: pending +description: > + await using: |await using let| split across two lines is treated as two statements. +info: | + Lexical declarations may not declare a binding named "let". +flags: [noStrict, async] +includes: [asyncHelpers.js] +features: [explicit-resource-management] +---*/ + +asyncTest(async function () { + await using + let = "irrelevant initializer"; + + assert(typeof let === "string"); + var using, let; +}); diff --git a/test/language/statements/await-using/syntax/await-using-invalid-arraybindingpattern-after-bindingidentifier.js b/test/language/statements/await-using/syntax/await-using-invalid-arraybindingpattern-after-bindingidentifier.js new file mode 100644 index 00000000000..5c00fb4f699 --- /dev/null +++ b/test/language/statements/await-using/syntax/await-using-invalid-arraybindingpattern-after-bindingidentifier.js @@ -0,0 +1,18 @@ +// Copyright (C) 2023 Ron Buckton. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: pending +description: > + 'await using' does not allow ArrayBindingPattern in lexical bindings, even after a valid lexical binding +negative: + phase: parse + type: SyntaxError +features: [explicit-resource-management] +---*/ + +$DONOTEVALUATE(); + +async function f() { + await using x = null, [] = null; +} diff --git a/test/language/statements/await-using/syntax/await-using-invalid-arraybindingpattern-does-not-break-element-access.js b/test/language/statements/await-using/syntax/await-using-invalid-arraybindingpattern-does-not-break-element-access.js new file mode 100644 index 00000000000..7d8ea32916f --- /dev/null +++ b/test/language/statements/await-using/syntax/await-using-invalid-arraybindingpattern-does-not-break-element-access.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: pending +description: > + 'await using' does not break existing element access +flags: [async] +includes: [asyncHelpers.js] +features: [explicit-resource-management] +---*/ + +var using = [], x = 0; + +asyncTest(async function () { + await using[x]; +}); diff --git a/test/language/statements/await-using/syntax/await-using-invalid-arraybindingpattern.js b/test/language/statements/await-using/syntax/await-using-invalid-arraybindingpattern.js new file mode 100644 index 00000000000..2c0b4d8007d --- /dev/null +++ b/test/language/statements/await-using/syntax/await-using-invalid-arraybindingpattern.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: pending +description: > + 'await using' does not allow ArrayBindingPattern in lexical bindings +negative: + phase: parse + type: SyntaxError +features: [explicit-resource-management] +---*/ + +$DONOTEVALUATE(); +async function() { + await using [] = null; +} diff --git a/test/language/statements/await-using/syntax/await-using-invalid-assignment-next-expression-for.js b/test/language/statements/await-using/syntax/await-using-invalid-assignment-next-expression-for.js new file mode 100644 index 00000000000..b4dcebaf164 --- /dev/null +++ b/test/language/statements/await-using/syntax/await-using-invalid-assignment-next-expression-for.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: pending +description: > + await using: invalid assignment in next expression +flags: [async] +includes: [asyncHelpers.js] +features: [explicit-resource-management] +---*/ + +asyncTest(async function () { + await assert.throwsAsync(TypeError, async function() { + for (await using i = 0; i < 1; i++) {} + }); +}); diff --git a/test/language/statements/await-using/syntax/await-using-invalid-assignment-statement-body-for-of.js b/test/language/statements/await-using/syntax/await-using-invalid-assignment-statement-body-for-of.js new file mode 100644 index 00000000000..fbfba48774e --- /dev/null +++ b/test/language/statements/await-using/syntax/await-using-invalid-assignment-statement-body-for-of.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: pending +description: > + await using: invalid assignment in Statement body +flags: [async] +includes: [asyncHelpers.js] +features: [explicit-resource-management] +---*/ + +asyncTest(async function () { + await assert.throwsAsync(TypeError, async function () { + for (await using x of [1, 2, 3]) { x++ } + }); +}); diff --git a/test/language/statements/await-using/syntax/await-using-invalid-for-in.js b/test/language/statements/await-using/syntax/await-using-invalid-for-in.js new file mode 100644 index 00000000000..75bf2f3b4a8 --- /dev/null +++ b/test/language/statements/await-using/syntax/await-using-invalid-for-in.js @@ -0,0 +1,16 @@ +// Copyright (C) 2011 the V8 project authors. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. +/*--- +esid: pending +description: > + await using: not allowed in for..in +negative: + phase: parse + type: SyntaxError +features: [explicit-resource-management] +---*/ + +$DONOTEVALUATE(); +async function f() { + for (await using x in [1, 2, 3]) { } +} diff --git a/test/language/statements/await-using/syntax/await-using-invalid-objectbindingpattern-after-bindingidentifier.js b/test/language/statements/await-using/syntax/await-using-invalid-objectbindingpattern-after-bindingidentifier.js new file mode 100644 index 00000000000..e1d96f5ea7e --- /dev/null +++ b/test/language/statements/await-using/syntax/await-using-invalid-objectbindingpattern-after-bindingidentifier.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: pending +description: > + 'await using' does not allow ObjectBindingPattern in lexical bindings, even after a valid lexical binding +negative: + phase: parse + type: SyntaxError +features: [explicit-resource-management] +---*/ + +$DONOTEVALUATE(); +async function f() { + await using x = null, {} = null; +} diff --git a/test/language/statements/await-using/syntax/await-using-invalid-objectbindingpattern.js b/test/language/statements/await-using/syntax/await-using-invalid-objectbindingpattern.js new file mode 100644 index 00000000000..8f9012a418c --- /dev/null +++ b/test/language/statements/await-using/syntax/await-using-invalid-objectbindingpattern.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: pending +description: > + 'await using' does not allow ObjectBindingPattern in lexical bindings +negative: + phase: parse + type: SyntaxError +features: [explicit-resource-management] +---*/ + +$DONOTEVALUATE(); +async function f() { + await using {} = null; +} diff --git a/test/language/statements/await-using/syntax/await-using-not-allowed-at-top-level-of-eval.js b/test/language/statements/await-using/syntax/await-using-not-allowed-at-top-level-of-eval.js new file mode 100644 index 00000000000..b2aaccee5ec --- /dev/null +++ b/test/language/statements/await-using/syntax/await-using-not-allowed-at-top-level-of-eval.js @@ -0,0 +1,24 @@ +// Copyright (C) 2023 Ron Buckton. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: pending +description: > + await using declarations not allowed at the top level of eval +info: | + AwaitUsingDeclaration : CoverAwaitExpressionAndAwaitUsingDeclarationHead BindingList ; + + - It is a Syntax Error if the goal symbol is Script and UsingDeclaration is not contained, either directly or + indirectly, within a Block, CaseBlock, ForStatement, ForInOfStatement, FunctionBody, GeneratorBody, + AsyncGeneratorBody, AsyncFunctionBody, ClassStaticBlockBody, or ClassBody. + +flags: [async] +includes: [asyncHelpers.js] +features: [explicit-resource-management] +---*/ + +asyncTest(async function () { + await assert.throwsAsync(SyntaxError, async function () { + eval('await using x = null;') + }); +}); diff --git a/test/language/statements/await-using/syntax/await-using-not-allowed-at-top-level-of-script.js b/test/language/statements/await-using/syntax/await-using-not-allowed-at-top-level-of-script.js new file mode 100644 index 00000000000..d7d2376d8e2 --- /dev/null +++ b/test/language/statements/await-using/syntax/await-using-not-allowed-at-top-level-of-script.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: pending +description: > + await using declarations not allowed at the top level of a Script +info: | + AwaitUsingDeclaration : CoverAwaitExpressionAndAwaitUsingDeclarationHead BindingList ; + + - It is a Syntax Error if the goal symbol is Script and UsingDeclaration is not contained, either directly or + indirectly, within a Block, CaseBlock, ForStatement, ForInOfStatement, FunctionBody, GeneratorBody, + AsyncGeneratorBody, AsyncFunctionBody, ClassStaticBlockBody, or ClassBody. + +negative: + phase: parse + type: SyntaxError +features: [explicit-resource-management] +---*/ + +$DONOTEVALUATE(); +await using x = null; diff --git a/test/language/statements/await-using/syntax/await-using-outer-inner-using-bindings.js b/test/language/statements/await-using/syntax/await-using-outer-inner-using-bindings.js new file mode 100644 index 00000000000..c90044284d2 --- /dev/null +++ b/test/language/statements/await-using/syntax/await-using-outer-inner-using-bindings.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: pending +description: > + outer await using binding unchanged by for-loop await using binding +flags: [async] +includes: [asyncHelpers.js] +features: [explicit-resource-management] +---*/ + +asyncTest(async function () { + const outer_x = { [Symbol.dispose]() {} }; + const outer_y = { [Symbol.dispose]() {} }; + const inner_x = { [Symbol.dispose]() {} }; + const inner_y = { [Symbol.dispose]() {} }; + + { + await using x = outer_x; + await using y = outer_y; + var i = 0; + + for (await using x = inner_x; i < 1; i++) { + await using y = inner_y; + + assert.sameValue(x, inner_x); + assert.sameValue(y, inner_y); + } + assert.sameValue(x, outer_x); + assert.sameValue(y, outer_y); + } +}); diff --git a/test/language/statements/await-using/syntax/await-using-valid-for-await-using-of-of.js b/test/language/statements/await-using/syntax/await-using-valid-for-await-using-of-of.js new file mode 100644 index 00000000000..d9d63c46fb1 --- /dev/null +++ b/test/language/statements/await-using/syntax/await-using-valid-for-await-using-of-of.js @@ -0,0 +1,12 @@ +// Copyright (C) 2011 the V8 project authors. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. +/*--- +esid: pending +description: > + await using: 'for (await using of of' interpreted as 'await using' +features: [explicit-resource-management] +---*/ + +async function f() { + for (await using of of []) { } +} diff --git a/test/language/statements/await-using/syntax/await-using.js b/test/language/statements/await-using/syntax/await-using.js new file mode 100644 index 00000000000..872c913c800 --- /dev/null +++ b/test/language/statements/await-using/syntax/await-using.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: pending +description: > + module and block scope await using +flags: [module] +features: [explicit-resource-management] +---*/ + +await using z = null; + +// Block local +{ + await using z = undefined; +} + +assert.sameValue(z, null); + +if (true) { + const obj = { [Symbol.dispose]() { } }; + await using z = obj; + assert.sameValue(z, obj); +} + diff --git a/test/language/statements/await-using/syntax/block-scope-syntax-await-using-declarations-mixed-with-without-initializer.js b/test/language/statements/await-using/syntax/block-scope-syntax-await-using-declarations-mixed-with-without-initializer.js new file mode 100644 index 00000000000..89e929ee863 --- /dev/null +++ b/test/language/statements/await-using/syntax/block-scope-syntax-await-using-declarations-mixed-with-without-initializer.js @@ -0,0 +1,18 @@ +// Copyright (C) 2023 Ron Buckton. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: pending +description: > + await using declarations mixed: with, without initializer +negative: + phase: parse + type: SyntaxError +flags: [async] +features: [explicit-resource-management] +---*/ + +$DONOTEVALUATE(); +async function f() { + await using x = null, y; +} diff --git a/test/language/statements/await-using/syntax/block-scope-syntax-await-using-declarations-mixed-without-with-initializer.js b/test/language/statements/await-using/syntax/block-scope-syntax-await-using-declarations-mixed-without-with-initializer.js new file mode 100644 index 00000000000..cf7160b7c7b --- /dev/null +++ b/test/language/statements/await-using/syntax/block-scope-syntax-await-using-declarations-mixed-without-with-initializer.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: pending +description: > + await using declarations mixed: without, with initializer +negative: + phase: parse + type: SyntaxError +features: [explicit-resource-management] +---*/ + +$DONOTEVALUATE(); +async function f() { + await using x, y = null; +} diff --git a/test/language/statements/await-using/syntax/block-scope-syntax-await-using-declarations-without-initializer.js b/test/language/statements/await-using/syntax/block-scope-syntax-await-using-declarations-without-initializer.js new file mode 100644 index 00000000000..a05e32b47b1 --- /dev/null +++ b/test/language/statements/await-using/syntax/block-scope-syntax-await-using-declarations-without-initializer.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: pending +description: > + await using declarations without initializer +negative: + phase: parse + type: SyntaxError +features: [explicit-resource-management] +---*/ + +$DONOTEVALUATE(); +async function f() { + await using x; +} diff --git a/test/language/statements/await-using/syntax/with-initializer-case-expression-statement-list.js b/test/language/statements/await-using/syntax/with-initializer-case-expression-statement-list.js new file mode 100644 index 00000000000..e17ddba599d --- /dev/null +++ b/test/language/statements/await-using/syntax/with-initializer-case-expression-statement-list.js @@ -0,0 +1,15 @@ +// Copyright (C) 2023 Ron Buckton. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: pending +description: > + await using declarations with initialisers in statement positions: + case Expression : StatementList +flags: [async] +includes: [asyncHelpers.js] +features: [explicit-resource-management] +---*/ +asyncTest(async function () { + switch (true) { case true: await using x = null; } +}); diff --git a/test/language/statements/await-using/syntax/with-initializer-default-statement-list.js b/test/language/statements/await-using/syntax/with-initializer-default-statement-list.js new file mode 100644 index 00000000000..8b26d78e922 --- /dev/null +++ b/test/language/statements/await-using/syntax/with-initializer-default-statement-list.js @@ -0,0 +1,15 @@ +// Copyright (C) 2023 Ron Buckton. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: pending +description: > + await using declarations with initialisers in statement positions: + default : StatementList +flags: [async] +includes: [asyncHelpers.js] +features: [explicit-resource-management] +---*/ +asyncTest(async function () { + switch (true) { default: await using x = null; } +}); diff --git a/test/language/statements/await-using/syntax/with-initializer-do-statement-while-expression.js b/test/language/statements/await-using/syntax/with-initializer-do-statement-while-expression.js new file mode 100644 index 00000000000..1e6e64617c0 --- /dev/null +++ b/test/language/statements/await-using/syntax/with-initializer-do-statement-while-expression.js @@ -0,0 +1,18 @@ +// Copyright (C) 2023 Ron Buckton. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: pending +description: > + await using declarations with initialisers in statement positions: + do Statement while ( Expression ) +negative: + phase: parse + type: SyntaxError +features: [explicit-resource-management] +---*/ + +$DONOTEVALUATE(); +async function f() { + do await using x = 1; while (false) +} diff --git a/test/language/statements/await-using/syntax/with-initializer-for-statement.js b/test/language/statements/await-using/syntax/with-initializer-for-statement.js new file mode 100644 index 00000000000..96560a86300 --- /dev/null +++ b/test/language/statements/await-using/syntax/with-initializer-for-statement.js @@ -0,0 +1,18 @@ +// Copyright (C) 2023 Ron Buckton. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: pending +description: > + await using declarations with initialisers in statement positions: + for (;;) Statement +negative: + phase: parse + type: SyntaxError +features: [explicit-resource-management] +---*/ + +$DONOTEVALUATE(); +async function f() { + for (;false;) await using x = null; +} diff --git a/test/language/statements/await-using/syntax/with-initializer-if-expression-statement-else-statement.js b/test/language/statements/await-using/syntax/with-initializer-if-expression-statement-else-statement.js new file mode 100644 index 00000000000..2008e328aed --- /dev/null +++ b/test/language/statements/await-using/syntax/with-initializer-if-expression-statement-else-statement.js @@ -0,0 +1,18 @@ +// Copyright (C) 2023 Ron Buckton. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: pending +description: > + await using declarations with initialisers in statement positions: + if ( Expression ) Statement else Statement +negative: + phase: parse + type: SyntaxError +features: [explicit-resource-management] +---*/ + +$DONOTEVALUATE(); +async function f() { + if (true) {} else await using x = null; +} diff --git a/test/language/statements/await-using/syntax/with-initializer-if-expression-statement.js b/test/language/statements/await-using/syntax/with-initializer-if-expression-statement.js new file mode 100644 index 00000000000..db35db982cf --- /dev/null +++ b/test/language/statements/await-using/syntax/with-initializer-if-expression-statement.js @@ -0,0 +1,18 @@ +// Copyright (C) 2023 Ron Buckton. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: pending +description: > + await using declarations with initialisers in statement positions: + if ( Expression ) Statement +negative: + phase: parse + type: SyntaxError +features: [explicit-resource-management] +---*/ + +$DONOTEVALUATE(); +async function f() { + if (true) await using x = null; +} diff --git a/test/language/statements/await-using/syntax/with-initializer-label-statement.js b/test/language/statements/await-using/syntax/with-initializer-label-statement.js new file mode 100644 index 00000000000..01b08058ba4 --- /dev/null +++ b/test/language/statements/await-using/syntax/with-initializer-label-statement.js @@ -0,0 +1,18 @@ +// Copyright (C) 2023 Ron Buckton. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: pending +description: > + await using declarations with initialisers in statement positions: + label: Statement +negative: + phase: parse + type: SyntaxError +features: [explicit-resource-management] +---*/ + +$DONOTEVALUATE(); +async function f() { + label: await using x = null; +} diff --git a/test/language/statements/await-using/syntax/with-initializer-while-expression-statement.js b/test/language/statements/await-using/syntax/with-initializer-while-expression-statement.js new file mode 100644 index 00000000000..fccc5f8c0fd --- /dev/null +++ b/test/language/statements/await-using/syntax/with-initializer-while-expression-statement.js @@ -0,0 +1,18 @@ +// Copyright (C) 2023 Ron Buckton. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: pending +description: > + await using declarations with initialisers in statement positions: + while ( Expression ) Statement +negative: + phase: parse + type: SyntaxError +features: [explicit-resource-management] +---*/ + +$DONOTEVALUATE(); +async function f() { + while (false) await using x = null; +} diff --git a/test/language/statements/await-using/syntax/without-initializer-case-expression-statement-list.js b/test/language/statements/await-using/syntax/without-initializer-case-expression-statement-list.js new file mode 100644 index 00000000000..6ff09631c3c --- /dev/null +++ b/test/language/statements/await-using/syntax/without-initializer-case-expression-statement-list.js @@ -0,0 +1,18 @@ +// Copyright (C) 2023 Ron Buckton. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: pending +description: > + await using declarations without initialisers in statement positions: + case Expression : StatementList +negative: + phase: parse + type: SyntaxError +features: [explicit-resource-management] +---*/ + +$DONOTEVALUATE(); +async function f() { + switch (true) { case true: await using x; } +} diff --git a/test/language/statements/await-using/syntax/without-initializer-default-statement-list.js b/test/language/statements/await-using/syntax/without-initializer-default-statement-list.js new file mode 100644 index 00000000000..e00ba7a7db2 --- /dev/null +++ b/test/language/statements/await-using/syntax/without-initializer-default-statement-list.js @@ -0,0 +1,18 @@ +// Copyright (C) 2023 Ron Buckton. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: pending +description: > + await using declarations without initialisers in statement positions: + default : StatementList +negative: + phase: parse + type: SyntaxError +features: [explicit-resource-management] +---*/ + +$DONOTEVALUATE(); +async function f() { + switch (true) { default: await using x; } +} diff --git a/test/language/statements/await-using/syntax/without-initializer-do-statement-while-expression.js b/test/language/statements/await-using/syntax/without-initializer-do-statement-while-expression.js new file mode 100644 index 00000000000..4c6e3df6203 --- /dev/null +++ b/test/language/statements/await-using/syntax/without-initializer-do-statement-while-expression.js @@ -0,0 +1,18 @@ +// Copyright (C) 2023 Ron Buckton. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: pending +description: > + await using declarations without initialisers in statement positions: + do Statement while ( Expression ) +negative: + phase: parse + type: SyntaxError +features: [explicit-resource-management] +---*/ + +$DONOTEVALUATE(); +async function f() { + do await using x; while (false) +} diff --git a/test/language/statements/await-using/syntax/without-initializer-for-statement.js b/test/language/statements/await-using/syntax/without-initializer-for-statement.js new file mode 100644 index 00000000000..02377b22eaa --- /dev/null +++ b/test/language/statements/await-using/syntax/without-initializer-for-statement.js @@ -0,0 +1,18 @@ +// Copyright (C) 2023 Ron Buckton. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: pending +description: > + await using declarations without initialisers in statement positions: + for ( ;;) Statement +negative: + phase: parse + type: SyntaxError +features: [explicit-resource-management] +---*/ + +$DONOTEVALUATE(); +async function f() { + for (;false;) await using x; +} diff --git a/test/language/statements/await-using/syntax/without-initializer-if-expression-statement-else-statement.js b/test/language/statements/await-using/syntax/without-initializer-if-expression-statement-else-statement.js new file mode 100644 index 00000000000..008b289dae7 --- /dev/null +++ b/test/language/statements/await-using/syntax/without-initializer-if-expression-statement-else-statement.js @@ -0,0 +1,18 @@ +// Copyright (C) 2023 Ron Buckton. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: pending +description: > + await using declarations without initialisers in statement positions: + if ( Expression ) Statement else Statement +negative: + phase: parse + type: SyntaxError +features: [explicit-resource-management] +---*/ + +$DONOTEVALUATE(); +async function f() { + if (true) {} else await using x; +} diff --git a/test/language/statements/await-using/syntax/without-initializer-if-expression-statement.js b/test/language/statements/await-using/syntax/without-initializer-if-expression-statement.js new file mode 100644 index 00000000000..32bc464b02d --- /dev/null +++ b/test/language/statements/await-using/syntax/without-initializer-if-expression-statement.js @@ -0,0 +1,18 @@ +// Copyright (C) 2023 Ron Buckton. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: pending +description: > + await using declarations without initialisers in statement positions: + if ( Expression ) Statement +negative: + phase: parse + type: SyntaxError +features: [explicit-resource-management] +---*/ + +$DONOTEVALUATE(); +async function f() { + if (true) await using x; +} diff --git a/test/language/statements/await-using/syntax/without-initializer-label-statement.js b/test/language/statements/await-using/syntax/without-initializer-label-statement.js new file mode 100644 index 00000000000..49fd9a68703 --- /dev/null +++ b/test/language/statements/await-using/syntax/without-initializer-label-statement.js @@ -0,0 +1,18 @@ +// Copyright (C) 2023 Ron Buckton. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: pending +description: > + await using declarations without initialisers in statement positions: + label: Statement +negative: + phase: parse + type: SyntaxError +features: [explicit-resource-management] +---*/ + +$DONOTEVALUATE(); +async function f() { + label: await using x; +} diff --git a/test/language/statements/await-using/syntax/without-initializer-while-expression-statement.js b/test/language/statements/await-using/syntax/without-initializer-while-expression-statement.js new file mode 100644 index 00000000000..e4939275444 --- /dev/null +++ b/test/language/statements/await-using/syntax/without-initializer-while-expression-statement.js @@ -0,0 +1,18 @@ +// Copyright (C) 2023 Ron Buckton. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: pending +description: > + await using declarations without initialisers in statement positions: + while ( Expression ) Statement +negative: + phase: parse + type: SyntaxError +features: [explicit-resource-management] +---*/ + +$DONOTEVALUATE(); +async function f() { + while (false) await using x; +} diff --git a/test/language/statements/await-using/throws-error-as-is-if-only-one-error-during-disposal.js b/test/language/statements/await-using/throws-error-as-is-if-only-one-error-during-disposal.js new file mode 100644 index 00000000000..5f64b4e1157 --- /dev/null +++ b/test/language/statements/await-using/throws-error-as-is-if-only-one-error-during-disposal.js @@ -0,0 +1,56 @@ +// Copyright (C) 2023 Ron Buckton. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-disposeresources +description: > + Rethrows an error as-is if it is the only error thrown during evaluation of subsequent statements following 'await using' + or from disposal. +info: | + 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 {} + await assert.throwsAsync(MyError, async function () { + await using _1 = { async [Symbol.asyncDispose]() { throw new MyError(); } }; + await using _2 = { [Symbol.dispose]() { } }; + }); + + await assert.throwsAsync(MyError, async function () { + await using _1 = { async [Symbol.asyncDispose]() { } }; + await using _2 = { [Symbol.dispose]() { throw new MyError(); } }; + }); + + await assert.throwsAsync(MyError, async function () { + await using _1 = { async [Symbol.asyncDispose]() { } }; + await using _2 = { [Symbol.dispose]() { } }; + throw new MyError(); + }); +}); diff --git a/test/language/statements/await-using/throws-if-initializer-Symbol.asyncDispose-property-is-null.js b/test/language/statements/await-using/throws-if-initializer-Symbol.asyncDispose-property-is-null.js new file mode 100644 index 00000000000..ba5e61f10d8 --- /dev/null +++ b/test/language/statements/await-using/throws-if-initializer-Symbol.asyncDispose-property-is-null.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-let-and-const-declarations-runtime-semantics-evaluation +description: Throws if initialized value's Symbol.asyncDispose property is null and Symbol.dispose is not present +info: | + RS: Evaluation + AwaitUsingDeclaration : CoverAwaitExpressionAndAwaitUsingDeclarationHead BindingList ; + + 1. Perform ? BindingEvaluation of BindingList with argument async-dispose. + 2. Return empty. + + RS: BindingEvaluation + LexicalBinding : BindingIdentifier Initializer + + ... + 5. Return ? InitializeReferencedBinding(lhs, value, hint). + + InitializeReferencedBinding ( V, W ) + + ... + 4. Return ? base.InitializeBinding(V.[[ReferencedName]], W). + + InitializeBinding ( N, V, hint ) + + ... + 2. If hint is not normal, perform ? AddDisposableResource(envRec.[[DisposeCapability]], V, hint). + ... + + 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. + + CreateDisposableResource ( V, hint [ , method ] ) + + 1. If method is not present, then + a. If V is either null or undefined, then + ... + b. Else, + i. If V is not an 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. ... + +flags: [async] +includes: [asyncHelpers.js] +features: [explicit-resource-management] +---*/ + +asyncTest(async function () { + await assert.throwsAsync(TypeError, async function () { + await using x = { [Symbol.asyncDispose]: null }; + }); +}); diff --git a/test/language/statements/await-using/throws-if-initializer-Symbol.asyncDispose-property-is-undefined.js b/test/language/statements/await-using/throws-if-initializer-Symbol.asyncDispose-property-is-undefined.js new file mode 100644 index 00000000000..714f0864677 --- /dev/null +++ b/test/language/statements/await-using/throws-if-initializer-Symbol.asyncDispose-property-is-undefined.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-let-and-const-declarations-runtime-semantics-evaluation +description: Throws if initialized value's Symbol.asyncDispose property is undefined and Symbol.dispose is not present +info: | + RS: Evaluation + AwaitUsingDeclaration : CoverAwaitExpressionAndAwaitUsingDeclarationHead BindingList ; + + 1. Perform ? BindingEvaluation of BindingList with argument async-dispose. + 2. Return empty. + + RS: BindingEvaluation + LexicalBinding : BindingIdentifier Initializer + + ... + 5. Return ? InitializeReferencedBinding(lhs, value, hint). + + InitializeReferencedBinding ( V, W ) + + ... + 4. Return ? base.InitializeBinding(V.[[ReferencedName]], W). + + InitializeBinding ( N, V, hint ) + + ... + 2. If hint is not normal, perform ? AddDisposableResource(envRec.[[DisposeCapability]], V, hint). + ... + + 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. + + CreateDisposableResource ( V, hint [ , method ] ) + + 1. If method is not present, then + a. If V is either null or undefined, then + ... + b. Else, + i. If V is not an 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. ... + +flags: [async] +includes: [asyncHelpers.js] +features: [explicit-resource-management] +---*/ + +asyncTest(async function () { + await assert.throwsAsync(TypeError, async function () { + await using x = { [Symbol.asyncDispose]: undefined }; + }); +}); diff --git a/test/language/statements/await-using/throws-if-initializer-Symbol.asyncDispose-property-not-callable.js b/test/language/statements/await-using/throws-if-initializer-Symbol.asyncDispose-property-not-callable.js new file mode 100644 index 00000000000..e00db2d7d7c --- /dev/null +++ b/test/language/statements/await-using/throws-if-initializer-Symbol.asyncDispose-property-not-callable.js @@ -0,0 +1,99 @@ +// Copyright (C) 2023 Ron Buckton. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-let-and-const-declarations-runtime-semantics-evaluation +description: Throws if initialized value's Symbol.asyncDispose property is not callable +info: | + RS: Evaluation + AwaitUsingDeclaration : CoverAwaitExpressionAndAwaitUsingDeclarationHead BindingList ; + + 1. Perform ? BindingEvaluation of BindingList with argument async-dispose. + 2. Return empty. + + RS: BindingEvaluation + LexicalBinding : BindingIdentifier Initializer + + ... + 5. Return ? InitializeReferencedBinding(lhs, value, hint). + + InitializeReferencedBinding ( V, W ) + + ... + 4. Return ? base.InitializeBinding(V.[[ReferencedName]], W). + + InitializeBinding ( N, V, hint ) + + ... + 2. If hint is not normal, perform ? AddDisposableResource(envRec.[[DisposeCapability]], V, hint). + ... + + 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. + + 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 V is not an Object, throw a TypeError exception. + ii. Set method to ? GetDisposeMethod(V, hint). + iii. If method is undefined, throw a TypeError exception. + 2. Else, + ... + 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. + + 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. + 4. Return func. + +flags: [async] +includes: [asyncHelpers.js] +features: [explicit-resource-management] +---*/ + +asyncTest(async function () { + await assert.throwsAsync(TypeError, async function() { + await using x = { [Symbol.asyncDispose]: true }; + }, 'true'); + + await assert.throwsAsync(TypeError, async function() { + await using x = { [Symbol.asyncDispose]: false }; + }, 'false'); + + await assert.throwsAsync(TypeError, async function() { + await using x = { [Symbol.asyncDispose]: 1 }; + }, 'number'); + + await assert.throwsAsync(TypeError, async function() { + await using x = { [Symbol.asyncDispose]: 'object' }; + }, 'string'); + + var s = Symbol(); + await assert.throwsAsync(TypeError, async function() { + await using x = { [Symbol.asyncDispose]: s }; + }, 'symbol'); +}); diff --git a/test/language/statements/await-using/throws-if-initializer-Symbol.dispose-property-is-null.js b/test/language/statements/await-using/throws-if-initializer-Symbol.dispose-property-is-null.js new file mode 100644 index 00000000000..41c92ed7170 --- /dev/null +++ b/test/language/statements/await-using/throws-if-initializer-Symbol.dispose-property-is-null.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-let-and-const-declarations-runtime-semantics-evaluation +description: Throws if initialized value's Symbol.dispose property is null +info: | + RS: Evaluation + AwaitUsingDeclaration : CoverAwaitExpressionAndAwaitUsingDeclarationHead BindingList ; + + 1. Perform ? BindingEvaluation of BindingList with argument async-dispose. + 2. Return empty. + + RS: BindingEvaluation + LexicalBinding : BindingIdentifier Initializer + + ... + 5. Return ? InitializeReferencedBinding(lhs, value, hint). + + InitializeReferencedBinding ( V, W ) + + ... + 4. Return ? base.InitializeBinding(V.[[ReferencedName]], W). + + InitializeBinding ( N, V, hint ) + + ... + 2. If hint is not normal, perform ? AddDisposableResource(envRec.[[DisposeCapability]], V, hint). + ... + + 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. + + CreateDisposableResource ( V, hint [ , method ] ) + + 1. If method is not present, then + a. If V is either null or undefined, then + ... + b. Else, + i. If V is not an 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. ... + +flags: [async] +includes: [asyncHelpers.js] +features: [explicit-resource-management] +---*/ + +asyncTest(async function () { + await assert.throwsAsync(TypeError, async function () { + await using x = { [Symbol.dispose]: null }; + }); +}); diff --git a/test/language/statements/await-using/throws-if-initializer-Symbol.dispose-property-is-undefined.js b/test/language/statements/await-using/throws-if-initializer-Symbol.dispose-property-is-undefined.js new file mode 100644 index 00000000000..33e3bdac8aa --- /dev/null +++ b/test/language/statements/await-using/throws-if-initializer-Symbol.dispose-property-is-undefined.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-let-and-const-declarations-runtime-semantics-evaluation +description: Throws if initialized value's Symbol.dispose property is undefined +info: | + RS: Evaluation + AwaitUsingDeclaration : CoverAwaitExpressionAndAwaitUsingDeclarationHead BindingList ; + + 1. Perform ? BindingEvaluation of BindingList with argument async-dispose. + 2. Return empty. + + RS: BindingEvaluation + LexicalBinding : BindingIdentifier Initializer + + ... + 5. Return ? InitializeReferencedBinding(lhs, value, hint). + + InitializeReferencedBinding ( V, W ) + + ... + 4. Return ? base.InitializeBinding(V.[[ReferencedName]], W). + + InitializeBinding ( N, V, hint ) + + ... + 2. If hint is not normal, perform ? AddDisposableResource(envRec.[[DisposeCapability]], V, hint). + ... + + 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. + + CreateDisposableResource ( V, hint [ , method ] ) + + 1. If method is not present, then + a. If V is either null or undefined, then + ... + b. Else, + i. If V is not an 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. ... + +flags: [async] +includes: [asyncHelpers.js] +features: [explicit-resource-management] +---*/ + +asyncTest(async function () { + await assert.throwsAsync(TypeError, async function () { + await using x = { [Symbol.dispose]: undefined }; + }); +}); diff --git a/test/language/statements/await-using/throws-if-initializer-Symbol.dispose-property-not-callable.js b/test/language/statements/await-using/throws-if-initializer-Symbol.dispose-property-not-callable.js new file mode 100644 index 00000000000..402dd4b332e --- /dev/null +++ b/test/language/statements/await-using/throws-if-initializer-Symbol.dispose-property-not-callable.js @@ -0,0 +1,99 @@ +// Copyright (C) 2023 Ron Buckton. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-let-and-const-declarations-runtime-semantics-evaluation +description: Throws if initialized value's Symbol.dispose property is not callable +info: | + RS: Evaluation + AwaitUsingDeclaration : CoverAwaitExpressionAndAwaitUsingDeclarationHead BindingList ; + + 1. Perform ? BindingEvaluation of BindingList with argument async-dispose. + 2. Return empty. + + RS: BindingEvaluation + LexicalBinding : BindingIdentifier Initializer + + ... + 5. Return ? InitializeReferencedBinding(lhs, value, hint). + + InitializeReferencedBinding ( V, W ) + + ... + 4. Return ? base.InitializeBinding(V.[[ReferencedName]], W). + + InitializeBinding ( N, V, hint ) + + ... + 2. If hint is not normal, perform ? AddDisposableResource(envRec.[[DisposeCapability]], V, hint). + ... + + 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. + + 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 V is not an Object, throw a TypeError exception. + ii. Set method to ? GetDisposeMethod(V, hint). + iii. If method is undefined, throw a TypeError exception. + 2. Else, + ... + 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. + + 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. + 4. Return func. + +flags: [async] +includes: [asyncHelpers.js] +features: [explicit-resource-management] +---*/ + +asyncTest(async function () { + await assert.throwsAsync(TypeError, async function () { + await using x = { [Symbol.dispose]: true }; + }, 'true'); + + await assert.throwsAsync(TypeError, async function () { + await using x = { [Symbol.dispose]: false }; + }, 'false'); + + await assert.throwsAsync(TypeError, async function () { + await using x = { [Symbol.dispose]: 1 }; + }, 'number'); + + await assert.throwsAsync(TypeError, async function () { + await using x = { [Symbol.dispose]: 'object' }; + }, 'string'); + + var s = Symbol(); + await assert.throwsAsync(TypeError, async function () { + await using x = { [Symbol.dispose]: s }; + }, 'symbol'); +}); diff --git a/test/language/statements/await-using/throws-if-initializer-missing-both-Symbol.asyncDispose-and-Symbol.dispose.js b/test/language/statements/await-using/throws-if-initializer-missing-both-Symbol.asyncDispose-and-Symbol.dispose.js new file mode 100644 index 00000000000..aa01d44c6ee --- /dev/null +++ b/test/language/statements/await-using/throws-if-initializer-missing-both-Symbol.asyncDispose-and-Symbol.dispose.js @@ -0,0 +1,79 @@ +// Copyright (C) 2023 Ron Buckton. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-let-and-const-declarations-runtime-semantics-evaluation +description: Throws if initialized value is missing both the Symbol.asyncDispose and Symbol.dispose properties +info: | + RS: Evaluation + AwaitUsingDeclaration : CoverAwaitExpressionAndAwaitUsingDeclarationHead BindingList ; + + 1. Perform ? BindingEvaluation of BindingList with argument async-dispose. + 2. Return empty. + + RS: BindingEvaluation + BindingList : BindingList , LexicalBinding + + 1. Perform ? BindingEvaluation of BindingList with argument hint. + 2. Perform ? BindingEvaluation of LexicalBinding with argument hint. + 3. Return unused. + + LexicalBinding : BindingIdentifier Initializer + + ... + 5. Return ? InitializeReferencedBinding(lhs, value, hint). + + InitializeReferencedBinding ( V, W ) + + ... + 4. Return ? base.InitializeBinding(V.[[ReferencedName]], W). + + InitializeBinding ( N, V, hint ) + + ... + 2. If hint is not normal, perform ? AddDisposableResource(envRec.[[DisposeCapability]], V, hint). + ... + + 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). + 3. Else, + ... + 3. Append resource to disposeCapability.[[DisposableResourceStack]]. + 4. Return unused. + + CreateDisposableResource ( V, hint [ , method ] ) + + 1. If method is not present, then + a. If V is either null or undefined, then + ... + b. Else, + i. If V is not an 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. + + +flags: [async] +includes: [asyncHelpers.js] +features: [explicit-resource-management] +---*/ + +asyncTest(async function () { + await assert.throwsAsync(TypeError, async function () { + await using x = {}; + }); +}); diff --git a/test/language/statements/await-using/throws-suppressederror-if-multiple-errors-during-disposal.js b/test/language/statements/await-using/throws-suppressederror-if-multiple-errors-during-disposal.js new file mode 100644 index 00000000000..adef24f93a7 --- /dev/null +++ b/test/language/statements/await-using/throws-suppressederror-if-multiple-errors-during-disposal.js @@ -0,0 +1,57 @@ +// Copyright (C) 2023 Ron Buckton. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-disposeresources +description: > + Throws a nested hierarchy of SuppressedErrors if multiple errors were thrown during evaluation of subsequent statements following 'await using' + and/or from disposal. +info: | + 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 {} + const error1 = new MyError(); + const error2 = new MyError(); + const error3 = new MyError(); + + try { + await using _1 = { async [Symbol.asyncDispose]() { throw error1; } }; + await using _2 = { [Symbol.dispose]() { throw error2; } }; + throw error3; + } + catch (e) { + assert(e instanceof SuppressedError, "Expected an SuppressedError to have been thrown"); + assert.sameValue(e.error, error1, "Expected the outermost suppressing error to have been 'error1'"); + assert(e.suppressed instanceof SuppressedError, "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/language/statements/for-await-of/head-await-using-init.js b/test/language/statements/for-await-of/head-await-using-init.js new file mode 100644 index 00000000000..0217069641d --- /dev/null +++ b/test/language/statements/for-await-of/head-await-using-init.js @@ -0,0 +1,21 @@ +// Copyright (C) 2023 Ron Buckton. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. +/*--- +esid: sec-iteration-statements +description: > + ForDeclaration containing 'await using' does not allow initializer. +info: | + IterationStatement: + for await (ForDeclaration of AssignmentExpression) Statement +negative: + phase: parse + type: SyntaxError +features: [async-iteration, explicit-resource-management] +---*/ + +$DONOTEVALUATE(); + +async function fn() { + const obj = { async [Symbol.asyncDispose]() {} }; + for await (await using x = obj of []) {} +} diff --git a/test/language/statements/for-await-of/head-using-init.js b/test/language/statements/for-await-of/head-using-init.js new file mode 100644 index 00000000000..98dabb9ac0c --- /dev/null +++ b/test/language/statements/for-await-of/head-using-init.js @@ -0,0 +1,21 @@ +// Copyright (C) 2023 Ron Buckton. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. +/*--- +esid: sec-iteration-statements +description: > + ForDeclaration containing 'using' does not allow initializer. +info: | + IterationStatement: + for await (ForDeclaration of AssignmentExpression) Statement +negative: + phase: parse + type: SyntaxError +features: [async-iteration, explicit-resource-management] +---*/ + +$DONOTEVALUATE(); + +async function fn() { + const obj = { [Symbol.dispose]() {} }; + for await (using x = obj of []) {} +} diff --git a/test/language/statements/for-of/head-await-using-bound-names-fordecl-tdz.js b/test/language/statements/for-of/head-await-using-bound-names-fordecl-tdz.js new file mode 100644 index 00000000000..af08980f09a --- /dev/null +++ b/test/language/statements/for-of/head-await-using-bound-names-fordecl-tdz.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. +/*--- +es6id: 13.6.4.12_S2 +description: > + ForIn/Of: Bound names of ForDeclaration are in TDZ (for-of) +flags: [async] +includes: [asyncHelpers.js] +features: [explicit-resource-management] +---*/ + +asyncTest(async function () { + await assert.throwsAsync(ReferenceError, async function() { + let x = { async [Symbol.asyncDispose]() { } }; + for (await using x of [x]) {} + }); +}); diff --git a/test/language/statements/for-of/head-await-using-bound-names-in-stmt.js b/test/language/statements/for-of/head-await-using-bound-names-in-stmt.js new file mode 100644 index 00000000000..c28ee12afed --- /dev/null +++ b/test/language/statements/for-of/head-await-using-bound-names-in-stmt.js @@ -0,0 +1,21 @@ +// Copyright (C) 2023 Ron Buckton. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. +/*--- +description: The body may not re-declare variables declared in the head +negative: + phase: parse + type: SyntaxError +info: | + It is a Syntax Error if any element of the BoundNames of ForDeclaration + also occurs in the VarDeclaredNames of Statement. +esid: sec-for-in-and-for-of-statements +es6id: 13.7.5 +flags: [module] +features: [explicit-resource-management] +---*/ + +$DONOTEVALUATE(); + +for (await using x of []) { + var x; +} diff --git a/test/language/statements/for-of/head-await-using-bound-names-let.js b/test/language/statements/for-of/head-await-using-bound-names-let.js new file mode 100644 index 00000000000..bf6b1ef3119 --- /dev/null +++ b/test/language/statements/for-of/head-await-using-bound-names-let.js @@ -0,0 +1,18 @@ +// Copyright (C) 2023 Ron Buckton. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. +/*--- +esid: sec-for-in-and-for-of-statements +description: ForDeclaration containing 'await using' may not contain a binding for `let` +negative: + phase: parse + type: SyntaxError +info: | + It is a Syntax Error if the BoundNames of ForDeclaration contains "let". +flags: [noStrict] +features: [explicit-resource-management] +---*/ + +$DONOTEVALUATE(); +async function f() { + for (await using let of []) {} +} diff --git a/test/language/statements/for-of/head-await-using-fresh-binding-per-iteration.js b/test/language/statements/for-of/head-await-using-fresh-binding-per-iteration.js new file mode 100644 index 00000000000..2b126e5a440 --- /dev/null +++ b/test/language/statements/for-of/head-await-using-fresh-binding-per-iteration.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: pending +description: > + ForDeclaration containing 'await using' creates a fresh binding per iteration +flags: [module] +features: [explicit-resource-management] +---*/ + +let f = [undefined, undefined, undefined]; + +const obj1 = { async [Symbol.asyncDispose]() { } }; +const obj2 = { async [Symbol.asyncDispose]() { } }; +const obj3 = { async [Symbol.asyncDispose]() { } }; + +let i = 0; +for (await using x of [obj1, obj2, obj3]) { + f[i++] = function() { return x; }; +} +assert.sameValue(f[0](), obj1, "`f[0]()` returns `obj1`"); +assert.sameValue(f[1](), obj2, "`f[1]()` returns `obj2`"); +assert.sameValue(f[2](), obj3, "`f[2]()` returns `obj3`"); diff --git a/test/language/statements/for-of/head-await-using-init.js b/test/language/statements/for-of/head-await-using-init.js new file mode 100644 index 00000000000..8760d31b799 --- /dev/null +++ b/test/language/statements/for-of/head-await-using-init.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-iteration-statements +description: > + ForDeclaration containing 'await using' does not support an initializer +info: | + IterationStatement: + for (ForDeclaration of AssignmentExpression) Statement +negative: + phase: parse + type: SyntaxError +features: [explicit-resource-management] +---*/ + +$DONOTEVALUATE(); + +const obj = { [Symbol.dispose]() { } }; +async function f() { + for (await using x = obj of []) {} +} diff --git a/test/language/statements/for-of/head-using-bound-names-fordecl-tdz.js b/test/language/statements/for-of/head-using-bound-names-fordecl-tdz.js new file mode 100644 index 00000000000..f13deef186c --- /dev/null +++ b/test/language/statements/for-of/head-using-bound-names-fordecl-tdz.js @@ -0,0 +1,13 @@ +// Copyright (C) 2023 Ron Buckton. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. +/*--- +es6id: 13.6.4.12_S2 +description: > + ForIn/Of: Bound names of ForDeclaration are in TDZ (for-of) +features: [explicit-resource-management] +---*/ + +assert.throws(ReferenceError, function() { + let x = { [Symbol.dispose]() { } }; + for (using x of [x]) {} +}); diff --git a/test/language/statements/for-of/head-using-bound-names-in-stmt.js b/test/language/statements/for-of/head-using-bound-names-in-stmt.js new file mode 100644 index 00000000000..bdfafc5cd6a --- /dev/null +++ b/test/language/statements/for-of/head-using-bound-names-in-stmt.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. +/*--- +description: The body may not re-declare variables declared in the head +negative: + phase: parse + type: SyntaxError +info: | + It is a Syntax Error if any element of the BoundNames of ForDeclaration + also occurs in the VarDeclaredNames of Statement. +esid: sec-for-in-and-for-of-statements +es6id: 13.7.5 +features: [explicit-resource-management] +---*/ + +$DONOTEVALUATE(); + +for (using x of []) { + var x; +} diff --git a/test/language/statements/for-of/head-using-bound-names-let.js b/test/language/statements/for-of/head-using-bound-names-let.js new file mode 100644 index 00000000000..7d86a15e2ea --- /dev/null +++ b/test/language/statements/for-of/head-using-bound-names-let.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-for-in-and-for-of-statements +description: ForDeclaration containing 'using' may not contain a binding for `let` +negative: + phase: parse + type: SyntaxError +info: | + It is a Syntax Error if the BoundNames of ForDeclaration contains "let". +flags: [noStrict] +features: [explicit-resource-management] +---*/ + +$DONOTEVALUATE(); + +for (using let of []) {} diff --git a/test/language/statements/for-of/head-using-fresh-binding-per-iteration.js b/test/language/statements/for-of/head-using-fresh-binding-per-iteration.js new file mode 100644 index 00000000000..d86077fee9e --- /dev/null +++ b/test/language/statements/for-of/head-using-fresh-binding-per-iteration.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: pending +description: > + ForDeclaration containing 'using' creates a fresh binding per iteration +features: [explicit-resource-management] +---*/ + +let f = [undefined, undefined, undefined]; + +const obj1 = { [Symbol.dispose]() { } }; +const obj2 = { [Symbol.dispose]() { } }; +const obj3 = { [Symbol.dispose]() { } }; + +let i = 0; +for (using x of [obj1, obj2, obj3]) { + f[i++] = function() { return x; }; +} +assert.sameValue(f[0](), obj1, "`f[0]()` returns `obj1`"); +assert.sameValue(f[1](), obj2, "`f[1]()` returns `obj2`"); +assert.sameValue(f[2](), obj3, "`f[2]()` returns `obj3`"); diff --git a/test/language/statements/for-of/head-using-init.js b/test/language/statements/for-of/head-using-init.js new file mode 100644 index 00000000000..4249bba9cf7 --- /dev/null +++ b/test/language/statements/for-of/head-using-init.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-iteration-statements +description: > + ForDeclaration containing 'using' does not support an initializer +info: | + IterationStatement: + for (ForDeclaration of AssignmentExpression) Statement +negative: + phase: parse + type: SyntaxError +features: [explicit-resource-management] +---*/ + +$DONOTEVALUATE(); + +const obj = { [Symbol.dispose]() { } }; +for (using x = obj of []) {} diff --git a/test/language/statements/using/Symbol.dispose-getter.js b/test/language/statements/using/Symbol.dispose-getter.js new file mode 100644 index 00000000000..cb9fa36c037 --- /dev/null +++ b/test/language/statements/using/Symbol.dispose-getter.js @@ -0,0 +1,87 @@ +// Copyright (C) 2023 Ron Buckton. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-let-and-const-declarations-runtime-semantics-evaluation +description: Invokes [Symbol.dispose] getter +info: | + RS: Evaluation + UsingDeclaration : using BindingList ; + + 1. Perform ? BindingEvaluation of BindingList with argument sync-dispose. + 2. Return empty. + + RS: BindingEvaluation + LexicalBinding : BindingIdentifier Initializer + + ... + 5. Return ? InitializeReferencedBinding(lhs, value, hint). + + InitializeReferencedBinding ( V, W ) + + ... + 4. Return ? base.InitializeBinding(V.[[ReferencedName]], W). + + InitializeBinding ( N, V, hint ) + + ... + 2. If hint is not normal, perform ? AddDisposableResource(envRec.[[DisposeCapability]], V, hint). + ... + + 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. + + 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 V is not an Object, throw a TypeError exception. + ii. Set method to ? GetDisposeMethod(V, hint). + iii. If method is undefined, throw a TypeError exception. + 2. Else, + ... + 3. Return the DisposableResource Record { [[ResourceValue]]: V, [[Hint]]: hint, [[DisposeMethod]]: method }. + + GetDisposeMethod ( V, hint ) + + 1. If hint is async-dispose, then + ... + 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. + 4. Return func. + +features: [explicit-resource-management] +---*/ + +var resource = { + disposed: false, + get [Symbol.dispose]() { + return function() { + this.disposed = true; + }; + } +}; + +{ + using _ = resource; +} + +assert.sameValue(resource.disposed, true, 'Expected resource to have been disposed'); diff --git a/test/language/statements/using/Symbol.dispose-method-called-with-correct-this.js b/test/language/statements/using/Symbol.dispose-method-called-with-correct-this.js new file mode 100644 index 00000000000..0759263c16a --- /dev/null +++ b/test/language/statements/using/Symbol.dispose-method-called-with-correct-this.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-block-runtime-semantics-evaluation +description: Initialized value is disposed with the correct 'this' value +info: | + RS: Evaluation + Block : { StatementList } + + ... + 5. Let blockValue be the result of evaluating StatementList. + 6. Set blockValue to DisposeResources(blockEnv.[[DisposeCapability]], blockValue). + ... + + 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). + ... + +features: [explicit-resource-management] +---*/ + +var resource = { + disposed: false, + [Symbol.dispose]() { + assert.sameValue(this, resource); + } +}; + +{ + using _ = resource; +} diff --git a/test/language/statements/using/block-local-closure-get-before-initialization.js b/test/language/statements/using/block-local-closure-get-before-initialization.js new file mode 100644 index 00000000000..469f439932e --- /dev/null +++ b/test/language/statements/using/block-local-closure-get-before-initialization.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-declarative-environment-records-getbindingvalue-n-s +description: > + using: block local closure [[Get]] before initialization. + (TDZ, Temporal Dead Zone) +features: [explicit-resource-management] +---*/ +{ + function f() { return x + 1; } + + assert.throws(ReferenceError, function() { + f(); + }); + + using x = null; +} + diff --git a/test/language/statements/using/block-local-use-before-initialization-in-declaration-statement.js b/test/language/statements/using/block-local-use-before-initialization-in-declaration-statement.js new file mode 100644 index 00000000000..d7c9277d579 --- /dev/null +++ b/test/language/statements/using/block-local-use-before-initialization-in-declaration-statement.js @@ -0,0 +1,16 @@ +// Copyright (C) 2023 Ron Buckton. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-declarative-environment-records-getbindingvalue-n-s +description: > + using: block local use before initialization in declaration statement. + (TDZ, Temporal Dead Zone) +features: [explicit-resource-management] +---*/ + +assert.throws(ReferenceError, function() { + { + using x = x + 1; + } +}); diff --git a/test/language/statements/using/block-local-use-before-initialization-in-prior-statement.js b/test/language/statements/using/block-local-use-before-initialization-in-prior-statement.js new file mode 100644 index 00000000000..3eb788fc7b7 --- /dev/null +++ b/test/language/statements/using/block-local-use-before-initialization-in-prior-statement.js @@ -0,0 +1,16 @@ +// Copyright (C) 2023 Ron Buckton. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-declarative-environment-records-getbindingvalue-n-s +description: > + using: block local use before initialization in prior statement. + (TDZ, Temporal Dead Zone) +features: [explicit-resource-management] +---*/ + +assert.throws(ReferenceError, function() { + { + x; using x = null; + } +}); diff --git a/test/language/statements/using/cptn-value.js b/test/language/statements/using/cptn-value.js new file mode 100644 index 00000000000..72ff9379b97 --- /dev/null +++ b/test/language/statements/using/cptn-value.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-let-and-const-declarations-runtime-semantics-evaluation +description: Returns an empty completion +info: | + UsingDeclaration : using BindingList ; + + 1. Perform ? BindingEvaluation of BindingList with argument sync-dispose. + 2. Return empty. + +features: [explicit-resource-management] +---*/ + +assert.sameValue( + eval('{using test262id1 = null;}'), undefined, 'Single declaration' +); +assert.sameValue( + eval('{using test262id2 = null, test262id3 = null;}'), + undefined, + 'Multiple declarations' +); + +assert.sameValue(eval('4; {using test262id5 = null;}'), 4); +assert.sameValue(eval('6; {using test262id7 = null, test262id8 = null;}'), 6); diff --git a/test/language/statements/using/fn-name-arrow.js b/test/language/statements/using/fn-name-arrow.js new file mode 100644 index 00000000000..8b46e92aa90 --- /dev/null +++ b/test/language/statements/using/fn-name-arrow.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-let-and-const-declarations-runtime-semantics-evaluation +description: Assignment of function `name` attribute (ArrowFunction) +info: | + LexicalBinding : BindingIdentifier Initializer + + ... + 3. If IsAnonymousFunctionDefinition(Initializer) is true, then + a. Let value be NamedEvaluation of Initializer with argument bindingId +includes: [propertyHelper.js] +features: [explicit-resource-management] +---*/ + +// NOTE: only way to verify is to patch `Function.prototype` so as not to trigger a TypeError from AddDisposableResource +Function.prototype[Symbol.dispose] = function () {} +{ + using arrow = () => {}; + + assert.sameValue(arrow.name, 'arrow'); + verifyNotEnumerable(arrow, 'name'); + verifyNotWritable(arrow, 'name'); + verifyConfigurable(arrow, 'name'); +} diff --git a/test/language/statements/using/fn-name-class.js b/test/language/statements/using/fn-name-class.js new file mode 100644 index 00000000000..b9a95fe7a75 --- /dev/null +++ b/test/language/statements/using/fn-name-class.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-let-and-const-declarations-runtime-semantics-evaluation +description: Assignment of function `name` attribute (ClassExpression) +info: | + LexicalBinding : BindingIdentifier Initializer + + ... + 3. If IsAnonymousFunctionDefinition(Initializer) is true, then + a. Let value be NamedEvaluation of Initializer with argument bindingId +includes: [propertyHelper.js] +features: [class, explicit-resource-management] +---*/ +{ + using xCls = class x { static [Symbol.dispose]() {} }; + using cls = class { static [Symbol.dispose]() {} }; + using xCls2 = class { static name() {} static [Symbol.dispose]() {} }; + + assert.notSameValue(xCls.name, 'xCls'); + assert.notSameValue(xCls2.name, 'xCls2'); + + assert.sameValue(cls.name, 'cls'); + verifyNotEnumerable(cls, 'name'); + verifyNotWritable(cls, 'name'); + verifyConfigurable(cls, 'name'); +} diff --git a/test/language/statements/using/fn-name-cover.js b/test/language/statements/using/fn-name-cover.js new file mode 100644 index 00000000000..d87fc7f3e84 --- /dev/null +++ b/test/language/statements/using/fn-name-cover.js @@ -0,0 +1,30 @@ +// Copyright (C) 2023 Ron Buckton. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-let-and-const-declarations-runtime-semantics-evaluation +description: > + Assignment of function `name` attribute (CoverParenthesizedExpression) +info: | + LexicalBinding : BindingIdentifier Initializer + + ... + 3. If IsAnonymousFunctionDefinition(Initializer) is true, then + a. Let value be NamedEvaluation of Initializer with argument bindingId +includes: [propertyHelper.js] +features: [explicit-resource-management] +---*/ + +// NOTE: only way to verify is to patch `Function.prototype` so as not to trigger a TypeError from AddDisposableResource +Function.prototype[Symbol.dispose] = function () {} +{ + using xCover = (0, function() {}); + using cover = (function() {}); + + assert(xCover.name !== 'xCover'); + + assert.sameValue(cover.name, 'cover'); + verifyNotEnumerable(cover, 'name'); + verifyNotWritable(cover, 'name'); + verifyConfigurable(cover, 'name'); +} diff --git a/test/language/statements/using/fn-name-fn.js b/test/language/statements/using/fn-name-fn.js new file mode 100644 index 00000000000..f00e784fe18 --- /dev/null +++ b/test/language/statements/using/fn-name-fn.js @@ -0,0 +1,29 @@ +// Copyright (C) 2023 Ron Buckton. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-let-and-const-declarations-runtime-semantics-evaluation +description: Assignment of function `name` attribute (FunctionExpression) +info: | + LexicalBinding : BindingIdentifier Initializer + + ... + 3. If IsAnonymousFunctionDefinition(Initializer) is true, then + a. Let value be NamedEvaluation of Initializer with argument bindingId +includes: [propertyHelper.js] +features: [explicit-resource-management] +---*/ + +// NOTE: only way to verify is to patch `Function.prototype` so as not to trigger a TypeError from AddDisposableResource +Function.prototype[Symbol.dispose] = function () {} +{ + using xFn = function x() {}; + using fn = function() {}; + + assert(xFn.name !== 'xFn'); + + assert.sameValue(fn.name, 'fn'); + verifyNotEnumerable(fn, 'name'); + verifyNotWritable(fn, 'name'); + verifyConfigurable(fn, 'name'); +} diff --git a/test/language/statements/using/fn-name-gen.js b/test/language/statements/using/fn-name-gen.js new file mode 100644 index 00000000000..63eaa908d35 --- /dev/null +++ b/test/language/statements/using/fn-name-gen.js @@ -0,0 +1,29 @@ +// Copyright (C) 2023 Ron Buckton. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-let-and-const-declarations-runtime-semantics-evaluation +description: Assignment of function `name` attribute (GeneratorExpression) +info: | + LexicalBinding : BindingIdentifier Initializer + + ... + 3. If IsAnonymousFunctionDefinition(Initializer) is true, then + a. Let value be NamedEvaluation of Initializer with argument bindingId +includes: [propertyHelper.js] +features: [generators,explicit-resource-management] +---*/ + +// NOTE: only way to verify is to patch `Function.prototype` so as not to trigger a TypeError from AddDisposableResource +Function.prototype[Symbol.dispose] = function () {} +{ + using xGen = function* x() {}; + using gen = function*() {}; + + assert(xGen.name !== 'xGen'); + + assert.sameValue(gen.name, 'gen'); + verifyNotEnumerable(gen, 'name'); + verifyNotWritable(gen, 'name'); + verifyConfigurable(gen, 'name'); +} diff --git a/test/language/statements/using/function-local-closure-get-before-initialization.js b/test/language/statements/using/function-local-closure-get-before-initialization.js new file mode 100644 index 00000000000..a9455db1565 --- /dev/null +++ b/test/language/statements/using/function-local-closure-get-before-initialization.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-declarative-environment-records-getbindingvalue-n-s +description: > + using: function local closure [[Get]] before initialization. + (TDZ, Temporal Dead Zone) +features: [explicit-resource-management] +---*/ + +(function() { + function f() { return x + 1; } + + assert.throws(ReferenceError, function() { + f(); + }); + + using x = null; +}()); diff --git a/test/language/statements/using/function-local-use-before-initialization-in-declaration-statement.js b/test/language/statements/using/function-local-use-before-initialization-in-declaration-statement.js new file mode 100644 index 00000000000..9551bb03f0a --- /dev/null +++ b/test/language/statements/using/function-local-use-before-initialization-in-declaration-statement.js @@ -0,0 +1,16 @@ +// Copyright (C) 2023 Ron Buckton. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-declarative-environment-records-getbindingvalue-n-s +description: > + using: function local use before initialization in declaration statement. + (TDZ, Temporal Dead Zone) +features: [explicit-resource-management] +---*/ + +assert.throws(ReferenceError, function() { + (function() { + using x = x + 1; + }()); +}); diff --git a/test/language/statements/using/function-local-use-before-initialization-in-prior-statement.js b/test/language/statements/using/function-local-use-before-initialization-in-prior-statement.js new file mode 100644 index 00000000000..bde53e16424 --- /dev/null +++ b/test/language/statements/using/function-local-use-before-initialization-in-prior-statement.js @@ -0,0 +1,16 @@ +// Copyright (C) 2023 Ron Buckton. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-declarative-environment-records-getbindingvalue-n-s +description: > + using: function local use before initialization in prior statement. + (TDZ, Temporal Dead Zone) +features: [explicit-resource-management] +---*/ + +assert.throws(ReferenceError, function() { + (function() { + x; using x = null; + }()); +}); diff --git a/test/language/statements/using/gets-initializer-Symbol.dispose-property-once.js b/test/language/statements/using/gets-initializer-Symbol.dispose-property-once.js new file mode 100644 index 00000000000..18d18f67877 --- /dev/null +++ b/test/language/statements/using/gets-initializer-Symbol.dispose-property-once.js @@ -0,0 +1,84 @@ +// Copyright (C) 2023 Ron Buckton. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-let-and-const-declarations-runtime-semantics-evaluation +description: Only reads `[Symbol.dispose]` method once, when initialized. +info: | + RS: Evaluation + UsingDeclaration : using BindingList ; + + 1. Perform ? BindingEvaluation of BindingList with argument sync-dispose. + 2. Return empty. + + RS: BindingEvaluation + BindingList : BindingList , LexicalBinding + + 1. Perform ? BindingEvaluation of BindingList with argument hint. + 2. Perform ? BindingEvaluation of LexicalBinding with argument hint. + 3. Return unused. + + LexicalBinding : BindingIdentifier Initializer + + ... + 5. Return ? InitializeReferencedBinding(lhs, value, hint). + + InitializeReferencedBinding ( V, W ) + + ... + 4. Return ? base.InitializeBinding(V.[[ReferencedName]], W). + + InitializeBinding ( N, V, hint ) + + ... + 2. If hint is not normal, perform ? AddDisposableResource(envRec.[[DisposeCapability]], V, hint). + ... + + 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. + +features: [explicit-resource-management] +---*/ + +var resource = { + disposeReadCount: 0, + get [Symbol.dispose]() { + this.disposeReadCount++; + return function() { }; + } +}; + +{ + using _ = resource; +} + +assert.sameValue(resource.disposeReadCount, 1, 'Expected [Symbol.dispose] to have been read only once'); diff --git a/test/language/statements/using/global-closure-get-before-initialization.js b/test/language/statements/using/global-closure-get-before-initialization.js new file mode 100644 index 00000000000..e2f6436fac9 --- /dev/null +++ b/test/language/statements/using/global-closure-get-before-initialization.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-declarative-environment-records-getbindingvalue-n-s +description: > + using: global closure [[Get]] before initialization. + (TDZ, Temporal Dead Zone) +features: [explicit-resource-management] +---*/ + +{ + function f() { return x + 1; } + + assert.throws(ReferenceError, function() { + f(); + }); + + using x = null; +} diff --git a/test/language/statements/using/global-use-before-initialization-in-declaration-statement.js b/test/language/statements/using/global-use-before-initialization-in-declaration-statement.js new file mode 100644 index 00000000000..d74401f32c0 --- /dev/null +++ b/test/language/statements/using/global-use-before-initialization-in-declaration-statement.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-declarative-environment-records-getbindingvalue-n-s +description: > + using: global use before initialization in declaration statement. + (TDZ, Temporal Dead Zone) +negative: + phase: runtime + type: ReferenceError +features: [explicit-resource-management] +---*/ + +{ + using x = x + 1; +} diff --git a/test/language/statements/using/global-use-before-initialization-in-prior-statement.js b/test/language/statements/using/global-use-before-initialization-in-prior-statement.js new file mode 100644 index 00000000000..33222306cca --- /dev/null +++ b/test/language/statements/using/global-use-before-initialization-in-prior-statement.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-declarative-environment-records-getbindingvalue-n-s +description: > + using: global use before initialization in prior statement. + (TDZ, Temporal Dead Zone) +negative: + phase: runtime + type: ReferenceError +features: [explicit-resource-management] +---*/ + +{ + x; using x = null; +} diff --git a/test/language/statements/using/initializer-disposed-at-end-of-asyncfunctionbody.js b/test/language/statements/using/initializer-disposed-at-end-of-asyncfunctionbody.js new file mode 100644 index 00000000000..a690233a467 --- /dev/null +++ b/test/language/statements/using/initializer-disposed-at-end-of-asyncfunctionbody.js @@ -0,0 +1,105 @@ +// Copyright (C) 2023 Ron Buckton. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-asyncblockstart +description: Initialized value is disposed at end of AsyncFunctionBody +info: | + AsyncBlockStart ( promiseCapability, asyncBody, asyncContext ) + + 1. Assert: promiseCapability is a PromiseCapability Record. + 2. Let runningContext be the running execution context. + 3. Let closure be a new Abstract Closure with no parameters that captures promiseCapability and asyncBody and performs the following steps when called: + a. Let acAsyncContext be the running execution context. + b. Let result be Completion(Evaluation of asyncBody). + c. Assert: If we return here, the async function either threw an exception or performed an implicit or explicit return; all awaiting is done. + d. Remove acAsyncContext from the execution context stack and restore the execution context that is at the top of the execution context stack as the running execution context. + e. Let env be acAsyncContext's LexicalEnvironment. + f. Set result to DisposeResources(env.[[DisposeCapability]], result). + g. If result.[[Type]] is normal, then + i. Perform ! Call(promiseCapability.[[Resolve]], undefined, « undefined »). + h. Else if result.[[Type]] is return, then + i. Perform ! Call(promiseCapability.[[Resolve]], undefined, « result.[[Value]] »). + i. Else, + i. Assert: result.[[Type]] is throw. + ii. Perform ! Call(promiseCapability.[[Reject]], undefined, « result.[[Value]] »). + j. Return unused. + 4. Set the code evaluation state of asyncContext such that when evaluation is resumed for that execution context, closure will be called with no arguments. + 5. Push asyncContext onto the execution context stack; asyncContext is now the running execution context. + 6. Resume the suspended evaluation of asyncContext. Let result be the value returned by the resumed computation. + 7. Assert: When we return here, asyncContext has already been removed from the execution context stack and runningContext is the currently running execution context. + 8. Assert: result is a normal completion with a value of unused. The possible sources of this value are Await or, if the async function doesn't await anything, step 3.h above. + 9. Return unused. + + 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. ... + 4. Return undefined. + +flags: [async] +includes: [asyncHelpers.js] +features: [explicit-resource-management] +---*/ + +asyncTest(async function () { + + var resource = { + disposed: false, + [Symbol.dispose]() { + this.disposed = true; + } + }; + + var releaseF1; + var suspendFPromise1 = new Promise(function (resolve) { releaseF1 = resolve; }); + + var releaseBody; + var suspendBodyPromise = new Promise(function (resolve) { releaseBody = resolve; }); + + var releaseF2; + var suspendFPromise2 = new Promise(function (resolve) { releaseF2 = resolve; }); + + async function f() { + using _ = resource; + await suspendFPromise1; + releaseBody(); + await suspendFPromise2; + } + + var resultPromise = f(); + + var wasDisposedWhileSuspended1 = resource.disposed; + + releaseF1(); + await suspendBodyPromise; + + var wasDisposedWhileSuspended2 = resource.disposed; + + releaseF2(); + await resultPromise; + + var isDisposedAfterCompleted = resource.disposed; + + assert.sameValue(wasDisposedWhileSuspended1, false, 'Expected resource to not have been disposed while async function is suspended during await'); + assert.sameValue(wasDisposedWhileSuspended2, false, 'Expected resource to not have been disposed while async function is suspended during await'); + assert.sameValue(isDisposedAfterCompleted, true, 'Expected resource to have been disposed after async function completed'); +}); diff --git a/test/language/statements/using/initializer-disposed-at-end-of-asyncgeneratorbody.js b/test/language/statements/using/initializer-disposed-at-end-of-asyncgeneratorbody.js new file mode 100644 index 00000000000..ba2ffe421cb --- /dev/null +++ b/test/language/statements/using/initializer-disposed-at-end-of-asyncgeneratorbody.js @@ -0,0 +1,107 @@ +// Copyright (C) 2023 Ron Buckton. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-asyncgeneratorstart +description: Initialized value is disposed at end of AsyncGeneratorBody +info: | + AsyncGeneratorStart ( generator, generatorBody ) + + 1. Assert: generator.[[AsyncGeneratorState]] is undefined. + 2. Let genContext be the running execution context. + 3. Set the Generator component of genContext to generator. + 4. Let closure be a new Abstract Closure with no parameters that captures generatorBody and performs the following steps when called: + a. Let acGenContext be the running execution context. + b. Let acGenerator be the Generator component of acGenContext. + c. If generatorBody is a Parse Node, then + i. Let result be Completion(Evaluation of generatorBody). + d. Else, + i. Assert: generatorBody is an Abstract Closure with no parameters. + ii. Let result be Completion(generatorBody()). + e. Assert: If we return here, the async generator either threw an exception or performed either an implicit or explicit return. + f. Remove acGenContext from the execution context stack and restore the execution context that is at the top of the execution context stack as the running execution context. + g. Set acGenerator.[[AsyncGeneratorState]] to completed. + h. Let env be genContext's LexicalEnvironment. + i. If env is not undefined, then + i. Assert: env is a Declarative Environment Record + ii. Set result to DisposeResources(env.[[DisposeCapability]], result). + h. If result.[[Type]] is normal, set result to NormalCompletion(undefined). + i. If result.[[Type]] is return, set result to NormalCompletion(result.[[Value]]). + j. Perform AsyncGeneratorCompleteStep(acGenerator, result, true). + k. Perform AsyncGeneratorDrainQueue(acGenerator). + l. Return undefined. + 5. Set the code evaluation state of genContext such that when evaluation is resumed for that execution context, closure will be called with no arguments. + 6. Set generator.[[AsyncGeneratorContext]] to genContext. + 7. Set generator.[[AsyncGeneratorState]] to suspendedStart. + 8. Set generator.[[AsyncGeneratorQueue]] to a new empty List. + 9. Return unused. + + 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. ... + 4. Return undefined. + +flags: [async] +includes: [asyncHelpers.js] +features: [explicit-resource-management] +---*/ + +asyncTest(async function () { + + var resource = { + disposed: false, + [Symbol.dispose]() { + this.disposed = true; + } + }; + + var releaseF; + var suspendFPromise = new Promise(function (resolve) { releaseF = resolve; }); + + async function * f() { + using _ = resource; + yield; + await suspendFPromise; + } + + var g = f(); + + var wasDisposedBeforeAsyncGeneratorStarted = resource.disposed; + + await g.next(); + + var wasDisposedWhileSuspendedForYield = resource.disposed; + + var nextPromise = g.next(); + + var wasDisposedWhileSuspendedForAwait = resource.disposed; + + releaseF(); + await nextPromise; + + var isDisposedAfterCompleted = resource.disposed; + + assert.sameValue(wasDisposedBeforeAsyncGeneratorStarted, false, 'Expected resource to not have been disposed prior to async generator start'); + assert.sameValue(wasDisposedWhileSuspendedForYield, false, 'Expected resource to not have been disposed while async generator function is suspended for yield'); + assert.sameValue(wasDisposedWhileSuspendedForAwait, false, 'Expected resource to not have been disposed while async generator function is suspended during await'); + assert.sameValue(isDisposedAfterCompleted, true, 'Expected resource to have been disposed after async generator function completed'); +}); diff --git a/test/language/statements/using/initializer-disposed-at-end-of-block.js b/test/language/statements/using/initializer-disposed-at-end-of-block.js new file mode 100644 index 00000000000..3ce3a97e652 --- /dev/null +++ b/test/language/statements/using/initializer-disposed-at-end-of-block.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-block-runtime-semantics-evaluation +description: Initialized value is disposed at end of Block +info: | + RS: Evaluation + Block : { StatementList } + + ... + 5. Let blockValue be the result of evaluating StatementList. + 6. Set blockValue to DisposeResources(blockEnv.[[DisposeCapability]], blockValue). + ... + + 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. ... + 4. Return undefined. + +features: [explicit-resource-management] +---*/ + +var resource = { + disposed: false, + [Symbol.dispose]() { + this.disposed = true; + } +}; + +{ + using _ = resource; +} + +assert.sameValue(resource.disposed, true, 'Expected resource to have been disposed'); diff --git a/test/language/statements/using/initializer-disposed-at-end-of-each-iteration-of-forofstatementjs b/test/language/statements/using/initializer-disposed-at-end-of-each-iteration-of-forofstatementjs new file mode 100644 index 00000000000..391643bce0b --- /dev/null +++ b/test/language/statements/using/initializer-disposed-at-end-of-each-iteration-of-forofstatementjs @@ -0,0 +1,80 @@ +// Copyright (C) 2023 Ron Buckton. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-runtime-semantics-forin-div-ofbodyevaluation-lhs-stmt-iterator-lhskind-labelset +description: Initialized value is disposed at end of each iteration of ForOfStatement +info: | + ForIn/OfBodyEvaluation ( lhs, stmt, iteratorRecord, iterationKind, lhsKind, labelSet [ , iteratorKind ] ) + + 1. If iteratorKind is not present, set iteratorKind to sync. + 2. Let oldEnv be the running execution context's LexicalEnvironment. + 3. Let V be undefined. + 4. If IsAwaitUsingDeclaration of lhs is true, then + a. Let hint be async-dispose. + 5. Else, if IsUsingDeclaration of lhs is true, then + a. Let hint be sync-dispose. + 6. Else, + a. Let hint be normal. + 7. Let destructuring be IsDestructuring of lhs. + 8. If destructuring is true and if lhsKind is assignment, then + a. Assert: lhs is a LeftHandSideExpression. + b. Let assignmentPattern be the AssignmentPattern that is covered by lhs. + 9. Repeat, + ... + j. Let result be Completion(Evaluation of stmt). + k. If iterationEnv is not undefined, then + i. Set result to Completion(DisposeResources(iterationEnv.[[DisposeCapability]], result)). + ... + + 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. ... + 4. Return undefined. + +features: [explicit-resource-management] +---*/ + +var resource = { + disposed: false, + [Symbol.dispose]() { + this.disposed = true; + } +}; + +var wasDisposedBeforeBody = false; +var wasDisposedBeforeIteration = false; +var wasDisposedAfterIteration = false; + +function * g() { + wasDisposedBeforeIteration = resource.disposed; + yield resource; + wasDisposedAfterIteration = resource.disposed; +} + +for (using _ of g()) { + wasDisposedBeforeBody = resource.disposed; +} + +assert.sameValue(wasDisposedBeforeIteration, false, 'Expected resource to not been disposed before the for-of loop has received a value'); +assert.sameValue(wasDisposedBeforeBody, false, 'Expected resource to not been disposed while for-of loop is still iterating'); +assert.sameValue(wasDisposedAfterIteration, true, 'Expected resource to have been disposed after the for-of loop advanced to the next value'); diff --git a/test/language/statements/using/initializer-disposed-at-end-of-forstatement.js b/test/language/statements/using/initializer-disposed-at-end-of-forstatement.js new file mode 100644 index 00000000000..8a2dc03df45 --- /dev/null +++ b/test/language/statements/using/initializer-disposed-at-end-of-forstatement.js @@ -0,0 +1,58 @@ +// Copyright (C) 2023 Ron Buckton. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-runtime-semantics-forloopevaluation +description: Initialized value is disposed at end of ForStatement +info: | + RS: ForLoopEvaluation + ForStatement : for ( LexicalDeclaration Expressionopt ; Expressionopt ) Statement + + ... + 12. Let bodyResult be Completion(ForBodyEvaluation(test, increment, Statement, perIterationLets, labelSet)). + 13. Set bodyResult to Completion(DisposeResources(loopEnv.[[DisposeCapability]], bodyResult)). + 14. Assert: If bodyResult.[[Type]] is normal, then bodyResult.[[Value]] is not empty. + ... + + 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. ... + 4. Return undefined. + +features: [explicit-resource-management] +---*/ + +var resource = { + disposed: false, + [Symbol.dispose]() { + this.disposed = true; + } +}; + +var i = 0; +var wasDisposedInForStatement; +for (using _ = resource; i < 1; i++) { + wasDisposedInForStatement = resource.disposed; +} + +assert.sameValue(wasDisposedInForStatement, false, 'Expected resource to not been disposed while for loop is still iterating'); +assert.sameValue(resource.disposed, true, 'Expected resource to have been disposed'); diff --git a/test/language/statements/using/initializer-disposed-at-end-of-functionbody.js b/test/language/statements/using/initializer-disposed-at-end-of-functionbody.js new file mode 100644 index 00000000000..6f498fb0d82 --- /dev/null +++ b/test/language/statements/using/initializer-disposed-at-end-of-functionbody.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-runtime-semantics-evaluatefunctionbody +description: Initialized value is disposed at end of FunctionBody +info: | + FunctionBody : FunctionStatementList + + ... + 3. Let result be Completion(Evaluation of FunctionStatementList). + 4. Let env be the running execution context's LexicalEnvironment. + 5. Return ? DisposeResources(env.[[DisposeCapability]], result). + + 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. ... + 4. Return undefined. + +features: [explicit-resource-management] +---*/ + +var resource = { + disposed: false, + [Symbol.dispose]() { + this.disposed = true; + } +}; + +function f() { + using _ = resource; +} + +f(); + +assert.sameValue(resource.disposed, true, 'Expected resource to have been disposed'); diff --git a/test/language/statements/using/initializer-disposed-at-end-of-generatorbody.js b/test/language/statements/using/initializer-disposed-at-end-of-generatorbody.js new file mode 100644 index 00000000000..a451b291901 --- /dev/null +++ b/test/language/statements/using/initializer-disposed-at-end-of-generatorbody.js @@ -0,0 +1,90 @@ +// Copyright (C) 2023 Ron Buckton. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-generatorstart +description: Initialized value is disposed at end of GeneratorBody +info: | + GeneratorStart ( generator, generatorBody ) + + 1. Assert: The value of generator.[[GeneratorState]] is undefined. + 2. Let genContext be the running execution context. + 3. Set the Generator component of genContext to generator. + 4. Let closure be a new Abstract Closure with no parameters that captures generatorBody and performs the following steps when called: + a. Let acGenContext be the running execution context. + b. Let acGenerator be the Generator component of acGenContext. + c. If generatorBody is a Parse Node, then + i. Let result be Completion(Evaluation of generatorBody). + d. Else, + i. Assert: generatorBody is an Abstract Closure with no parameters. + ii. Let result be generatorBody(). + e. Assert: If we return here, the generator either threw an exception or performed either an implicit or explicit return. + f. Remove acGenContext from the execution context stack and restore the execution context that is at the top of the execution context stack as the running execution context. + g. Set acGenerator.[[GeneratorState]] to completed. + h. NOTE: Once a generator enters the completed state it never leaves it and its associated execution context is never resumed. Any execution state associated with acGenerator can be discarded at this point. + i. Let env be genContext's LexicalEnvironment. + j. If env is not undefined, then + i. Assert: env is a Declarative Environment Record. + i. Set result to DisposeResources(env.[[DisposeCapability]], result). + k. If result.[[Type]] is normal, then + i. Let resultValue be undefined. + l. Else if result.[[Type]] is return, then + i. Let resultValue be result.[[Value]]. + m. Else, + i. Assert: result.[[Type]] is throw. + ii. Return ? result. + n. Return CreateIterResultObject(resultValue, true). + 5. Set the code evaluation state of genContext such that when evaluation is resumed for that execution context, closure will be called with no arguments. + 6. Set generator.[[GeneratorContext]] to genContext. + 7. Set generator.[[GeneratorState]] to suspendedStart. + 8. Return unused. + + 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. ... + 4. Return undefined. + +features: [explicit-resource-management] +---*/ + +var resource = { + disposed: false, + [Symbol.dispose]() { + this.disposed = true; + } +}; + +function * f() { + using _ = resource; + yield; +} + +var g = f(); +var wasDisposedBeforeGeneratorStarted = resource.disposed; +g.next(); +var wasDisposedWhileSuspended = resource.disposed; +assert.sameValue(g.next().done, true); +var isDisposedAfterGeneratorCompleted = resource.disposed; + +assert.sameValue(wasDisposedBeforeGeneratorStarted, false, 'Expected resource to not have been disposed prior to generator start'); +assert.sameValue(wasDisposedWhileSuspended, false, 'Expected resource to not have been disposed while generator is suspended'); +assert.sameValue(isDisposedAfterGeneratorCompleted, true, 'Expected resource to have been disposed after generator completed'); diff --git a/test/language/statements/using/initializer-disposed-at-end-of-switchstatement.js b/test/language/statements/using/initializer-disposed-at-end-of-switchstatement.js new file mode 100644 index 00000000000..dda3a8ca923 --- /dev/null +++ b/test/language/statements/using/initializer-disposed-at-end-of-switchstatement.js @@ -0,0 +1,57 @@ +// Copyright (C) 2023 Ron Buckton. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-switch-statement-runtime-semantics-evaluation +description: Initialized value is disposed at end of a SwitchStatement +info: | + RS: Evaluation + SwitchStatement : switch ( Expression ) CaseBlock + + ... + 7. Let R be Completion(CaseBlockEvaluation of CaseBlock with argument switchValue). + 8. Set R to Completion(DisposeResources(blockEnv.[[DisposeCapability]], R)). + 9. Assert: If R.[[Type]] is normal, then R.[[Value]] is not empty. + .. + + 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. ... + 4. Return undefined. + +features: [explicit-resource-management] +---*/ + +var resource = { + disposed: false, + [Symbol.dispose]() { + this.disposed = true; + } +}; + +switch (0) { + default: + using _ = resource; + break; +} + +assert.sameValue(resource.disposed, true, 'Expected resource to have been disposed'); diff --git a/test/language/statements/using/initializer-disposed-if-subsequent-initializer-throws-in-forstatement-head.js b/test/language/statements/using/initializer-disposed-if-subsequent-initializer-throws-in-forstatement-head.js new file mode 100644 index 00000000000..d773ca81b5c --- /dev/null +++ b/test/language/statements/using/initializer-disposed-if-subsequent-initializer-throws-in-forstatement-head.js @@ -0,0 +1,64 @@ +// Copyright (C) 2023 Ron Buckton. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-runtime-semantics-forloopevaluation +description: Initialized value is disposed at end of FunctionBody +info: | + RS: ForLoopEvaluation + ForStatement : for ( LexicalDeclaration Expressionopt ; Expressionopt ) Statement + + ... + 7. Let forDcl be Completion(Evaluation of LexicalDeclaration). + 8. If forDcl is an abrupt completion, then + a. Set forDcl to Completion(DisposeResources(loopEnv.[[DisposeCapability]], forDcl)). + b. Assert: forDcl is an abrupt completion. + c. Set the running execution context's LexicalEnvironment to oldEnv. + d. Return ? forDcl. + ... + + 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. ... + 4. Return undefined. + +features: [explicit-resource-management] +---*/ + +var resource = { + disposed: false, + [Symbol.dispose]() { + this.disposed = true; + } +}; + +function getResource() { + throw new Error(); +} + +assert.throws(Error, function () { + var i = 0; + for (using _1 = resource, _2 = getResource(); i < 1; i++) { + } +}, 'for'); + +assert.sameValue(resource.disposed, true, 'Expected resource to have been disposed'); diff --git a/test/language/statements/using/initializer-disposed-if-subsequent-initializer-throws.js b/test/language/statements/using/initializer-disposed-if-subsequent-initializer-throws.js new file mode 100644 index 00000000000..8c5a5db821b --- /dev/null +++ b/test/language/statements/using/initializer-disposed-if-subsequent-initializer-throws.js @@ -0,0 +1,58 @@ +// Copyright (C) 2023 Ron Buckton. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-block-runtime-semantics-evaluation +description: Initialized value is disposed even if subsequent initializer throws +info: | + RS: Evaluation + Block : { StatementList } + + ... + 5. Let blockValue be the result of evaluating StatementList. + 6. Set blockValue to DisposeResources(blockEnv.[[DisposeCapability]], blockValue). + ... + + 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. ... + 4. Return undefined. + +features: [explicit-resource-management] +---*/ + +var resource = { + disposed: false, + [Symbol.dispose]() { + this.disposed = true; + } +}; + +function getResource() { + throw new Error(); +} + +assert.throws(Error, function () { + using _1 = resource, _2 = getResource(); +}); + +assert.sameValue(resource.disposed, true, 'Expected resource to have been disposed'); diff --git a/test/language/statements/using/multiple-resources-disposed-in-reverse-order.js b/test/language/statements/using/multiple-resources-disposed-in-reverse-order.js new file mode 100644 index 00000000000..7c7ffc899db --- /dev/null +++ b/test/language/statements/using/multiple-resources-disposed-in-reverse-order.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-disposeresources +description: Multiple resources are disposed in the reverse of the order in which they were added +info: | + RS: Evaluation + Block : { StatementList } + + ... + 5. Let blockValue be the result of evaluating StatementList. + 6. Set blockValue to DisposeResources(blockEnv.[[DisposeCapability]], blockValue). + ... + + 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. ... + 4. Return undefined. + +features: [explicit-resource-management] +---*/ + +var disposed = []; +var resource1 = { [Symbol.dispose]() { disposed.push(this); } }; +var resource2 = { [Symbol.dispose]() { disposed.push(this); } }; +var resource3 = { [Symbol.dispose]() { disposed.push(this); } }; + +{ + using _1 = resource1, _2 = resource2; + using _3 = resource3; +} + +assert.sameValue(disposed[0], resource3); +assert.sameValue(disposed[1], resource2); +assert.sameValue(disposed[2], resource1); diff --git a/test/language/statements/using/puts-initializer-on-top-of-disposableresourcestack-multiple-bindings.js b/test/language/statements/using/puts-initializer-on-top-of-disposableresourcestack-multiple-bindings.js new file mode 100644 index 00000000000..1aede973b5e --- /dev/null +++ b/test/language/statements/using/puts-initializer-on-top-of-disposableresourcestack-multiple-bindings.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-let-and-const-declarations-runtime-semantics-evaluation +description: > + Puts initialized value on the top of the environment's [[DisposableResourceStack]] with multiple lexical bindings + in a single 'using' declaration +info: | + RS: Evaluation + UsingDeclaration : using BindingList ; + + 1. Perform ? BindingEvaluation of BindingList with argument sync-dispose. + 2. Return empty. + + RS: BindingEvaluation + BindingList : BindingList , LexicalBinding + + 1. Perform ? BindingEvaluation of BindingList with argument hint. + 2. Perform ? BindingEvaluation of LexicalBinding with argument hint. + 3. Return unused. + + LexicalBinding : BindingIdentifier Initializer + + ... + 5. Return ? InitializeReferencedBinding(lhs, value, hint). + + InitializeReferencedBinding ( V, W ) + + ... + 4. Return ? base.InitializeBinding(V.[[ReferencedName]], W). + + InitializeBinding ( N, V, hint ) + + ... + 2. If hint is not normal, perform ? AddDisposableResource(envRec.[[DisposeCapability]], V, hint). + ... + + 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). + 3. Else, + ... + 3. Append resource to disposeCapability.[[DisposableResourceStack]]. + 4. Return unused. + +features: [explicit-resource-management] +---*/ + +var disposed = []; +var resource1 = { + [Symbol.dispose]() { + disposed.push(this); + } +}; +var resource2 = { + [Symbol.dispose]() { + disposed.push(this); + } +}; +{ + using r1 = resource1, r2 = resource2; +} +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/language/statements/using/puts-initializer-on-top-of-disposableresourcestack-subsequent-usings.js b/test/language/statements/using/puts-initializer-on-top-of-disposableresourcestack-subsequent-usings.js new file mode 100644 index 00000000000..e04af6525df --- /dev/null +++ b/test/language/statements/using/puts-initializer-on-top-of-disposableresourcestack-subsequent-usings.js @@ -0,0 +1,64 @@ +// Copyright (C) 2023 Ron Buckton. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-let-and-const-declarations-runtime-semantics-evaluation +description: > + Puts initialized value on the top of the environment's [[DisposableResourceStack]] with multiple subsequent 'using' + declarations in the same block scope +info: | + RS: Evaluation + UsingDeclaration : using BindingList ; + + 1. Perform ? BindingEvaluation of BindingList with argument sync-dispose. + 2. Return empty. + + RS: BindingEvaluation + LexicalBinding : BindingIdentifier Initializer + + ... + 5. Return ? InitializeReferencedBinding(lhs, value, hint). + + InitializeReferencedBinding ( V, W ) + + ... + 4. Return ? base.InitializeBinding(V.[[ReferencedName]], W). + + InitializeBinding ( N, V, hint ) + + ... + 2. If hint is not normal, perform ? AddDisposableResource(envRec.[[DisposeCapability]], V, hint). + ... + + 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). + 3. Else, + ... + 3. Append resource to disposeCapability.[[DisposableResourceStack]]. + 4. Return unused. + +features: [explicit-resource-management] +---*/ + +var disposed = []; +var resource1 = { + [Symbol.dispose]() { + disposed.push(this); + } +}; +var resource2 = { + [Symbol.dispose]() { + disposed.push(this); + } +}; +{ + using r1 = resource1; + using r2 = resource2; +} +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/language/statements/using/redeclaration-error-from-within-strict-mode-function-using.js b/test/language/statements/using/redeclaration-error-from-within-strict-mode-function-using.js new file mode 100644 index 00000000000..838423f3595 --- /dev/null +++ b/test/language/statements/using/redeclaration-error-from-within-strict-mode-function-using.js @@ -0,0 +1,16 @@ +// Copyright (C) 2023 Ron Buckton. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-declarative-environment-records-initializebinding-n-v +description: > + Redeclaration error within strict mode function inside non-strict code. +negative: + phase: parse + type: SyntaxError +flags: [noStrict, explicit-resource-management] +---*/ + +$DONOTEVALUATE(); +(function() { 'use strict'; { using f = null; var f; } }) + diff --git a/test/language/statements/using/static-init-await-binding-invalid.js b/test/language/statements/using/static-init-await-binding-invalid.js new file mode 100644 index 00000000000..1a9de298513 --- /dev/null +++ b/test/language/statements/using/static-init-await-binding-invalid.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-class-definitions-static-semantics-early-errors +description: BindingIdentifier may not be `await` within class static blocks +info: | + BindingIdentifier : Identifier + + [...] + - It is a Syntax Error if the code matched by this production is nested, + directly or indirectly (but not crossing function or static initialization + block boundaries), within a ClassStaticBlock and the StringValue of + Identifier is "await". +negative: + phase: parse + type: SyntaxError +features: [class-static-block, explicit-resource-management] +---*/ + +$DONOTEVALUATE(); + +class C { + static { + using await = null; + } +} diff --git a/test/language/statements/using/static-init-await-binding-valid.js b/test/language/statements/using/static-init-await-binding-valid.js new file mode 100644 index 00000000000..62d31d9f79f --- /dev/null +++ b/test/language/statements/using/static-init-await-binding-valid.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-class-definitions-static-semantics-early-errors +description: The `await` keyword is interpreted as an identifier within arrow function bodies +info: | + ClassStaticBlockBody : ClassStaticBlockStatementList + + [...] + - It is a Syntax Error if ContainsAwait of ClassStaticBlockStatementList is true. +features: [class-static-block, explicit-resource-management] +---*/ + +class C { + static { + (() => { using await = null; }); + } +} diff --git a/test/language/statements/using/syntax/block-scope-syntax-using-declarations-mixed-with-without-initializer.js b/test/language/statements/using/syntax/block-scope-syntax-using-declarations-mixed-with-without-initializer.js new file mode 100644 index 00000000000..f57050b3d3f --- /dev/null +++ b/test/language/statements/using/syntax/block-scope-syntax-using-declarations-mixed-with-without-initializer.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: pending +description: > + using declarations mixed: with, without initializer +negative: + phase: parse + type: SyntaxError +features: [explicit-resource-management] +---*/ + +$DONOTEVALUATE(); +{ + using x = null, y; +} diff --git a/test/language/statements/using/syntax/block-scope-syntax-using-declarations-mixed-without-with-initializer.js b/test/language/statements/using/syntax/block-scope-syntax-using-declarations-mixed-without-with-initializer.js new file mode 100644 index 00000000000..8e1c754502d --- /dev/null +++ b/test/language/statements/using/syntax/block-scope-syntax-using-declarations-mixed-without-with-initializer.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: pending +description: > + using declarations mixed: without, with initializer +negative: + phase: parse + type: SyntaxError +features: [explicit-resource-management] +---*/ + +$DONOTEVALUATE(); +{ + using x, y = null; +} diff --git a/test/language/statements/using/syntax/block-scope-syntax-using-declarations-without-initializer.js b/test/language/statements/using/syntax/block-scope-syntax-using-declarations-without-initializer.js new file mode 100644 index 00000000000..31b9c60ed19 --- /dev/null +++ b/test/language/statements/using/syntax/block-scope-syntax-using-declarations-without-initializer.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: pending +description: > + using declarations without initializer +negative: + phase: parse + type: SyntaxError +features: [explicit-resource-management] +---*/ + +$DONOTEVALUATE(); +{ + using x; +} diff --git a/test/language/statements/using/syntax/using-allowed-at-top-level-of-module.js b/test/language/statements/using/syntax/using-allowed-at-top-level-of-module.js new file mode 100644 index 00000000000..c4062e145b0 --- /dev/null +++ b/test/language/statements/using/syntax/using-allowed-at-top-level-of-module.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: pending +description: > + using declarations allowed at the top level of a module +info: | + UsingDeclaration : using BindingList ; + + - It is a Syntax Error if the goal symbol is Script and UsingDeclaration is not contained, either directly or + indirectly, within a Block, CaseBlock, ForStatement, ForInOfStatement, FunctionBody, GeneratorBody, + AsyncGeneratorBody, AsyncFunctionBody, ClassStaticBlockBody, or ClassBody. + +flags: [module] +features: [explicit-resource-management] +---*/ + +using x = null; diff --git a/test/language/statements/using/syntax/using-allows-bindingidentifier.js b/test/language/statements/using/syntax/using-allows-bindingidentifier.js new file mode 100644 index 00000000000..df48a255c1f --- /dev/null +++ b/test/language/statements/using/syntax/using-allows-bindingidentifier.js @@ -0,0 +1,12 @@ +// Copyright (C) 2023 Ron Buckton. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: pending +description: > + 'using' allows BindingIdentifier in lexical bindings +features: [explicit-resource-management] +---*/ +{ + using x = null; +} diff --git a/test/language/statements/using/syntax/using-allows-multiple-bindings.js b/test/language/statements/using/syntax/using-allows-multiple-bindings.js new file mode 100644 index 00000000000..e473785f12d --- /dev/null +++ b/test/language/statements/using/syntax/using-allows-multiple-bindings.js @@ -0,0 +1,13 @@ +// Copyright (C) 2023 Ron Buckton. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: pending +description: > + 'using' allows multiple lexical bindings +features: [explicit-resource-management] +---*/ + +{ + using x = null, y = null; +} diff --git a/test/language/statements/using/syntax/using-declaring-let-split-across-two-lines.js b/test/language/statements/using/syntax/using-declaring-let-split-across-two-lines.js new file mode 100644 index 00000000000..b4c7437f21c --- /dev/null +++ b/test/language/statements/using/syntax/using-declaring-let-split-across-two-lines.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: pending +description: > + using: |using let| split across two lines is treated as two statements. +info: | + Lexical declarations may not declare a binding named "let". +flags: [noStrict] +features: [explicit-resource-management] +---*/ + +{ + using + let = "irrelevant initializer"; + + assert(typeof let === "string"); + var using, let; +} diff --git a/test/language/statements/using/syntax/using-invalid-arraybindingpattern-after-bindingidentifier.js b/test/language/statements/using/syntax/using-invalid-arraybindingpattern-after-bindingidentifier.js new file mode 100644 index 00000000000..2c74f1ad0e5 --- /dev/null +++ b/test/language/statements/using/syntax/using-invalid-arraybindingpattern-after-bindingidentifier.js @@ -0,0 +1,18 @@ +// Copyright (C) 2023 Ron Buckton. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: pending +description: > + 'using' does not allow ArrayBindingPattern in lexical bindings, even after a valid lexical binding +negative: + phase: parse + type: SyntaxError +features: [explicit-resource-management] +---*/ + +$DONOTEVALUATE(); + +{ + using x = null, [] = null; +} diff --git a/test/language/statements/using/syntax/using-invalid-arraybindingpattern-does-not-break-element-access.js b/test/language/statements/using/syntax/using-invalid-arraybindingpattern-does-not-break-element-access.js new file mode 100644 index 00000000000..8904f57bc5d --- /dev/null +++ b/test/language/statements/using/syntax/using-invalid-arraybindingpattern-does-not-break-element-access.js @@ -0,0 +1,15 @@ +// Copyright (C) 2023 Ron Buckton. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: pending +description: > + 'using' does not break existing element access +features: [explicit-resource-management] +---*/ + +var using = [], x = 0; + +{ + using[x] = null; +} diff --git a/test/language/statements/using/syntax/using-invalid-arraybindingpattern.js b/test/language/statements/using/syntax/using-invalid-arraybindingpattern.js new file mode 100644 index 00000000000..9106861b96c --- /dev/null +++ b/test/language/statements/using/syntax/using-invalid-arraybindingpattern.js @@ -0,0 +1,18 @@ +// Copyright (C) 2023 Ron Buckton. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: pending +description: > + 'using' does not allow ArrayBindingPattern in lexical bindings +negative: + phase: parse + type: SyntaxError +features: [explicit-resource-management] +---*/ + +$DONOTEVALUATE(); + +{ + using [] = null; +} diff --git a/test/language/statements/using/syntax/using-invalid-assignment-next-expression-for.js b/test/language/statements/using/syntax/using-invalid-assignment-next-expression-for.js new file mode 100644 index 00000000000..78bfeed05f8 --- /dev/null +++ b/test/language/statements/using/syntax/using-invalid-assignment-next-expression-for.js @@ -0,0 +1,13 @@ +// Copyright (C) 2023 Ron Buckton. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: pending +description: > + using: invalid assignment in next expression +features: [explicit-resource-management] +---*/ + +assert.throws(TypeError, function() { + for (using i = 0; i < 1; i++) {} +}); diff --git a/test/language/statements/using/syntax/using-invalid-assignment-statement-body-for-of.js b/test/language/statements/using/syntax/using-invalid-assignment-statement-body-for-of.js new file mode 100644 index 00000000000..cd9c5628159 --- /dev/null +++ b/test/language/statements/using/syntax/using-invalid-assignment-statement-body-for-of.js @@ -0,0 +1,13 @@ +// Copyright (C) 2023 Ron Buckton. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: pending +description: > + using: invalid assignment in Statement body +features: [explicit-resource-management] +---*/ + +assert.throws(TypeError, function() { + for (using x of [1, 2, 3]) { x++ } +}); diff --git a/test/language/statements/using/syntax/using-invalid-for-in.js b/test/language/statements/using/syntax/using-invalid-for-in.js new file mode 100644 index 00000000000..6bc449b8345 --- /dev/null +++ b/test/language/statements/using/syntax/using-invalid-for-in.js @@ -0,0 +1,14 @@ +// Copyright (C) 2011 the V8 project authors. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. +/*--- +esid: pending +description: > + using: not allowed in for..in +negative: + phase: parse + type: SyntaxError +features: [explicit-resource-management] +---*/ + +$DONOTEVALUATE(); +for (using x in [1, 2, 3]) { } diff --git a/test/language/statements/using/syntax/using-invalid-for-using-of-of.js b/test/language/statements/using/syntax/using-invalid-for-using-of-of.js new file mode 100644 index 00000000000..54d6a038123 --- /dev/null +++ b/test/language/statements/using/syntax/using-invalid-for-using-of-of.js @@ -0,0 +1,14 @@ +// Copyright (C) 2011 the V8 project authors. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. +/*--- +esid: pending +description: > + using: 'for (using of' is always interpreted as identifier +negative: + phase: parse + type: SyntaxError +features: [explicit-resource-management] +---*/ + +$DONOTEVALUATE(); +for (using of of [1, 2, 3]) { } diff --git a/test/language/statements/using/syntax/using-invalid-objectbindingpattern-after-bindingidentifier.js b/test/language/statements/using/syntax/using-invalid-objectbindingpattern-after-bindingidentifier.js new file mode 100644 index 00000000000..609f9ff3cd5 --- /dev/null +++ b/test/language/statements/using/syntax/using-invalid-objectbindingpattern-after-bindingidentifier.js @@ -0,0 +1,18 @@ +// Copyright (C) 2023 Ron Buckton. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: pending +description: > + 'using' does not allow ObjectBindingPattern in lexical bindings, even after a valid lexical binding +negative: + phase: parse + type: SyntaxError +features: [explicit-resource-management] +---*/ + +$DONOTEVALUATE(); + +{ + using x = null, {} = null; +} diff --git a/test/language/statements/using/syntax/using-invalid-objectbindingpattern.js b/test/language/statements/using/syntax/using-invalid-objectbindingpattern.js new file mode 100644 index 00000000000..57d85ed5072 --- /dev/null +++ b/test/language/statements/using/syntax/using-invalid-objectbindingpattern.js @@ -0,0 +1,18 @@ +// Copyright (C) 2023 Ron Buckton. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: pending +description: > + 'using' does not allow ObjectBindingPattern in lexical bindings +negative: + phase: parse + type: SyntaxError +features: [explicit-resource-management] +---*/ + +$DONOTEVALUATE(); + +{ + using {} = null; +} diff --git a/test/language/statements/using/syntax/using-not-allowed-at-top-level-of-eval.js b/test/language/statements/using/syntax/using-not-allowed-at-top-level-of-eval.js new file mode 100644 index 00000000000..eaca27450c4 --- /dev/null +++ b/test/language/statements/using/syntax/using-not-allowed-at-top-level-of-eval.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: pending +description: > + using declarations not allowed at the top level of eval +info: | + UsingDeclaration : using BindingList ; + + - It is a Syntax Error if the goal symbol is Script and UsingDeclaration is not contained, either directly or + indirectly, within a Block, CaseBlock, ForStatement, ForInOfStatement, FunctionBody, GeneratorBody, + AsyncGeneratorBody, AsyncFunctionBody, ClassStaticBlockBody, or ClassBody. + +features: [explicit-resource-management] +---*/ + +assert.throws(SyntaxError, function() { + eval('using x = null;') +}); diff --git a/test/language/statements/using/syntax/using-not-allowed-at-top-level-of-script.js b/test/language/statements/using/syntax/using-not-allowed-at-top-level-of-script.js new file mode 100644 index 00000000000..9ad239809b1 --- /dev/null +++ b/test/language/statements/using/syntax/using-not-allowed-at-top-level-of-script.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: pending +description: > + using declarations not allowed at the top level of a Script +info: | + UsingDeclaration : using BindingList ; + + - It is a Syntax Error if the goal symbol is Script and UsingDeclaration is not contained, either directly or + indirectly, within a Block, CaseBlock, ForStatement, ForInOfStatement, FunctionBody, GeneratorBody, + AsyncGeneratorBody, AsyncFunctionBody, ClassStaticBlockBody, or ClassBody. + +negative: + phase: parse + type: SyntaxError +features: [explicit-resource-management] +---*/ + +$DONOTEVALUATE(); +using x = null; diff --git a/test/language/statements/using/syntax/using-outer-inner-using-bindings.js b/test/language/statements/using/syntax/using-outer-inner-using-bindings.js new file mode 100644 index 00000000000..a816fa5f8a5 --- /dev/null +++ b/test/language/statements/using/syntax/using-outer-inner-using-bindings.js @@ -0,0 +1,29 @@ +// Copyright (C) 2023 Ron Buckton. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: pending +description: > + outer using binding unchanged by for-loop using binding +features: [explicit-resource-management] +---*/ + +const outer_x = { [Symbol.dispose]() {} }; +const outer_y = { [Symbol.dispose]() {} }; +const inner_x = { [Symbol.dispose]() {} }; +const inner_y = { [Symbol.dispose]() {} }; + +{ + using x = outer_x; + using y = outer_y; + var i = 0; + + for (using x = inner_x; i < 1; i++) { + using y = inner_y; + + assert.sameValue(x, inner_x); + assert.sameValue(y, inner_y); + } + assert.sameValue(x, outer_x); + assert.sameValue(y, outer_y); +} diff --git a/test/language/statements/using/syntax/using.js b/test/language/statements/using/syntax/using.js new file mode 100644 index 00000000000..f4af38dbc53 --- /dev/null +++ b/test/language/statements/using/syntax/using.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: pending +description: > + module and block scope using +flags: [module] +features: [explicit-resource-management] +---*/ + +using z = null; + +// Block local +{ + using z = undefined; +} + +assert.sameValue(z, null); + +if (true) { + const obj = { [Symbol.dispose]() { } }; + using z = obj; + assert.sameValue(z, obj); +} diff --git a/test/language/statements/using/syntax/with-initializer-case-expression-statement-list.js b/test/language/statements/using/syntax/with-initializer-case-expression-statement-list.js new file mode 100644 index 00000000000..b57997fc68a --- /dev/null +++ b/test/language/statements/using/syntax/with-initializer-case-expression-statement-list.js @@ -0,0 +1,11 @@ +// Copyright (C) 2023 Ron Buckton. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: pending +description: > + using declarations with initialisers in statement positions: + case Expression : StatementList +features: [explicit-resource-management] +---*/ +switch (true) { case true: using x = null; } diff --git a/test/language/statements/using/syntax/with-initializer-default-statement-list.js b/test/language/statements/using/syntax/with-initializer-default-statement-list.js new file mode 100644 index 00000000000..68c11363c35 --- /dev/null +++ b/test/language/statements/using/syntax/with-initializer-default-statement-list.js @@ -0,0 +1,11 @@ +// Copyright (C) 2023 Ron Buckton. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: pending +description: > + using declarations with initialisers in statement positions: + default : StatementList +features: [explicit-resource-management] +---*/ +switch (true) { default: using x = null; } diff --git a/test/language/statements/using/syntax/with-initializer-do-statement-while-expression.js b/test/language/statements/using/syntax/with-initializer-do-statement-while-expression.js new file mode 100644 index 00000000000..33737f4ef36 --- /dev/null +++ b/test/language/statements/using/syntax/with-initializer-do-statement-while-expression.js @@ -0,0 +1,16 @@ +// Copyright (C) 2023 Ron Buckton. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: pending +description: > + using declarations with initialisers in statement positions: + do Statement while ( Expression ) +negative: + phase: parse + type: SyntaxError +features: [explicit-resource-management] +---*/ + +$DONOTEVALUATE(); +do using x = 1; while (false) diff --git a/test/language/statements/using/syntax/with-initializer-for-statement.js b/test/language/statements/using/syntax/with-initializer-for-statement.js new file mode 100644 index 00000000000..ddff126ad3d --- /dev/null +++ b/test/language/statements/using/syntax/with-initializer-for-statement.js @@ -0,0 +1,16 @@ +// Copyright (C) 2023 Ron Buckton. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: pending +description: > + using declarations with initialisers in statement positions: + for (;;) Statement +negative: + phase: parse + type: SyntaxError +features: [explicit-resource-management] +---*/ + +$DONOTEVALUATE(); +for (;false;) using x = null; diff --git a/test/language/statements/using/syntax/with-initializer-if-expression-statement-else-statement.js b/test/language/statements/using/syntax/with-initializer-if-expression-statement-else-statement.js new file mode 100644 index 00000000000..95372d54920 --- /dev/null +++ b/test/language/statements/using/syntax/with-initializer-if-expression-statement-else-statement.js @@ -0,0 +1,16 @@ +// Copyright (C) 2023 Ron Buckton. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: pending +description: > + using declarations with initialisers in statement positions: + if ( Expression ) Statement else Statement +negative: + phase: parse + type: SyntaxError +features: [explicit-resource-management] +---*/ + +$DONOTEVALUATE(); +if (true) {} else using x = null; diff --git a/test/language/statements/using/syntax/with-initializer-if-expression-statement.js b/test/language/statements/using/syntax/with-initializer-if-expression-statement.js new file mode 100644 index 00000000000..714fb650d7c --- /dev/null +++ b/test/language/statements/using/syntax/with-initializer-if-expression-statement.js @@ -0,0 +1,16 @@ +// Copyright (C) 2023 Ron Buckton. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: pending +description: > + using declarations with initialisers in statement positions: + if ( Expression ) Statement +negative: + phase: parse + type: SyntaxError +features: [explicit-resource-management] +---*/ + +$DONOTEVALUATE(); +if (true) using x = null; diff --git a/test/language/statements/using/syntax/with-initializer-label-statement.js b/test/language/statements/using/syntax/with-initializer-label-statement.js new file mode 100644 index 00000000000..ad1661f757b --- /dev/null +++ b/test/language/statements/using/syntax/with-initializer-label-statement.js @@ -0,0 +1,16 @@ +// Copyright (C) 2023 Ron Buckton. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: pending +description: > + using declarations with initialisers in statement positions: + label: Statement +negative: + phase: parse + type: SyntaxError +features: [explicit-resource-management] +---*/ + +$DONOTEVALUATE(); +label: using x = null; diff --git a/test/language/statements/using/syntax/with-initializer-while-expression-statement.js b/test/language/statements/using/syntax/with-initializer-while-expression-statement.js new file mode 100644 index 00000000000..0dded0dc27b --- /dev/null +++ b/test/language/statements/using/syntax/with-initializer-while-expression-statement.js @@ -0,0 +1,16 @@ +// Copyright (C) 2023 Ron Buckton. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: pending +description: > + using declarations with initialisers in statement positions: + while ( Expression ) Statement +negative: + phase: parse + type: SyntaxError +features: [explicit-resource-management] +---*/ + +$DONOTEVALUATE(); +while (false) using x = null; diff --git a/test/language/statements/using/syntax/without-initializer-case-expression-statement-list.js b/test/language/statements/using/syntax/without-initializer-case-expression-statement-list.js new file mode 100644 index 00000000000..00d8e6a15c8 --- /dev/null +++ b/test/language/statements/using/syntax/without-initializer-case-expression-statement-list.js @@ -0,0 +1,16 @@ +// Copyright (C) 2023 Ron Buckton. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: pending +description: > + using declarations without initialisers in statement positions: + case Expression : StatementList +negative: + phase: parse + type: SyntaxError +features: [explicit-resource-management] +---*/ + +$DONOTEVALUATE(); +switch (true) { case true: using x; } diff --git a/test/language/statements/using/syntax/without-initializer-default-statement-list.js b/test/language/statements/using/syntax/without-initializer-default-statement-list.js new file mode 100644 index 00000000000..d42a5a33c87 --- /dev/null +++ b/test/language/statements/using/syntax/without-initializer-default-statement-list.js @@ -0,0 +1,16 @@ +// Copyright (C) 2023 Ron Buckton. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: pending +description: > + using declarations without initialisers in statement positions: + default : StatementList +negative: + phase: parse + type: SyntaxError +features: [explicit-resource-management] +---*/ + +$DONOTEVALUATE(); +switch (true) { default: using x; } diff --git a/test/language/statements/using/syntax/without-initializer-do-statement-while-expression.js b/test/language/statements/using/syntax/without-initializer-do-statement-while-expression.js new file mode 100644 index 00000000000..a696f47d581 --- /dev/null +++ b/test/language/statements/using/syntax/without-initializer-do-statement-while-expression.js @@ -0,0 +1,16 @@ +// Copyright (C) 2023 Ron Buckton. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: pending +description: > + using declarations without initialisers in statement positions: + do Statement while ( Expression ) +negative: + phase: parse + type: SyntaxError +features: [explicit-resource-management] +---*/ + +$DONOTEVALUATE(); +do using x; while (false) diff --git a/test/language/statements/using/syntax/without-initializer-for-statement.js b/test/language/statements/using/syntax/without-initializer-for-statement.js new file mode 100644 index 00000000000..c22df3b55ec --- /dev/null +++ b/test/language/statements/using/syntax/without-initializer-for-statement.js @@ -0,0 +1,16 @@ +// Copyright (C) 2023 Ron Buckton. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: pending +description: > + using declarations without initialisers in statement positions: + for ( ;;) Statement +negative: + phase: parse + type: SyntaxError +features: [explicit-resource-management] +---*/ + +$DONOTEVALUATE(); +for (;false;) using x; diff --git a/test/language/statements/using/syntax/without-initializer-if-expression-statement-else-statement.js b/test/language/statements/using/syntax/without-initializer-if-expression-statement-else-statement.js new file mode 100644 index 00000000000..ef283c6710a --- /dev/null +++ b/test/language/statements/using/syntax/without-initializer-if-expression-statement-else-statement.js @@ -0,0 +1,16 @@ +// Copyright (C) 2023 Ron Buckton. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: pending +description: > + using declarations without initialisers in statement positions: + if ( Expression ) Statement else Statement +negative: + phase: parse + type: SyntaxError +features: [explicit-resource-management] +---*/ + +$DONOTEVALUATE(); +if (true) {} else using x; diff --git a/test/language/statements/using/syntax/without-initializer-if-expression-statement.js b/test/language/statements/using/syntax/without-initializer-if-expression-statement.js new file mode 100644 index 00000000000..c4871b8af14 --- /dev/null +++ b/test/language/statements/using/syntax/without-initializer-if-expression-statement.js @@ -0,0 +1,16 @@ +// Copyright (C) 2023 Ron Buckton. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: pending +description: > + using declarations without initialisers in statement positions: + if ( Expression ) Statement +negative: + phase: parse + type: SyntaxError +features: [explicit-resource-management] +---*/ + +$DONOTEVALUATE(); +if (true) using x; diff --git a/test/language/statements/using/syntax/without-initializer-label-statement.js b/test/language/statements/using/syntax/without-initializer-label-statement.js new file mode 100644 index 00000000000..bd04fb3a8fe --- /dev/null +++ b/test/language/statements/using/syntax/without-initializer-label-statement.js @@ -0,0 +1,16 @@ +// Copyright (C) 2023 Ron Buckton. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: pending +description: > + using declarations without initialisers in statement positions: + label: Statement +negative: + phase: parse + type: SyntaxError +features: [explicit-resource-management] +---*/ + +$DONOTEVALUATE(); +label: using x; diff --git a/test/language/statements/using/syntax/without-initializer-while-expression-statement.js b/test/language/statements/using/syntax/without-initializer-while-expression-statement.js new file mode 100644 index 00000000000..c498de1d4b7 --- /dev/null +++ b/test/language/statements/using/syntax/without-initializer-while-expression-statement.js @@ -0,0 +1,16 @@ +// Copyright (C) 2023 Ron Buckton. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: pending +description: > + using declarations without initialisers in statement positions: + while ( Expression ) Statement +negative: + phase: parse + type: SyntaxError +features: [explicit-resource-management] +---*/ + +$DONOTEVALUATE(); +while (false) using x; diff --git a/test/language/statements/using/throws-error-as-is-if-only-one-error-during-disposal.js b/test/language/statements/using/throws-error-as-is-if-only-one-error-during-disposal.js new file mode 100644 index 00000000000..98e65f4ef50 --- /dev/null +++ b/test/language/statements/using/throws-error-as-is-if-only-one-error-during-disposal.js @@ -0,0 +1,52 @@ +// Copyright (C) 2023 Ron Buckton. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-disposeresources +description: > + Rethrows an error as-is if it is the only error thrown during evaluation of subsequent statements following 'using' + or from disposal. +info: | + 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] +---*/ + +class MyError extends Error {} +assert.throws(MyError, function () { + using _1 = { [Symbol.dispose]() { throw new MyError(); } }; + using _2 = { [Symbol.dispose]() { } }; +}); + +assert.throws(MyError, function () { + using _1 = { [Symbol.dispose]() { } }; + using _2 = { [Symbol.dispose]() { throw new MyError(); } }; +}); + +assert.throws(MyError, function () { + using _1 = { [Symbol.dispose]() { } }; + using _2 = { [Symbol.dispose]() { } }; + throw new MyError(); +}); diff --git a/test/language/statements/using/throws-if-initializer-Symbol.dispose-property-is-null.js b/test/language/statements/using/throws-if-initializer-Symbol.dispose-property-is-null.js new file mode 100644 index 00000000000..75172e39283 --- /dev/null +++ b/test/language/statements/using/throws-if-initializer-Symbol.dispose-property-is-null.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-let-and-const-declarations-runtime-semantics-evaluation +description: Throws if initialized value's Symbol.dispose property is null +info: | + RS: Evaluation + UsingDeclaration : using BindingList ; + + 1. Perform ? BindingEvaluation of BindingList with argument sync-dispose. + 2. Return empty. + + RS: BindingEvaluation + LexicalBinding : BindingIdentifier Initializer + + ... + 5. Return ? InitializeReferencedBinding(lhs, value, hint). + + InitializeReferencedBinding ( V, W ) + + ... + 4. Return ? base.InitializeBinding(V.[[ReferencedName]], W). + + InitializeBinding ( N, V, hint ) + + ... + 2. If hint is not normal, perform ? AddDisposableResource(envRec.[[DisposeCapability]], V, hint). + ... + + 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. + + CreateDisposableResource ( V, hint [ , method ] ) + + 1. If method is not present, then + a. If V is either null or undefined, then + ... + b. Else, + i. If V is not an 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 + ... + 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] +---*/ + +assert.throws(TypeError, function () { + using x = { [Symbol.dispose]: null }; +}); diff --git a/test/language/statements/using/throws-if-initializer-Symbol.dispose-property-is-undefined.js b/test/language/statements/using/throws-if-initializer-Symbol.dispose-property-is-undefined.js new file mode 100644 index 00000000000..fb496778c0b --- /dev/null +++ b/test/language/statements/using/throws-if-initializer-Symbol.dispose-property-is-undefined.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-let-and-const-declarations-runtime-semantics-evaluation +description: Throws if initialized value's Symbol.dispose property is undefined +info: | + RS: Evaluation + UsingDeclaration : using BindingList ; + + 1. Perform ? BindingEvaluation of BindingList with argument sync-dispose. + 2. Return empty. + + RS: BindingEvaluation + LexicalBinding : BindingIdentifier Initializer + + ... + 5. Return ? InitializeReferencedBinding(lhs, value, hint). + + InitializeReferencedBinding ( V, W ) + + ... + 4. Return ? base.InitializeBinding(V.[[ReferencedName]], W). + + InitializeBinding ( N, V, hint ) + + ... + 2. If hint is not normal, perform ? AddDisposableResource(envRec.[[DisposeCapability]], V, hint). + ... + + 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. + + CreateDisposableResource ( V, hint [ , method ] ) + + 1. If method is not present, then + a. If V is either null or undefined, then + ... + b. Else, + i. If V is not an 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 + ... + 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] +---*/ + +assert.throws(TypeError, function () { + using x = { [Symbol.dispose]: undefined }; +}); diff --git a/test/language/statements/using/throws-if-initializer-Symbol.dispose-property-not-callable.js b/test/language/statements/using/throws-if-initializer-Symbol.dispose-property-not-callable.js new file mode 100644 index 00000000000..c804d80af21 --- /dev/null +++ b/test/language/statements/using/throws-if-initializer-Symbol.dispose-property-not-callable.js @@ -0,0 +1,93 @@ +// Copyright (C) 2023 Ron Buckton. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-let-and-const-declarations-runtime-semantics-evaluation +description: Throws if initialized value's Symbol.dispose property is not callable +info: | + RS: Evaluation + UsingDeclaration : using BindingList ; + + 1. Perform ? BindingEvaluation of BindingList with argument sync-dispose. + 2. Return empty. + + RS: BindingEvaluation + LexicalBinding : BindingIdentifier Initializer + + ... + 5. Return ? InitializeReferencedBinding(lhs, value, hint). + + InitializeReferencedBinding ( V, W ) + + ... + 4. Return ? base.InitializeBinding(V.[[ReferencedName]], W). + + InitializeBinding ( N, V, hint ) + + ... + 2. If hint is not normal, perform ? AddDisposableResource(envRec.[[DisposeCapability]], V, hint). + ... + + 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. + + 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 V is not an Object, throw a TypeError exception. + ii. Set method to ? GetDisposeMethod(V, hint). + iii. If method is undefined, throw a TypeError exception. + 2. Else, + ... + 3. Return the DisposableResource Record { [[ResourceValue]]: V, [[Hint]]: hint, [[DisposeMethod]]: method }. + + GetDisposeMethod ( V, hint ) + + 1. If hint is async-dispose, then + ... + 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. + 4. Return func. + +features: [explicit-resource-management] +---*/ + +assert.throws(TypeError, function() { + using x = { [Symbol.dispose]: true }; +}, 'true'); + +assert.throws(TypeError, function() { + using x = { [Symbol.dispose]: false }; +}, 'false'); + +assert.throws(TypeError, function() { + using x = { [Symbol.dispose]: 1 }; +}, 'number'); + +assert.throws(TypeError, function() { + using x = { [Symbol.dispose]: 'object' }; +}, 'string'); + +var s = Symbol(); +assert.throws(TypeError, function() { + using x = { [Symbol.dispose]: s }; +}, 'symbol'); diff --git a/test/language/statements/using/throws-if-initializer-missing-Symbol.dispose.js b/test/language/statements/using/throws-if-initializer-missing-Symbol.dispose.js new file mode 100644 index 00000000000..9ad18b502ba --- /dev/null +++ b/test/language/statements/using/throws-if-initializer-missing-Symbol.dispose.js @@ -0,0 +1,65 @@ +// Copyright (C) 2023 Ron Buckton. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-let-and-const-declarations-runtime-semantics-evaluation +description: Throws if initialized value is missing Symbol.dispose property +info: | + RS: Evaluation + UsingDeclaration : using BindingList ; + + 1. Perform ? BindingEvaluation of BindingList with argument sync-dispose. + 2. Return empty. + + RS: BindingEvaluation + BindingList : BindingList , LexicalBinding + + 1. Perform ? BindingEvaluation of BindingList with argument hint. + 2. Perform ? BindingEvaluation of LexicalBinding with argument hint. + 3. Return unused. + + LexicalBinding : BindingIdentifier Initializer + + ... + 5. Return ? InitializeReferencedBinding(lhs, value, hint). + + InitializeReferencedBinding ( V, W ) + + ... + 4. Return ? base.InitializeBinding(V.[[ReferencedName]], W). + + InitializeBinding ( N, V, hint ) + + ... + 2. If hint is not normal, perform ? AddDisposableResource(envRec.[[DisposeCapability]], V, hint). + ... + + 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). + 3. Else, + ... + 3. Append resource to disposeCapability.[[DisposableResourceStack]]. + 4. Return unused. + + CreateDisposableResource ( V, hint [ , method ] ) + + 1. If method is not present, then + a. If V is either null or undefined, then + ... + b. Else, + i. If V is not an Object, throw a TypeError exception. + ii. Set method to ? GetDisposeMethod(V, hint). + iii. If method is undefined, throw a TypeError exception. + ... + + +features: [explicit-resource-management] +---*/ + +assert.throws(TypeError, function () { + using x = {}; +}); diff --git a/test/language/statements/using/throws-suppressederror-if-multiple-errors-during-disposal.js b/test/language/statements/using/throws-suppressederror-if-multiple-errors-during-disposal.js new file mode 100644 index 00000000000..1d512b20f59 --- /dev/null +++ b/test/language/statements/using/throws-suppressederror-if-multiple-errors-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-disposeresources +description: > + Throws a nested hierarchy of SuppressedErrors if multiple errors were thrown during evaluation of subsequent statements following 'using' + and/or from disposal. +info: | + 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] +---*/ + +class MyError extends Error {} +const error1 = new MyError(); +const error2 = new MyError(); +const error3 = new MyError(); + +try { + using _1 = { [Symbol.dispose]() { throw error1; } }; + using _2 = { [Symbol.dispose]() { throw error2; } }; + throw error3; +} +catch (e) { + assert(e instanceof SuppressedError, "Expected an SuppressedError to have been thrown"); + assert.sameValue(e.error, error1, "Expected the outermost suppressing error to have been 'error1'"); + assert(e.suppressed instanceof SuppressedError, "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/language/statements/using/using-allows-null-initializer.js b/test/language/statements/using/using-allows-null-initializer.js new file mode 100644 index 00000000000..15593f537b0 --- /dev/null +++ b/test/language/statements/using/using-allows-null-initializer.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-let-and-const-declarations-runtime-semantics-evaluation +description: Allows null in initializer of 'using' +info: | + RS: Evaluation + UsingDeclaration : using BindingList ; + + 1. Perform ? BindingEvaluation of BindingList with argument sync-dispose. + 2. Return empty. + + RS: BindingEvaluation + LexicalBinding : BindingIdentifier Initializer + + ... + 5. Return ? InitializeReferencedBinding(lhs, value, hint). + + InitializeReferencedBinding ( V, W ) + + ... + 4. Return ? base.InitializeBinding(V.[[ReferencedName]], W). + + InitializeBinding ( N, V, hint ) + + ... + 2. If hint is not normal, perform ? AddDisposableResource(envRec.[[DisposeCapability]], V, hint). + ... + + 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] +---*/ + +{ + using x = null; +} diff --git a/test/language/statements/using/using-allows-undefined-initializer.js b/test/language/statements/using/using-allows-undefined-initializer.js new file mode 100644 index 00000000000..717591ffe46 --- /dev/null +++ b/test/language/statements/using/using-allows-undefined-initializer.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-let-and-const-declarations-runtime-semantics-evaluation +description: Allows undefined in initializer of 'using' +info: | + RS: Evaluation + UsingDeclaration : using BindingList ; + + 1. Perform ? BindingEvaluation of BindingList with argument sync-dispose. + 2. Return empty. + + RS: BindingEvaluation + LexicalBinding : BindingIdentifier Initializer + + ... + 5. Return ? InitializeReferencedBinding(lhs, value, hint). + + InitializeReferencedBinding ( V, W ) + + ... + 4. Return ? base.InitializeBinding(V.[[ReferencedName]], W). + + InitializeBinding ( N, V, hint ) + + ... + 2. If hint is not normal, perform ? AddDisposableResource(envRec.[[DisposeCapability]], V, hint). + ... + + 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] +---*/ + +{ + using x = undefined; +}