diff --git a/CHANGELOG.md b/CHANGELOG.md index 55785be8e8c7..20beb4961305 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -59,6 +59,30 @@ - `String.prototype.toWellFormed` - Moved to Stage 3, [November 2022 TC39 meeting](https://github.com/babel/proposals/issues/85#issuecomment-1332180862) - Added `/actual/` entries, disabled unconditional forced replacement +- [Explicit resource management](https://github.com/tc39/proposal-explicit-resource-management) Stage 3 and [Async explicit resource management](https://github.com/tc39/proposal-async-explicit-resource-management) Stage 2 proposals: + - Renamed from "`using` statement" and [splitted into 2 (sync and async) proposals](https://github.com/tc39/proposal-explicit-resource-management/pull/131) + - In addition to already present well-known symbols, added new built-ins: + - `Symbol.dispose` + - `Symbol.asyncDispose` + - `SuppressedError` + - `DisposableStack` + - `DisposableStack.prototype.dispose` + - `DisposableStack.prototype.use` + - `DisposableStack.prototype.adopt` + - `DisposableStack.prototype.defer` + - `DisposableStack.prototype.move` + - `DisposableStack.prototype[@@dispose]` + - `AsyncDisposableStack` + - `AsyncDisposableStack.prototype.disposeAsync` + - `AsyncDisposableStack.prototype.use` + - `AsyncDisposableStack.prototype.adopt` + - `AsyncDisposableStack.prototype.defer` + - `AsyncDisposableStack.prototype.move` + - `AsyncDisposableStack.prototype[@@asyncDispose]` + - `Iterator.prototype[@@dispose]` + - `AsyncIterator.prototype[@@asyncDispose]` + - Sync version of this proposal moved to Stage 3, [November 2022 TC39 meeting](https://github.com/babel/proposals/issues/85#issuecomment-1333747094) + - Added `/actual/` namespace entries for Stage 3 proposal - [Compat data targets](/packages/core-js-compat#targets-option) improvements: - [React Native from 0.70 shipped with Hermes as the default engine.](https://reactnative.dev/blog/2022/07/08/hermes-as-the-default) However, bundled Hermes versions differ from standalone Hermes releases. So added **`react-native`** target for React Native with bundled Hermes. - [According to the documentation](https://developer.oculus.com/documentation/web/browser-intro/), Oculus Browser was renamed to Meta Quest Browser, so `oculus` target was renamed to **`quest`**. diff --git a/README.md b/README.md index 4fcbea36081a..f0bba4b0952a 100644 --- a/README.md +++ b/README.md @@ -130,11 +130,12 @@ queueMicrotask(() => console.log('called as microtask')); - [`Array` grouping](#array-grouping) - [Change `Array` by copy](#change-array-by-copy) - [New `Set` methods](#new-set-methods) + - [Explicit resource management](#explicit-resource-management) - [Well-formed unicode strings](#well-formed-unicode-strings) - [Stage 2 proposals](#stage-2-proposals) - [`Map.prototype.emplace`](#mapprototypeemplace) - [`Array.isTemplateObject`](#arrayistemplateobject) - - [`Symbol.{ asyncDispose, dispose }` for `using` statement](#symbol-asyncdispose-dispose--for-using-statement) + - [Async explicit resource management](#async-explicit-resource-management) - [`Symbol.metadataKey` for decorators metadata proposal](#symbolmetadatakey-for-decorators-metadata-proposal) - [Stage 1 proposals](#stage-1-proposals) - [`Observable`](#observable) @@ -593,10 +594,11 @@ class [ constructor(message: string, { cause: any }): %Error%; } -class AggregateError { - constructor(errors: Iterable, message: string, { cause: any }): AggregateError; +class AggregateError extends Error { + constructor(errors: Iterable, message?: string, { cause: any }?): AggregateError; errors: Array; message: string; + cause: any; } class Error { @@ -2311,6 +2313,45 @@ new Set([1, 2, 3]).isDisjointFrom(new Set([4, 5, 6])); // => true new Set([1, 2, 3]).isSubsetOf(new Set([5, 4, 3, 2, 1])); // => true new Set([5, 4, 3, 2, 1]).isSupersetOf(new Set([1, 2, 3])); // => true ``` +##### [Explicit Resource Management](https://github.com/tc39/proposal-explicit-resource-management)[⬆](#index) +Note: **This is only built-ins for this proposal, `using` syntax support requires transpiler support.** + +Modules [`esnext.symbol.dispose`](https://github.com/zloirock/core-js/blob/master/packages/core-js/modules/esnext.symbol.dispose.js), [`esnext.disposable-stack.constructor`](https://github.com/zloirock/core-js/blob/master/packages/core-js/modules/esnext.disposable-stack.constructor.js), [`esnext.suppressed-error.constructor`](https://github.com/zloirock/core-js/blob/master/packages/core-js/modules/esnext.suppressed-error.constructor.js), [`esnext.iterator.dispose`](https://github.com/zloirock/core-js/blob/master/packages/core-js/modules/esnext.iterator.dispose.js). +```js +class Symbol { + static dispose: @@dispose; +} + +class DisposableStack { + constructor(): DisposableStack; + dispose(): undefined; + use(value: Disposable): value; + adopt(value: object, onDispose: Function): value; + defer(onDispose: Function): undefined; + @@dispose(): undefined; + @@toStringTag: 'DisposableStack'; +} + +class SuppressedError extends Error { + constructor(error: any, suppressed: any, message?: string, { cause: any }?): SuppressedError; + error: any; + suppressed: any; + message: string; + cause: any; +} + +class Iterator { + @@dispose(): undefined; +} +``` +[*CommonJS entry points:*](#commonjs-api) +```js +core-js/proposals/explicit-resource-management +core-js(-pure)/full/symbol/dispose +core-js(-pure)/full/disposable-stack +core-js(-pure)/full/suppressed-error +core-js(-pure)/full/iterator/dispose +``` ##### [Well-formed unicode strings](https://github.com/tc39/proposal-is-usv-string)[⬆](#index) Modules [`esnext.string.is-well-formed`](https://github.com/zloirock/core-js/blob/master/packages/core-js/modules/esnext.string.is-well-formed.js) and [`esnext.string.to-well-formed`](https://github.com/zloirock/core-js/blob/master/packages/core-js/modules/esnext.string.to-well-formed.js) ```js @@ -2382,19 +2423,35 @@ core-js(-pure)/full/array/is-template-object ```js console.log(Array.isTemplateObject((it => it)`qwe${ 123 }asd`)); // => true ``` -##### [`Symbol.{ asyncDispose, dispose }` for `using` statement](https://github.com/tc39/proposal-using-statement)[⬆](#index) -Modules [`esnext.symbol.dispose`](https://github.com/zloirock/core-js/blob/master/packages/core-js/modules/esnext.symbol.dispose.js) and [`esnext.symbol.async-dispose`](https://github.com/zloirock/core-js/blob/master/packages/core-js/modules/esnext.symbol.async-dispose.js). +##### [Async Explicit Resource Management](https://github.com/tc39/proposal-async-explicit-resource-management)[⬆](#index) +Note: **This is only built-ins for this proposal, `using` syntax support requires transpiler support.** + +Modules [`esnext.symbol.async-dispose`](https://github.com/zloirock/core-js/blob/master/packages/core-js/modules/esnext.symbol.async-dispose.js), [`esnext.async-disposable-stack.constructor`](https://github.com/zloirock/core-js/blob/master/packages/core-js/modules/esnext.async-disposable-stack.constructor.js), [`esnext.async-iterator.async-dispose`](https://github.com/zloirock/core-js/blob/master/packages/core-js/modules/esnext.async-iterator.async-dispose.js). ```js class Symbol { static asyncDispose: @@asyncDispose; - static dispose: @@dispose; +} + +class AsyncDisposableStack { + constructor(): AsyncDisposableStack; + disposeAsync(): Promise; + use(value: AsyncDisposable | Disposable): value; + adopt(value: object, onDispose: Function): value; + defer(onDispose: Function): undefined; + @@asyncDispose(): Promise; + @@toStringTag: 'AsyncDisposableStack'; +} + +class AsyncIterator { + @@asyncDispose(): Promise; } ``` [*CommonJS entry points:*](#commonjs-api) ```js -core-js/proposals/using-statement +core-js/proposals/async-explicit-resource-management core-js(-pure)/full/symbol/async-dispose -core-js(-pure)/full/symbol/dispose +core-js(-pure)/full/async-disposable-stack +core-js(-pure)/full/async-iterator/async-dispose ``` ##### [`Symbol.metadataKey` for decorators metadata proposal](https://github.com/tc39/proposal-decorator-metadata)[⬆](#index) Module [`esnext.symbol.metadata-key`](https://github.com/zloirock/core-js/blob/master/packages/core-js/modules/esnext.symbol.metadata-key.js). diff --git a/packages/core-js-compat/src/data.mjs b/packages/core-js-compat/src/data.mjs index 7fabd6ce139d..3b726a29d21c 100644 --- a/packages/core-js-compat/src/data.mjs +++ b/packages/core-js-compat/src/data.mjs @@ -1802,6 +1802,8 @@ export const data = { }, // TODO: Remove from `core-js@4` 'esnext.aggregate-error': null, + 'esnext.suppressed-error.constructor': { + }, 'esnext.array.from-async': { bun: '0.3.0', }, @@ -1858,11 +1860,15 @@ export const data = { deno: '1.27', safari: '16.0', }, + 'esnext.async-disposable-stack.constructor': { + }, 'esnext.async-iterator.constructor': { }, // TODO: Remove from `core-js@4` 'esnext.async-iterator.as-indexed-pairs': { }, + 'esnext.async-iterator.async-dispose': { + }, 'esnext.async-iterator.drop': { }, 'esnext.async-iterator.every': { @@ -1895,6 +1901,8 @@ export const data = { }, 'esnext.composite-symbol': { }, + 'esnext.disposable-stack.constructor': { + }, 'esnext.function.is-callable': { }, 'esnext.function.is-constructor': { @@ -1908,6 +1916,8 @@ export const data = { // TODO: Remove from `core-js@4` 'esnext.iterator.as-indexed-pairs': { }, + 'esnext.iterator.dispose': { + }, 'esnext.iterator.drop': { }, 'esnext.iterator.every': { diff --git a/packages/core-js-compat/src/modules-by-versions.mjs b/packages/core-js-compat/src/modules-by-versions.mjs index 4c9cda9c7043..5e52f0f38518 100644 --- a/packages/core-js-compat/src/modules-by-versions.mjs +++ b/packages/core-js-compat/src/modules-by-versions.mjs @@ -163,6 +163,11 @@ export default { 'web.self', ], 3.27: [ + 'esnext.suppressed-error.constructor', + 'esnext.async-disposable-stack.constructor', + 'esnext.async-iterator.async-dispose', + 'esnext.disposable-stack.constructor', + 'esnext.iterator.dispose', 'esnext.set.difference.v2', 'esnext.set.intersection.v2', 'esnext.set.is-disjoint-from.v2', diff --git a/packages/core-js/actual/disposable-stack/constructor.js b/packages/core-js/actual/disposable-stack/constructor.js new file mode 100644 index 000000000000..90c1292cd8d4 --- /dev/null +++ b/packages/core-js/actual/disposable-stack/constructor.js @@ -0,0 +1,7 @@ +require('../../modules/es.error.cause'); +require('../../modules/es.object.to-string'); +require('../../modules/esnext.suppressed-error.constructor'); +require('../../modules/esnext.disposable-stack.constructor'); +var path = require('../../internals/path'); + +module.exports = path.DisposableStack; diff --git a/packages/core-js/actual/disposable-stack/index.js b/packages/core-js/actual/disposable-stack/index.js new file mode 100644 index 000000000000..90c1292cd8d4 --- /dev/null +++ b/packages/core-js/actual/disposable-stack/index.js @@ -0,0 +1,7 @@ +require('../../modules/es.error.cause'); +require('../../modules/es.object.to-string'); +require('../../modules/esnext.suppressed-error.constructor'); +require('../../modules/esnext.disposable-stack.constructor'); +var path = require('../../internals/path'); + +module.exports = path.DisposableStack; diff --git a/packages/core-js/actual/iterator/dispose.js b/packages/core-js/actual/iterator/dispose.js new file mode 100644 index 000000000000..bf3b517d9677 --- /dev/null +++ b/packages/core-js/actual/iterator/dispose.js @@ -0,0 +1 @@ +require('../../modules/esnext.iterator.dispose'); diff --git a/packages/core-js/actual/iterator/index.js b/packages/core-js/actual/iterator/index.js index 0c3281124d17..fa70861c22cc 100644 --- a/packages/core-js/actual/iterator/index.js +++ b/packages/core-js/actual/iterator/index.js @@ -3,6 +3,7 @@ require('../../modules/es.object.to-string'); require('../../modules/es.promise'); require('../../modules/es.string.iterator'); require('../../modules/esnext.iterator.constructor'); +require('../../modules/esnext.iterator.dispose'); require('../../modules/esnext.iterator.drop'); require('../../modules/esnext.iterator.every'); require('../../modules/esnext.iterator.filter'); diff --git a/packages/core-js/actual/suppressed-error.js b/packages/core-js/actual/suppressed-error.js new file mode 100644 index 000000000000..0f41bdef653f --- /dev/null +++ b/packages/core-js/actual/suppressed-error.js @@ -0,0 +1,5 @@ +require('../modules/es.error.cause'); +require('../modules/esnext.suppressed-error.constructor'); +var path = require('../internals/path'); + +module.exports = path.SuppressedError; diff --git a/packages/core-js/actual/symbol/dispose.js b/packages/core-js/actual/symbol/dispose.js new file mode 100644 index 000000000000..71a4d5044347 --- /dev/null +++ b/packages/core-js/actual/symbol/dispose.js @@ -0,0 +1,4 @@ +require('../../modules/esnext.symbol.dispose'); +var WrappedWellKnownSymbolModule = require('../../internals/well-known-symbol-wrapped'); + +module.exports = WrappedWellKnownSymbolModule.f('dispose'); diff --git a/packages/core-js/actual/symbol/index.js b/packages/core-js/actual/symbol/index.js index 5eeb6fbc6a61..92842b61a8ef 100644 --- a/packages/core-js/actual/symbol/index.js +++ b/packages/core-js/actual/symbol/index.js @@ -1,3 +1,5 @@ var parent = require('../../stable/symbol'); +require('../../modules/esnext.symbol.dispose'); + module.exports = parent; diff --git a/packages/core-js/full/async-disposable-stack/constructor.js b/packages/core-js/full/async-disposable-stack/constructor.js new file mode 100644 index 000000000000..ae44f521b344 --- /dev/null +++ b/packages/core-js/full/async-disposable-stack/constructor.js @@ -0,0 +1,8 @@ +require('../../modules/es.error.cause'); +require('../../modules/es.object.to-string'); +require('../../modules/es.promise'); +require('../../modules/esnext.suppressed-error.constructor'); +require('../../modules/esnext.async-disposable-stack.constructor'); +var path = require('../../internals/path'); + +module.exports = path.AsyncDisposableStack; diff --git a/packages/core-js/full/async-disposable-stack/index.js b/packages/core-js/full/async-disposable-stack/index.js new file mode 100644 index 000000000000..ae44f521b344 --- /dev/null +++ b/packages/core-js/full/async-disposable-stack/index.js @@ -0,0 +1,8 @@ +require('../../modules/es.error.cause'); +require('../../modules/es.object.to-string'); +require('../../modules/es.promise'); +require('../../modules/esnext.suppressed-error.constructor'); +require('../../modules/esnext.async-disposable-stack.constructor'); +var path = require('../../internals/path'); + +module.exports = path.AsyncDisposableStack; diff --git a/packages/core-js/full/async-iterator/async-dispose.js b/packages/core-js/full/async-iterator/async-dispose.js new file mode 100644 index 000000000000..da1a182c9ec5 --- /dev/null +++ b/packages/core-js/full/async-iterator/async-dispose.js @@ -0,0 +1,3 @@ +require('../../modules/es.object.to-string'); +require('../../modules/es.promise'); +require('../../modules/esnext.async-iterator.async-dispose'); diff --git a/packages/core-js/full/async-iterator/index.js b/packages/core-js/full/async-iterator/index.js index b56043f4d688..e213c98a98df 100644 --- a/packages/core-js/full/async-iterator/index.js +++ b/packages/core-js/full/async-iterator/index.js @@ -1,4 +1,5 @@ var parent = require('../../actual/async-iterator'); +require('../../modules/esnext.async-iterator.async-dispose'); // TODO: Remove from `core-js@4` require('../../modules/esnext.async-iterator.as-indexed-pairs'); require('../../modules/esnext.async-iterator.indexed'); diff --git a/packages/core-js/full/async-iterator/indexed.js b/packages/core-js/full/async-iterator/indexed.js index 2686c40d504a..1fec52caba8a 100644 --- a/packages/core-js/full/async-iterator/indexed.js +++ b/packages/core-js/full/async-iterator/indexed.js @@ -1,3 +1,4 @@ +// TODO: Remove from `core-js@4` require('../../modules/es.object.to-string'); require('../../modules/es.promise'); require('../../modules/esnext.async-iterator.constructor'); diff --git a/packages/core-js/full/disposable-stack/constructor.js b/packages/core-js/full/disposable-stack/constructor.js new file mode 100644 index 000000000000..7ec52460bbb2 --- /dev/null +++ b/packages/core-js/full/disposable-stack/constructor.js @@ -0,0 +1,3 @@ +var parent = require('../../actual/disposable-stack/constructor'); + +module.exports = parent; diff --git a/packages/core-js/full/disposable-stack/index.js b/packages/core-js/full/disposable-stack/index.js new file mode 100644 index 000000000000..7ada28b9e474 --- /dev/null +++ b/packages/core-js/full/disposable-stack/index.js @@ -0,0 +1,3 @@ +var parent = require('../../actual/disposable-stack'); + +module.exports = parent; diff --git a/packages/core-js/full/iterator/dispose.js b/packages/core-js/full/iterator/dispose.js new file mode 100644 index 000000000000..46438105e607 --- /dev/null +++ b/packages/core-js/full/iterator/dispose.js @@ -0,0 +1,3 @@ +var parent = require('../../actual/iterator/dispose'); + +module.exports = parent; diff --git a/packages/core-js/full/iterator/indexed.js b/packages/core-js/full/iterator/indexed.js index 133712fff554..4d7df998ca10 100644 --- a/packages/core-js/full/iterator/indexed.js +++ b/packages/core-js/full/iterator/indexed.js @@ -1,3 +1,4 @@ +// TODO: Remove from `core-js@4` require('../../modules/es.object.to-string'); require('../../modules/esnext.iterator.constructor'); require('../../modules/esnext.iterator.indexed'); diff --git a/packages/core-js/full/suppressed-error.js b/packages/core-js/full/suppressed-error.js new file mode 100644 index 000000000000..da67d8242fec --- /dev/null +++ b/packages/core-js/full/suppressed-error.js @@ -0,0 +1,3 @@ +var parent = require('../actual/suppressed-error'); + +module.exports = parent; diff --git a/packages/core-js/full/symbol/dispose.js b/packages/core-js/full/symbol/dispose.js index 71a4d5044347..188e06930300 100644 --- a/packages/core-js/full/symbol/dispose.js +++ b/packages/core-js/full/symbol/dispose.js @@ -1,4 +1,3 @@ -require('../../modules/esnext.symbol.dispose'); -var WrappedWellKnownSymbolModule = require('../../internals/well-known-symbol-wrapped'); +var parent = require('../../actual/symbol/dispose'); -module.exports = WrappedWellKnownSymbolModule.f('dispose'); +module.exports = parent; diff --git a/packages/core-js/full/symbol/index.js b/packages/core-js/full/symbol/index.js index 5dbbfb6a480a..19cf18008474 100644 --- a/packages/core-js/full/symbol/index.js +++ b/packages/core-js/full/symbol/index.js @@ -1,6 +1,5 @@ var parent = require('../../actual/symbol'); require('../../modules/esnext.symbol.async-dispose'); -require('../../modules/esnext.symbol.dispose'); require('../../modules/esnext.symbol.matcher'); require('../../modules/esnext.symbol.metadata-key'); require('../../modules/esnext.symbol.observable'); diff --git a/packages/core-js/internals/disposable-stack-helpers.js b/packages/core-js/internals/disposable-stack-helpers.js new file mode 100644 index 000000000000..3f91c8f0753b --- /dev/null +++ b/packages/core-js/internals/disposable-stack-helpers.js @@ -0,0 +1,40 @@ +var uncurryThis = require('../internals/function-uncurry-this'); +var bind = require('../internals/function-bind-context'); +var anObject = require('../internals/an-object'); +var isNullOrUndefined = require('../internals/is-null-or-undefined'); +var getMethod = require('../internals/get-method'); +var wellKnownSymbol = require('../internals/well-known-symbol'); + +var ASYNC_DISPOSE = wellKnownSymbol('asyncDispose'); +var DISPOSE = wellKnownSymbol('dispose'); + +var push = uncurryThis([].push); + +var getDisposeMethod = function (V, hint) { + if (hint == 'async-dispose') { + return getMethod(V, ASYNC_DISPOSE) || getMethod(V, DISPOSE); + } return getMethod(V, DISPOSE); +}; + +var createDisposableResource = function (V, hint, method) { + return bind(method || getDisposeMethod(V, hint), V); +}; + +var addDisposableResource = function (disposable, V, hint, method) { + var resource; + if (!method) { + if (isNullOrUndefined(V)) return; + resource = createDisposableResource(V, hint); + } else if (isNullOrUndefined(V)) { + resource = createDisposableResource(undefined, hint, method); + } else { + resource = createDisposableResource(anObject(V), hint, method); + } + + push(disposable.stack, resource); +}; + +module.exports = { + getDisposeMethod: getDisposeMethod, + addDisposableResource: addDisposableResource +}; diff --git a/packages/core-js/modules/esnext.async-disposable-stack.constructor.js b/packages/core-js/modules/esnext.async-disposable-stack.constructor.js new file mode 100644 index 000000000000..5d985fbe74e4 --- /dev/null +++ b/packages/core-js/modules/esnext.async-disposable-stack.constructor.js @@ -0,0 +1,136 @@ +'use strict'; +// https://github.com/tc39/proposal-async-explicit-resource-management +var $ = require('../internals/export'); +var DESCRIPTORS = require('../internals/descriptors'); +var getBuiltIn = require('../internals/get-built-in'); +var aCallable = require('../internals/a-callable'); +var anObject = require('../internals/an-object'); +var anInstance = require('../internals/an-instance'); +var isNullOrUndefined = require('../internals/is-null-or-undefined'); +var defineBuiltIn = require('../internals/define-built-in'); +var defineBuiltIns = require('../internals/define-built-ins'); +var defineBuiltInAccessor = require('../internals/define-built-in-accessor'); +var wellKnownSymbol = require('../internals/well-known-symbol'); +var InternalStateModule = require('../internals/internal-state'); +var DisposableStackHelpers = require('../internals/disposable-stack-helpers'); + +var Promise = getBuiltIn('Promise'); +var SuppressedError = getBuiltIn('SuppressedError'); +var $ReferenceError = ReferenceError; + +var ASYNC_DISPOSE = wellKnownSymbol('asyncDispose'); +var TO_STRING_TAG = wellKnownSymbol('toStringTag'); + +var getDisposeMethod = DisposableStackHelpers.getDisposeMethod; +var addDisposableResource = DisposableStackHelpers.addDisposableResource; + +var ASYNC_DISPOSABLE_STACK = 'AsyncDisposableStack'; +var setInternalState = InternalStateModule.set; +var getAsyncDisposableStackInternalState = InternalStateModule.getterFor(ASYNC_DISPOSABLE_STACK); + +var HINT = 'async-dispose'; +var DISPOSED = 'disposed'; +var PENDING = 'pending'; + +var ALREADY_DISPOSED = ASYNC_DISPOSABLE_STACK + ' already disposed'; + +var $AsyncDisposableStack = function AsyncDisposableStack() { + setInternalState(anInstance(this, AsyncDisposableStackPrototype), { + type: ASYNC_DISPOSABLE_STACK, + state: PENDING, + stack: [] + }); + + if (!DESCRIPTORS) this.disposed = false; +}; + +var AsyncDisposableStackPrototype = $AsyncDisposableStack.prototype; + +defineBuiltIns(AsyncDisposableStackPrototype, { + disposeAsync: function disposeAsync() { + var asyncDisposableStack = this; + return new Promise(function (resolve, reject) { + var internalState = getAsyncDisposableStackInternalState(asyncDisposableStack); + if (internalState.state == DISPOSED) return resolve(undefined); + internalState.state = DISPOSED; + if (!DESCRIPTORS) asyncDisposableStack.disposed = true; + var stack = internalState.stack; + var i = stack.length; + var thrown = false; + var suppressed; + + var handleError = function (result) { + if (thrown) { + suppressed = new SuppressedError(result, suppressed); + } else { + thrown = true; + suppressed = result; + } + + loop(); + }; + + var loop = function () { + if (i) { + var disposeMethod = stack[--i]; + try { + Promise.resolve(disposeMethod()).then(loop, handleError); + } catch (error) { + handleError(error); + } + } else { + internalState.stack = null; + thrown ? reject(suppressed) : resolve(undefined); + } + }; + + loop(); + }); + }, + use: function use(value) { + var internalState = getAsyncDisposableStackInternalState(this); + if (internalState.state == DISPOSED) throw $ReferenceError(ALREADY_DISPOSED); + if (!isNullOrUndefined(value)) { + anObject(value); + var method = aCallable(getDisposeMethod(value, HINT)); + addDisposableResource(internalState, value, HINT, method); + } return value; + }, + adopt: function adopt(value, onDispose) { + var internalState = getAsyncDisposableStackInternalState(this); + if (internalState.state == DISPOSED) throw $ReferenceError(ALREADY_DISPOSED); + aCallable(onDispose); + addDisposableResource(internalState, undefined, HINT, function () { + onDispose(value); + }); + return value; + }, + defer: function defer(onDispose) { + var internalState = getAsyncDisposableStackInternalState(this); + if (internalState.state == DISPOSED) throw $ReferenceError(ALREADY_DISPOSED); + aCallable(onDispose); + addDisposableResource(internalState, undefined, HINT, onDispose); + }, + move: function move() { + var internalState = getAsyncDisposableStackInternalState(this); + if (internalState.state == DISPOSED) throw $ReferenceError(ALREADY_DISPOSED); + var newAsyncDisposableStack = new $AsyncDisposableStack(); + getAsyncDisposableStackInternalState(newAsyncDisposableStack).stack = internalState.stack; + internalState.stack = []; + return newAsyncDisposableStack; + } +}); + +if (DESCRIPTORS) defineBuiltInAccessor(AsyncDisposableStackPrototype, 'disposed', { + configurable: true, + get: function disposed() { + return getAsyncDisposableStackInternalState(this).state == DISPOSED; + } +}); + +defineBuiltIn(AsyncDisposableStackPrototype, ASYNC_DISPOSE, AsyncDisposableStackPrototype.disposeAsync, { name: 'disposeAsync' }); +defineBuiltIn(AsyncDisposableStackPrototype, TO_STRING_TAG, ASYNC_DISPOSABLE_STACK, { nonWritable: true }); + +$({ global: true, constructor: true, forced: true }, { + AsyncDisposableStack: $AsyncDisposableStack +}); diff --git a/packages/core-js/modules/esnext.async-iterator.async-dispose.js b/packages/core-js/modules/esnext.async-iterator.async-dispose.js new file mode 100644 index 000000000000..d6c1cd1cb02e --- /dev/null +++ b/packages/core-js/modules/esnext.async-iterator.async-dispose.js @@ -0,0 +1,26 @@ +'use strict'; +// https://github.com/tc39/proposal-async-explicit-resource-management +var call = require('../internals/function-call'); +var defineBuiltIn = require('../internals/define-built-in'); +var getBuiltIn = require('../internals/get-built-in'); +var getMethod = require('../internals/get-method'); +var hasOwn = require('../internals/has-own-property'); +var wellKnownSymbol = require('../internals/well-known-symbol'); +var AsyncIteratorPrototype = require('../internals/async-iterator-prototype'); + +var ASYNC_DISPOSE = wellKnownSymbol('asyncDispose'); +var Promise = getBuiltIn('Promise'); + +if (!hasOwn(AsyncIteratorPrototype, ASYNC_DISPOSE)) { + defineBuiltIn(AsyncIteratorPrototype, ASYNC_DISPOSE, function () { + var O = this; + return new Promise(function (resolve, reject) { + var $return = getMethod(O, 'return'); + if ($return) { + Promise.resolve(call($return, O)).then(function () { + resolve(undefined); + }, reject); + } else resolve(undefined); + }); + }); +} diff --git a/packages/core-js/modules/esnext.disposable-stack.constructor.js b/packages/core-js/modules/esnext.disposable-stack.constructor.js new file mode 100644 index 000000000000..0c6eb7dda4b1 --- /dev/null +++ b/packages/core-js/modules/esnext.disposable-stack.constructor.js @@ -0,0 +1,120 @@ +'use strict'; +// https://github.com/tc39/proposal-explicit-resource-management +var $ = require('../internals/export'); +var DESCRIPTORS = require('../internals/descriptors'); +var getBuiltIn = require('../internals/get-built-in'); +var aCallable = require('../internals/a-callable'); +var anObject = require('../internals/an-object'); +var anInstance = require('../internals/an-instance'); +var isNullOrUndefined = require('../internals/is-null-or-undefined'); +var defineBuiltIn = require('../internals/define-built-in'); +var defineBuiltIns = require('../internals/define-built-ins'); +var defineBuiltInAccessor = require('../internals/define-built-in-accessor'); +var wellKnownSymbol = require('../internals/well-known-symbol'); +var InternalStateModule = require('../internals/internal-state'); +var DisposableStackHelpers = require('../internals/disposable-stack-helpers'); + +var SuppressedError = getBuiltIn('SuppressedError'); +var $ReferenceError = ReferenceError; + +var DISPOSE = wellKnownSymbol('dispose'); +var TO_STRING_TAG = wellKnownSymbol('toStringTag'); + +var getDisposeMethod = DisposableStackHelpers.getDisposeMethod; +var addDisposableResource = DisposableStackHelpers.addDisposableResource; + +var DISPOSABLE_STACK = 'DisposableStack'; +var setInternalState = InternalStateModule.set; +var getDisposableStackInternalState = InternalStateModule.getterFor(DISPOSABLE_STACK); + +var HINT = 'sync-dispose'; +var DISPOSED = 'disposed'; +var PENDING = 'pending'; + +var ALREADY_DISPOSED = DISPOSABLE_STACK + ' already disposed'; + +var $DisposableStack = function DisposableStack() { + setInternalState(anInstance(this, DisposableStackPrototype), { + type: DISPOSABLE_STACK, + state: PENDING, + stack: [] + }); + + if (!DESCRIPTORS) this.disposed = false; +}; + +var DisposableStackPrototype = $DisposableStack.prototype; + +defineBuiltIns(DisposableStackPrototype, { + dispose: function dispose() { + var internalState = getDisposableStackInternalState(this); + if (internalState.state == DISPOSED) return; + internalState.state = DISPOSED; + if (!DESCRIPTORS) this.disposed = true; + var stack = internalState.stack; + var i = stack.length; + var thrown = false; + var suppressed; + while (i) { + var disposeMethod = stack[--i]; + try { + disposeMethod(); + } catch (errorResult) { + if (thrown) { + suppressed = new SuppressedError(errorResult, suppressed); + } else { + thrown = true; + suppressed = errorResult; + } + } + } + internalState.stack = null; + if (thrown) throw suppressed; + }, + use: function use(value) { + var internalState = getDisposableStackInternalState(this); + if (internalState.state == DISPOSED) throw $ReferenceError(ALREADY_DISPOSED); + if (!isNullOrUndefined(value)) { + anObject(value); + var method = aCallable(getDisposeMethod(value, HINT)); + addDisposableResource(internalState, value, HINT, method); + } return value; + }, + adopt: function adopt(value, onDispose) { + var internalState = getDisposableStackInternalState(this); + if (internalState.state == DISPOSED) throw $ReferenceError(ALREADY_DISPOSED); + aCallable(onDispose); + addDisposableResource(internalState, undefined, HINT, function () { + onDispose(value); + }); + return value; + }, + defer: function defer(onDispose) { + var internalState = getDisposableStackInternalState(this); + if (internalState.state == DISPOSED) throw $ReferenceError(ALREADY_DISPOSED); + aCallable(onDispose); + addDisposableResource(internalState, undefined, HINT, onDispose); + }, + move: function move() { + var internalState = getDisposableStackInternalState(this); + if (internalState.state == DISPOSED) throw $ReferenceError(ALREADY_DISPOSED); + var newDisposableStack = new $DisposableStack(); + getDisposableStackInternalState(newDisposableStack).stack = internalState.stack; + internalState.stack = []; + return newDisposableStack; + } +}); + +if (DESCRIPTORS) defineBuiltInAccessor(DisposableStackPrototype, 'disposed', { + configurable: true, + get: function disposed() { + return getDisposableStackInternalState(this).state == DISPOSED; + } +}); + +defineBuiltIn(DisposableStackPrototype, DISPOSE, DisposableStackPrototype.dispose, { name: 'dispose' }); +defineBuiltIn(DisposableStackPrototype, TO_STRING_TAG, DISPOSABLE_STACK, { nonWritable: true }); + +$({ global: true, constructor: true }, { + DisposableStack: $DisposableStack +}); diff --git a/packages/core-js/modules/esnext.iterator.dispose.js b/packages/core-js/modules/esnext.iterator.dispose.js new file mode 100644 index 000000000000..ac463eebc9d0 --- /dev/null +++ b/packages/core-js/modules/esnext.iterator.dispose.js @@ -0,0 +1,17 @@ +'use strict'; +// https://github.com/tc39/proposal-explicit-resource-management +var call = require('../internals/function-call'); +var defineBuiltIn = require('../internals/define-built-in'); +var getMethod = require('../internals/get-method'); +var hasOwn = require('../internals/has-own-property'); +var wellKnownSymbol = require('../internals/well-known-symbol'); +var IteratorPrototype = require('../internals/iterators-core').IteratorPrototype; + +var DISPOSE = wellKnownSymbol('dispose'); + +if (!hasOwn(IteratorPrototype, DISPOSE)) { + defineBuiltIn(IteratorPrototype, DISPOSE, function () { + var $return = getMethod(this, 'return'); + if ($return) call($return, this); + }); +} diff --git a/packages/core-js/modules/esnext.suppressed-error.constructor.js b/packages/core-js/modules/esnext.suppressed-error.constructor.js new file mode 100644 index 000000000000..809e070bc9c1 --- /dev/null +++ b/packages/core-js/modules/esnext.suppressed-error.constructor.js @@ -0,0 +1,50 @@ +'use strict'; +var $ = require('../internals/export'); +var isPrototypeOf = require('../internals/object-is-prototype-of'); +var getPrototypeOf = require('../internals/object-get-prototype-of'); +var setPrototypeOf = require('../internals/object-set-prototype-of'); +var copyConstructorProperties = require('../internals/copy-constructor-properties'); +var create = require('../internals/object-create'); +var createNonEnumerableProperty = require('../internals/create-non-enumerable-property'); +var createPropertyDescriptor = require('../internals/create-property-descriptor'); +var clearErrorStack = require('../internals/error-stack-clear'); +var installErrorCause = require('../internals/install-error-cause'); +var normalizeStringArgument = require('../internals/normalize-string-argument'); +var wellKnownSymbol = require('../internals/well-known-symbol'); +var ERROR_STACK_INSTALLABLE = require('../internals/error-stack-installable'); + +var TO_STRING_TAG = wellKnownSymbol('toStringTag'); +var $Error = Error; + +var $SuppressedError = function SuppressedError(error, suppressed, message /* , options */) { + var options = arguments.length > 3 ? arguments[3] : undefined; + var isInstance = isPrototypeOf(SuppressedErrorPrototype, this); + var that; + if (setPrototypeOf) { + that = setPrototypeOf($Error(), isInstance ? getPrototypeOf(this) : SuppressedErrorPrototype); + } else { + that = isInstance ? this : create(SuppressedErrorPrototype); + createNonEnumerableProperty(that, TO_STRING_TAG, 'Error'); + } + if (message !== undefined) createNonEnumerableProperty(that, 'message', normalizeStringArgument(message)); + if (ERROR_STACK_INSTALLABLE) createNonEnumerableProperty(that, 'stack', clearErrorStack(that.stack, 1)); + installErrorCause(that, options); + createNonEnumerableProperty(that, 'error', error); + createNonEnumerableProperty(that, 'suppressed', suppressed); + return that; +}; + +if (setPrototypeOf) setPrototypeOf($SuppressedError, $Error); +else copyConstructorProperties($SuppressedError, $Error, { name: true }); + +var SuppressedErrorPrototype = $SuppressedError.prototype = create($Error.prototype, { + constructor: createPropertyDescriptor(1, $SuppressedError), + message: createPropertyDescriptor(1, ''), + name: createPropertyDescriptor(1, 'SuppressedError') +}); + +// `SuppressedError` constructor +// https://github.com/tc39/proposal-explicit-resource-management +$({ global: true, constructor: true, arity: 3 }, { + SuppressedError: $SuppressedError +}); diff --git a/packages/core-js/modules/esnext.symbol.async-dispose.js b/packages/core-js/modules/esnext.symbol.async-dispose.js index c38839d284c8..d108d76319f4 100644 --- a/packages/core-js/modules/esnext.symbol.async-dispose.js +++ b/packages/core-js/modules/esnext.symbol.async-dispose.js @@ -1,5 +1,5 @@ var defineWellKnownSymbol = require('../internals/well-known-symbol-define'); // `Symbol.asyncDispose` well-known symbol -// https://github.com/tc39/proposal-using-statement +// https://github.com/tc39/proposal-async-explicit-resource-management defineWellKnownSymbol('asyncDispose'); diff --git a/packages/core-js/modules/esnext.symbol.dispose.js b/packages/core-js/modules/esnext.symbol.dispose.js index 8ea9d8fb8585..13ddbd1ea380 100644 --- a/packages/core-js/modules/esnext.symbol.dispose.js +++ b/packages/core-js/modules/esnext.symbol.dispose.js @@ -1,5 +1,5 @@ var defineWellKnownSymbol = require('../internals/well-known-symbol-define'); // `Symbol.dispose` well-known symbol -// https://github.com/tc39/proposal-using-statement +// https://github.com/tc39/proposal-explicit-resource-management defineWellKnownSymbol('dispose'); diff --git a/packages/core-js/proposals/async-explicit-resource-management.js b/packages/core-js/proposals/async-explicit-resource-management.js new file mode 100644 index 000000000000..3dd08cc7e7ff --- /dev/null +++ b/packages/core-js/proposals/async-explicit-resource-management.js @@ -0,0 +1,5 @@ +// https://github.com/tc39/proposal-async-explicit-resource-management +require('../modules/esnext.suppressed-error.constructor'); +require('../modules/esnext.async-disposable-stack.constructor'); +require('../modules/esnext.async-iterator.async-dispose'); +require('../modules/esnext.symbol.async-dispose'); diff --git a/packages/core-js/proposals/explicit-resource-management.js b/packages/core-js/proposals/explicit-resource-management.js new file mode 100644 index 000000000000..ade8ecdd91a6 --- /dev/null +++ b/packages/core-js/proposals/explicit-resource-management.js @@ -0,0 +1,5 @@ +// https://github.com/tc39/proposal-explicit-resource-management +require('../modules/esnext.suppressed-error.constructor'); +require('../modules/esnext.disposable-stack.constructor'); +require('../modules/esnext.iterator.dispose'); +require('../modules/esnext.symbol.dispose'); diff --git a/packages/core-js/proposals/using-statement.js b/packages/core-js/proposals/using-statement.js index 2ac3df7fc201..b236f1768213 100644 --- a/packages/core-js/proposals/using-statement.js +++ b/packages/core-js/proposals/using-statement.js @@ -1,3 +1,4 @@ -// https://github.com/tc39/proposal-using-statement +// TODO: Renamed, remove from `core-js@4` +// https://github.com/tc39/proposal-explicit-resource-management require('../modules/esnext.symbol.async-dispose'); require('../modules/esnext.symbol.dispose'); diff --git a/packages/core-js/stage/2.js b/packages/core-js/stage/2.js index 887a5bfdc63c..2919b2f14887 100644 --- a/packages/core-js/stage/2.js +++ b/packages/core-js/stage/2.js @@ -1,14 +1,14 @@ var parent = require('./3'); require('../proposals/array-is-template-object'); +require('../proposals/async-explicit-resource-management'); require('../proposals/decorator-metadata'); -// TODO: Obsolete versions, remove from `core-js@4` -require('../proposals/iterator-helpers'); require('../proposals/map-upsert-stage-2'); -require('../proposals/set-methods'); -require('../proposals/using-statement'); // TODO: Obsolete versions, remove from `core-js@4` require('../proposals/array-grouping'); require('../proposals/decorators'); +require('../proposals/iterator-helpers'); +require('../proposals/set-methods'); +require('../proposals/using-statement'); module.exports = parent; diff --git a/packages/core-js/stage/3.js b/packages/core-js/stage/3.js index be4945b4e737..bd00ae57c478 100644 --- a/packages/core-js/stage/3.js +++ b/packages/core-js/stage/3.js @@ -3,6 +3,7 @@ var parent = require('./4'); require('../proposals/array-from-async-stage-2'); require('../proposals/array-grouping-stage-3-2'); require('../proposals/change-array-by-copy'); +require('../proposals/explicit-resource-management'); require('../proposals/iterator-helpers-stage-3'); require('../proposals/set-methods-v2'); require('../proposals/well-formed-unicode-strings'); diff --git a/tests/compat/tests.js b/tests/compat/tests.js index 8139caf39e31..be729084442a 100644 --- a/tests/compat/tests.js +++ b/tests/compat/tests.js @@ -1392,6 +1392,9 @@ GLOBAL.tests = { && set.add({}) == set && set[Symbol.toStringTag]; }], + 'esnext.suppressed-error.constructor': function () { + return typeof SuppressedError == 'function'; + }, 'esnext.array.from-async': function () { return Array.fromAsync; }, @@ -1432,9 +1435,15 @@ GLOBAL.tests = { 'esnext.array.with': function () { return []['with']; }, + 'esnext.async-disposable-stack.constructor': function () { + return typeof AsyncDisposableStack == 'function'; + }, 'esnext.async-iterator.constructor': function () { return typeof AsyncIterator == 'function'; }, + 'esnext.async-iterator.async-dispose': function () { + return AsyncIterator.prototype[Symbol.asyncDispose]; + }, 'esnext.async-iterator.drop': function () { return AsyncIterator.prototype.drop; }, @@ -1483,6 +1492,9 @@ GLOBAL.tests = { 'esnext.composite-symbol': function () { return compositeSymbol; }, + 'esnext.disposable-stack.constructor': function () { + return typeof DisposableStack == 'function'; + }, 'esnext.function.is-callable': function () { return Function.isCallable; }, @@ -1500,6 +1512,9 @@ GLOBAL.tests = { && Iterator.prototype === Object.getPrototypeOf(Object.getPrototypeOf([].values())); } }, + 'esnext.iterator.dispose': function () { + return [].keys()[Symbol.dispose]; + }, 'esnext.iterator.drop': function () { return Iterator.prototype.drop; }, diff --git a/tests/entries/unit.mjs b/tests/entries/unit.mjs index 663fb6e7a0bb..019bee4719aa 100644 --- a/tests/entries/unit.mjs +++ b/tests/entries/unit.mjs @@ -652,6 +652,11 @@ for (PATH of ['core-js-pure', 'core-js']) { ok(load(NS, 'string/virtual/is-well-formed').call('a')); ok(load(NS, 'string/to-well-formed')('a') === 'a'); ok(load(NS, 'string/virtual/to-well-formed').call('a') === 'a'); + ok(load(NS, 'symbol/dispose')); + ok(new (load(NS, 'suppressed-error'))(1, 2).suppressed === 2); + ok(typeof load(NS, 'disposable-stack') == 'function'); + ok(typeof load(NS, 'disposable-stack/constructor') == 'function'); + load(NS, 'iterator/dispose'); const instanceGroup = load(NS, 'instance/group'); ok(typeof instanceGroup == 'function'); @@ -803,8 +808,6 @@ for (PATH of ['core-js-pure', 'core-js']) { ok(load(NS, 'string/cooked')`a${ 1 }b` === 'a1b'); ok('next' in load(NS, 'string/code-points')('a')); ok('next' in load(NS, 'string/virtual/code-points').call('a')); - ok(load(NS, 'symbol/async-dispose')); - ok(load(NS, 'symbol/dispose')); ok(load(NS, 'symbol/matcher')); ok(load(NS, 'symbol/metadata')); ok(load(NS, 'symbol/metadata-key')); @@ -820,6 +823,10 @@ for (PATH of ['core-js-pure', 'core-js']) { ok(load(NS, 'weak-set/delete-all')(new WeakSet(), [], {}) === false); ok(load(NS, 'weak-set/from')([{}, []]) instanceof WeakSet); ok(load(NS, 'weak-set/of')({}, []) instanceof WeakSet); + ok(load(NS, 'symbol/async-dispose')); + load(NS, 'async-iterator/async-dispose'); + ok(typeof load(NS, 'async-disposable-stack') == 'function'); + ok(typeof load(NS, 'async-disposable-stack/constructor') == 'function'); const instanceCodePoints = load(NS, 'instance/code-points'); ok(typeof instanceCodePoints == 'function'); @@ -866,6 +873,7 @@ for (PATH of ['core-js-pure', 'core-js']) { load('proposals/array-is-template-object'); load('proposals/array-last'); load('proposals/array-unique'); + load('proposals/async-explicit-resource-management'); load('proposals/async-iteration'); load('proposals/change-array-by-copy'); load('proposals/collection-methods'); @@ -874,6 +882,7 @@ for (PATH of ['core-js-pure', 'core-js']) { load('proposals/decorators'); load('proposals/efficient-64-bit-arithmetic'); load('proposals/error-cause'); + load('proposals/explicit-resource-management'); load('proposals/function-is-callable-is-constructor'); load('proposals/function-un-this'); load('proposals/global-this'); diff --git a/tests/eslint/eslint.config.js b/tests/eslint/eslint.config.js index d1db2f2d4c28..ebe7b54a4c19 100644 --- a/tests/eslint/eslint.config.js +++ b/tests/eslint/eslint.config.js @@ -1136,9 +1136,12 @@ const json = { }; const globalsESNext = { + AsyncDisposableStack: READONLY, AsyncIterator: READONLY, + DisposableStack: READONLY, Iterator: READONLY, Observable: READONLY, + SuppressedError: READONLY, compositeKey: READONLY, compositeSymbol: READONLY, }; diff --git a/tests/unit-global/esnext.async-disposable-stack.constructor.js b/tests/unit-global/esnext.async-disposable-stack.constructor.js new file mode 100644 index 000000000000..d6f0aaa99af3 --- /dev/null +++ b/tests/unit-global/esnext.async-disposable-stack.constructor.js @@ -0,0 +1,192 @@ +import { STRICT } from '../helpers/constants'; + +QUnit.test('AsyncDisposableStack constructor', assert => { + assert.isFunction(AsyncDisposableStack); + assert.arity(AsyncDisposableStack, 0); + assert.name(AsyncDisposableStack, 'AsyncDisposableStack'); + assert.looksNative(AsyncDisposableStack); + + assert.throws(() => AsyncDisposableStack(), 'throws w/o `new`'); + assert.true(new AsyncDisposableStack() instanceof AsyncDisposableStack); + + assert.same(AsyncDisposableStack.prototype.constructor, AsyncDisposableStack); +}); + +QUnit.test('AsyncDisposableStack#disposeAsync', assert => { + assert.isFunction(AsyncDisposableStack.prototype.disposeAsync); + assert.arity(AsyncDisposableStack.prototype.disposeAsync, 0); + assert.name(AsyncDisposableStack.prototype.disposeAsync, 'disposeAsync'); + assert.looksNative(AsyncDisposableStack.prototype.disposeAsync); + assert.nonEnumerable(AsyncDisposableStack.prototype, 'disposeAsync'); +}); + +QUnit.test('AsyncDisposableStack#use', assert => { + assert.isFunction(AsyncDisposableStack.prototype.use); + assert.arity(AsyncDisposableStack.prototype.use, 1); + assert.name(AsyncDisposableStack.prototype.use, 'use'); + assert.looksNative(AsyncDisposableStack.prototype.use); + assert.nonEnumerable(AsyncDisposableStack.prototype, 'use'); + + let result = ''; + const stack = new AsyncDisposableStack(); + const resource = { + [Symbol.asyncDispose]() { + result += '1'; + assert.same(this, resource); + assert.same(arguments.length, 0); + }, + }; + + assert.same(stack.use(resource), resource); + + return stack.disposeAsync().then(it => { + assert.same(it, undefined); + assert.same(result, '1'); + }); +}); + +QUnit.test('AsyncDisposableStack#adopt', assert => { + assert.isFunction(AsyncDisposableStack.prototype.adopt); + assert.arity(AsyncDisposableStack.prototype.adopt, 2); + assert.name(AsyncDisposableStack.prototype.adopt, 'adopt'); + assert.looksNative(AsyncDisposableStack.prototype.adopt); + assert.nonEnumerable(AsyncDisposableStack.prototype, 'adopt'); + + let result = ''; + const stack = new AsyncDisposableStack(); + const resource = {}; + + assert.same(stack.adopt(resource, function (arg) { + result += '1'; + if (STRICT) assert.same(this, undefined); + assert.same(arguments.length, 1); + assert.same(arg, resource); + }), resource); + + return stack.disposeAsync().then(it => { + assert.same(it, undefined); + assert.same(result, '1'); + }); +}); + +QUnit.test('AsyncDisposableStack#defer', assert => { + assert.isFunction(AsyncDisposableStack.prototype.defer); + assert.arity(AsyncDisposableStack.prototype.defer, 1); + assert.name(AsyncDisposableStack.prototype.defer, 'defer'); + assert.looksNative(AsyncDisposableStack.prototype.defer); + assert.nonEnumerable(AsyncDisposableStack.prototype, 'defer'); + + let result = ''; + const stack = new AsyncDisposableStack(); + + assert.same(stack.defer(function () { + result += '1'; + if (STRICT) assert.same(this, undefined); + assert.same(arguments.length, 0); + }), undefined); + + return stack.disposeAsync().then(it => { + assert.same(it, undefined); + assert.same(result, '1'); + }); +}); + +QUnit.test('AsyncDisposableStack#move', assert => { + assert.isFunction(AsyncDisposableStack.prototype.move); + assert.arity(AsyncDisposableStack.prototype.move, 0); + assert.name(AsyncDisposableStack.prototype.move, 'move'); + assert.looksNative(AsyncDisposableStack.prototype.move); + assert.nonEnumerable(AsyncDisposableStack.prototype, 'move'); + + let result = ''; + const stack1 = new AsyncDisposableStack(); + + stack1.defer(() => result += '2'); + stack1.defer(() => result += '1'); + + const stack2 = stack1.move(); + + assert.false(stack1.disposed); + + return stack1.disposeAsync().then(() => { + assert.same(result, ''); + + assert.true(stack1.disposed); + + return stack2.disposeAsync(); + }).then(() => { + assert.same(result, '12'); + }); +}); + +QUnit.test('AsyncDisposableStack#@@asyncDispose', assert => { + assert.same(AsyncDisposableStack.prototype[Symbol.asyncDispose], AsyncDisposableStack.prototype.disposeAsync); +}); + +QUnit.test('AsyncDisposableStack#@@toStringTag', assert => { + assert.same(AsyncDisposableStack.prototype[Symbol.toStringTag], 'AsyncDisposableStack', '@@toStringTag'); +}); + +QUnit.test('AsyncDisposableStack#1', assert => { + let result = ''; + const stack = new AsyncDisposableStack(); + + stack.use({ [Symbol.asyncDispose]: () => result += '6' }); + stack.adopt({}, () => result += '5'); + stack.defer(() => result += '4'); + stack.use({ [Symbol.asyncDispose]: () => Promise.resolve(result += '3') }); + stack.adopt({}, () => Promise.resolve(result += '2')); + stack.defer(() => Promise.resolve(result += '1')); + + assert.false(stack.disposed); + + return stack.disposeAsync().then(it => { + assert.same(it, undefined); + assert.same(result, '123456'); + assert.true(stack.disposed); + return stack.disposeAsync(); + }).then(it => { + assert.same(it, undefined); + }); +}); + +QUnit.test('AsyncDisposableStack#2', assert => { + let result = ''; + const stack = new AsyncDisposableStack(); + + stack.use({ [Symbol.asyncDispose]: () => result += '6' }); + stack.adopt({}, () => { throw Error(5); }); + stack.defer(() => result += '4'); + stack.use({ [Symbol.asyncDispose]: () => Promise.resolve(result += '3') }); + stack.adopt({}, () => Promise.resolve(result += '2')); + stack.defer(() => Promise.resolve(result += '1')); + + return stack.disposeAsync().then(() => { + assert.avoid(); + }, error => { + assert.same(result, '12346'); + assert.true(error instanceof Error); + assert.same(error.message, '5'); + }); +}); + +QUnit.test('AsyncDisposableStack#3', assert => { + let result = ''; + const stack = new AsyncDisposableStack(); + + stack.use({ [Symbol.asyncDispose]: () => result += '6' }); + stack.adopt({}, () => { throw Error(5); }); + stack.defer(() => result += '4'); + stack.use({ [Symbol.asyncDispose]: () => Promise.reject(Error(3)) }); + stack.adopt({}, () => Promise.resolve(result += '2')); + stack.defer(() => Promise.resolve(result += '1')); + + return stack.disposeAsync().then(() => { + assert.avoid(); + }, error => { + assert.same(result, '1246'); + assert.true(error instanceof SuppressedError); + assert.same(error.error.message, '5'); + assert.same(error.suppressed.message, '3'); + }); +}); diff --git a/tests/unit-global/esnext.async-iterator.async-dispose.js b/tests/unit-global/esnext.async-iterator.async-dispose.js new file mode 100644 index 000000000000..a785baba31bc --- /dev/null +++ b/tests/unit-global/esnext.async-iterator.async-dispose.js @@ -0,0 +1,25 @@ +const { create } = Object; + +QUnit.test('AsyncIterator#@@asyncDispose', assert => { + const asyncDispose = AsyncIterator.prototype[Symbol.asyncDispose]; + assert.isFunction(asyncDispose); + assert.arity(asyncDispose, 0); + assert.looksNative(asyncDispose); + + return create(AsyncIterator.prototype)[Symbol.asyncDispose]().then(result => { + assert.same(result, undefined); + }).then(() => { + let called = false; + const iterator2 = create(AsyncIterator.prototype); + iterator2.return = function () { + called = true; + assert.same(this, iterator2); + return 7; + }; + + return iterator2[Symbol.asyncDispose]().then(result => { + assert.same(result, undefined); + assert.true(called); + }); + }); +}); diff --git a/tests/unit-global/esnext.disposable-stack.constructor.js b/tests/unit-global/esnext.disposable-stack.constructor.js new file mode 100644 index 000000000000..055e64bd9aa0 --- /dev/null +++ b/tests/unit-global/esnext.disposable-stack.constructor.js @@ -0,0 +1,182 @@ +import { STRICT } from '../helpers/constants'; + +QUnit.test('DisposableStack constructor', assert => { + assert.isFunction(DisposableStack); + assert.arity(DisposableStack, 0); + assert.name(DisposableStack, 'DisposableStack'); + assert.looksNative(DisposableStack); + + assert.throws(() => DisposableStack(), 'throws w/o `new`'); + assert.true(new DisposableStack() instanceof DisposableStack); + + assert.same(DisposableStack.prototype.constructor, DisposableStack); +}); + +QUnit.test('DisposableStack#dispose', assert => { + assert.isFunction(DisposableStack.prototype.dispose); + assert.arity(DisposableStack.prototype.dispose, 0); + assert.name(DisposableStack.prototype.dispose, 'dispose'); + assert.looksNative(DisposableStack.prototype.dispose); + assert.nonEnumerable(DisposableStack.prototype, 'dispose'); +}); + +QUnit.test('DisposableStack#use', assert => { + assert.isFunction(DisposableStack.prototype.use); + assert.arity(DisposableStack.prototype.use, 1); + assert.name(DisposableStack.prototype.use, 'use'); + assert.looksNative(DisposableStack.prototype.use); + assert.nonEnumerable(DisposableStack.prototype, 'use'); + + let result = ''; + const stack1 = new DisposableStack(); + const resource = { + [Symbol.dispose]() { + result += '1'; + assert.same(this, resource); + assert.same(arguments.length, 0); + }, + }; + + assert.same(stack1.use(resource), resource); + assert.same(stack1.dispose(), undefined); + assert.same(result, '1'); +}); + +QUnit.test('DisposableStack#adopt', assert => { + assert.isFunction(DisposableStack.prototype.adopt); + assert.arity(DisposableStack.prototype.adopt, 2); + assert.name(DisposableStack.prototype.adopt, 'adopt'); + assert.looksNative(DisposableStack.prototype.adopt); + assert.nonEnumerable(DisposableStack.prototype, 'adopt'); + + let result = ''; + const stack = new DisposableStack(); + const resource = {}; + + assert.same(stack.adopt(resource, function (arg) { + result += '1'; + if (STRICT) assert.same(this, undefined); + assert.same(arguments.length, 1); + assert.same(arg, resource); + }), resource); + + assert.same(stack.dispose(), undefined); + assert.same(result, '1'); +}); + +QUnit.test('DisposableStack#defer', assert => { + assert.isFunction(DisposableStack.prototype.defer); + assert.arity(DisposableStack.prototype.defer, 1); + assert.name(DisposableStack.prototype.defer, 'defer'); + assert.looksNative(DisposableStack.prototype.defer); + assert.nonEnumerable(DisposableStack.prototype, 'defer'); + + let result = ''; + const stack = new DisposableStack(); + + assert.same(stack.defer(function () { + result += '1'; + if (STRICT) assert.same(this, undefined); + assert.same(arguments.length, 0); + }), undefined); + + assert.same(stack.dispose(), undefined); + assert.same(result, '1'); +}); + +QUnit.test('DisposableStack#move', assert => { + assert.isFunction(DisposableStack.prototype.move); + assert.arity(DisposableStack.prototype.move, 0); + assert.name(DisposableStack.prototype.move, 'move'); + assert.looksNative(DisposableStack.prototype.move); + assert.nonEnumerable(DisposableStack.prototype, 'move'); + + let result = ''; + const stack = new DisposableStack(); + + stack.defer(() => result += '2'); + stack.defer(() => result += '1'); + + const stack2 = stack.move(); + + assert.false(stack.disposed); + + stack.dispose(); + + assert.same(result, ''); + + assert.true(stack.disposed); + + stack2.dispose(); + + assert.same(result, '12'); +}); + +QUnit.test('DisposableStack#@@dispose', assert => { + assert.same(DisposableStack.prototype[Symbol.dispose], DisposableStack.prototype.dispose); +}); + +QUnit.test('DisposableStack#@@toStringTag', assert => { + assert.same(DisposableStack.prototype[Symbol.toStringTag], 'DisposableStack', '@@toStringTag'); +}); + +QUnit.test('DisposableStack', assert => { + let result1 = ''; + const stack1 = new DisposableStack(); + + stack1.use({ [Symbol.dispose]: () => result1 += '6' }); + stack1.adopt({}, () => result1 += '5'); + stack1.defer(() => result1 += '4'); + stack1.use({ [Symbol.dispose]: () => result1 += '3' }); + stack1.adopt({}, () => result1 += '2'); + stack1.defer(() => result1 += '1'); + + assert.false(stack1.disposed); + assert.same(stack1.dispose(), undefined); + assert.same(result1, '123456'); + assert.true(stack1.disposed); + assert.same(stack1.dispose(), undefined); + + let result2 = ''; + const stack2 = new DisposableStack(); + let error2; + + stack2.use({ [Symbol.dispose]: () => result2 += '6' }); + stack2.adopt({}, () => { throw Error(5); }); + stack2.defer(() => result2 += '4'); + stack2.use({ [Symbol.dispose]: () => result2 += '3' }); + stack2.adopt({}, () => result2 += '2'); + stack2.defer(() => result2 += '1'); + + try { + stack2.dispose(); + } catch (error2$) { + error2 = error2$; + } + + assert.same(result2, '12346'); + assert.true(error2 instanceof Error); + assert.same(error2.message, '5'); + + let result3 = ''; + const stack3 = new DisposableStack(); + let error3; + + stack3.use({ [Symbol.dispose]: () => result3 += '6' }); + stack3.adopt({}, () => { throw Error(5); }); + stack3.defer(() => result3 += '4'); + stack3.use({ [Symbol.dispose]: () => { throw Error(3); } }); + stack3.adopt({}, () => result3 += '2'); + stack3.defer(() => result3 += '1'); + + try { + stack3.dispose(); + } catch (error3$) { + error3 = error3$; + } + + assert.same(result3, '1246'); + assert.true(error3 instanceof SuppressedError); + assert.same(error3.error.message, '5'); + assert.same(error3.suppressed.message, '3'); +}); diff --git a/tests/unit-global/esnext.iterator.dispose.js b/tests/unit-global/esnext.iterator.dispose.js new file mode 100644 index 000000000000..ebafc338f61a --- /dev/null +++ b/tests/unit-global/esnext.iterator.dispose.js @@ -0,0 +1,20 @@ +const { create } = Object; + +QUnit.test('Iterator#@@dispose', assert => { + const dispose = Iterator.prototype[Symbol.dispose]; + assert.isFunction(dispose); + assert.arity(dispose, 0); + assert.looksNative(dispose); + + assert.same(create(Iterator.prototype)[Symbol.dispose](), undefined); + + let called = false; + const iterator2 = create(Iterator.prototype); + iterator2.return = function () { + called = true; + assert.same(this, iterator2); + return 7; + }; + assert.same(iterator2[Symbol.dispose](), undefined); + assert.true(called); +}); diff --git a/tests/unit-global/esnext.suppressed-error.constructor.js b/tests/unit-global/esnext.suppressed-error.constructor.js new file mode 100644 index 000000000000..a1d62f87a3b0 --- /dev/null +++ b/tests/unit-global/esnext.suppressed-error.constructor.js @@ -0,0 +1,47 @@ +QUnit.test('SuppressedError', assert => { + assert.isFunction(SuppressedError); + assert.arity(SuppressedError, 3); + assert.name(SuppressedError, 'SuppressedError'); + assert.looksNative(SuppressedError); + assert.true(new SuppressedError() instanceof SuppressedError); + assert.true(new SuppressedError() instanceof Error); + assert.true(SuppressedError() instanceof SuppressedError); + assert.true(SuppressedError() instanceof Error); + + assert.same(SuppressedError().error, undefined); + assert.same(SuppressedError().suppressed, undefined); + assert.same(SuppressedError().message, ''); + assert.same(SuppressedError().cause, undefined); + assert.false('cause' in SuppressedError()); + assert.same(SuppressedError().name, 'SuppressedError'); + + assert.same(new SuppressedError().error, undefined); + assert.same(new SuppressedError().suppressed, undefined); + assert.same(new SuppressedError().message, ''); + assert.same(new SuppressedError().cause, undefined); + assert.false('cause' in new SuppressedError()); + assert.same(new SuppressedError().name, 'SuppressedError'); + + const error1 = SuppressedError(1, 2, 3, { cause: 4 }); + + assert.same(error1.error, 1); + assert.same(error1.suppressed, 2); + assert.same(error1.message, '3'); + assert.same(error1.cause, 4); + assert.same(error1.name, 'SuppressedError'); + + const error2 = new SuppressedError(1, 2, 3, { cause: 4 }); + + assert.same(error2.error, 1); + assert.same(error2.suppressed, 2); + assert.same(error2.message, '3'); + assert.same(error2.cause, 4); + assert.same(error2.name, 'SuppressedError'); + + assert.throws(() => SuppressedError(1, 2, Symbol()), 'throws on symbol as a message'); + assert.same(({}).toString.call(SuppressedError()), '[object Error]', 'Object#toString'); + + assert.same(SuppressedError.prototype.constructor, SuppressedError, 'prototype constructor'); + // eslint-disable-next-line no-prototype-builtins -- safe + assert.false(SuppressedError.prototype.hasOwnProperty('cause'), 'prototype has not cause'); +}); diff --git a/tests/unit-pure/esnext.async-disposable-stack.constructor.js b/tests/unit-pure/esnext.async-disposable-stack.constructor.js new file mode 100644 index 000000000000..c8f3f09d350c --- /dev/null +++ b/tests/unit-pure/esnext.async-disposable-stack.constructor.js @@ -0,0 +1,191 @@ +import { STRICT } from '../helpers/constants'; + +import Promise from 'core-js-pure/es/promise'; +import Symbol from 'core-js-pure/full/symbol'; +import AsyncDisposableStack from 'core-js-pure/full/async-disposable-stack'; +import SuppressedError from 'core-js-pure/full/suppressed-error'; + +QUnit.test('AsyncDisposableStack constructor', assert => { + assert.isFunction(AsyncDisposableStack); + assert.arity(AsyncDisposableStack, 0); + assert.name(AsyncDisposableStack, 'AsyncDisposableStack'); + + assert.throws(() => AsyncDisposableStack(), 'throws w/o `new`'); + assert.true(new AsyncDisposableStack() instanceof AsyncDisposableStack); + + assert.same(AsyncDisposableStack.prototype.constructor, AsyncDisposableStack); +}); + +QUnit.test('AsyncDisposableStack#disposeAsync', assert => { + assert.isFunction(AsyncDisposableStack.prototype.disposeAsync); + assert.arity(AsyncDisposableStack.prototype.disposeAsync, 0); + assert.name(AsyncDisposableStack.prototype.disposeAsync, 'disposeAsync'); + assert.nonEnumerable(AsyncDisposableStack.prototype, 'disposeAsync'); +}); + +QUnit.test('AsyncDisposableStack#use', assert => { + assert.isFunction(AsyncDisposableStack.prototype.use); + assert.arity(AsyncDisposableStack.prototype.use, 1); + assert.name(AsyncDisposableStack.prototype.use, 'use'); + assert.nonEnumerable(AsyncDisposableStack.prototype, 'use'); + + let result = ''; + const stack = new AsyncDisposableStack(); + const resource = { + [Symbol.asyncDispose]() { + result += '1'; + assert.same(this, resource); + assert.same(arguments.length, 0); + }, + }; + + assert.same(stack.use(resource), resource); + + return stack.disposeAsync().then(it => { + assert.same(it, undefined); + assert.same(result, '1'); + }); +}); + +QUnit.test('AsyncDisposableStack#adopt', assert => { + assert.isFunction(AsyncDisposableStack.prototype.adopt); + assert.arity(AsyncDisposableStack.prototype.adopt, 2); + assert.name(AsyncDisposableStack.prototype.adopt, 'adopt'); + assert.nonEnumerable(AsyncDisposableStack.prototype, 'adopt'); + + let result = ''; + const stack = new AsyncDisposableStack(); + const resource = {}; + + assert.same(stack.adopt(resource, function (arg) { + result += '1'; + if (STRICT) assert.same(this, undefined); + assert.same(arguments.length, 1); + assert.same(arg, resource); + }), resource); + + return stack.disposeAsync().then(it => { + assert.same(it, undefined); + assert.same(result, '1'); + }); +}); + +QUnit.test('AsyncDisposableStack#defer', assert => { + assert.isFunction(AsyncDisposableStack.prototype.defer); + assert.arity(AsyncDisposableStack.prototype.defer, 1); + assert.name(AsyncDisposableStack.prototype.defer, 'defer'); + assert.nonEnumerable(AsyncDisposableStack.prototype, 'defer'); + + let result = ''; + const stack = new AsyncDisposableStack(); + + assert.same(stack.defer(function () { + result += '1'; + if (STRICT) assert.same(this, undefined); + assert.same(arguments.length, 0); + }), undefined); + + return stack.disposeAsync().then(it => { + assert.same(it, undefined); + assert.same(result, '1'); + }); +}); + +QUnit.test('AsyncDisposableStack#move', assert => { + assert.isFunction(AsyncDisposableStack.prototype.move); + assert.arity(AsyncDisposableStack.prototype.move, 0); + assert.name(AsyncDisposableStack.prototype.move, 'move'); + assert.nonEnumerable(AsyncDisposableStack.prototype, 'move'); + + let result = ''; + const stack1 = new AsyncDisposableStack(); + + stack1.defer(() => result += '2'); + stack1.defer(() => result += '1'); + + const stack2 = stack1.move(); + + assert.false(stack1.disposed); + + return stack1.disposeAsync().then(() => { + assert.same(result, ''); + + assert.true(stack1.disposed); + + return stack2.disposeAsync(); + }).then(() => { + assert.same(result, '12'); + }); +}); + +QUnit.test('AsyncDisposableStack#@@asyncDispose', assert => { + assert.same(AsyncDisposableStack.prototype[Symbol.asyncDispose], AsyncDisposableStack.prototype.disposeAsync); +}); + +QUnit.test('AsyncDisposableStack#@@toStringTag', assert => { + assert.same(AsyncDisposableStack.prototype[Symbol.toStringTag], 'AsyncDisposableStack', '@@toStringTag'); +}); + +QUnit.test('AsyncDisposableStack#1', assert => { + let result = ''; + const stack = new AsyncDisposableStack(); + + stack.use({ [Symbol.asyncDispose]: () => result += '6' }); + stack.adopt({}, () => result += '5'); + stack.defer(() => result += '4'); + stack.use({ [Symbol.asyncDispose]: () => Promise.resolve(result += '3') }); + stack.adopt({}, () => Promise.resolve(result += '2')); + stack.defer(() => Promise.resolve(result += '1')); + + assert.false(stack.disposed); + + return stack.disposeAsync().then(it => { + assert.same(it, undefined); + assert.same(result, '123456'); + assert.true(stack.disposed); + return stack.disposeAsync(); + }).then(it => { + assert.same(it, undefined); + }); +}); + +QUnit.test('AsyncDisposableStack#2', assert => { + let result = ''; + const stack = new AsyncDisposableStack(); + + stack.use({ [Symbol.asyncDispose]: () => result += '6' }); + stack.adopt({}, () => { throw Error(5); }); + stack.defer(() => result += '4'); + stack.use({ [Symbol.asyncDispose]: () => Promise.resolve(result += '3') }); + stack.adopt({}, () => Promise.resolve(result += '2')); + stack.defer(() => Promise.resolve(result += '1')); + + return stack.disposeAsync().then(() => { + assert.avoid(); + }, error => { + assert.same(result, '12346'); + assert.true(error instanceof Error); + assert.same(error.message, '5'); + }); +}); + +QUnit.test('AsyncDisposableStack#3', assert => { + let result = ''; + const stack = new AsyncDisposableStack(); + + stack.use({ [Symbol.asyncDispose]: () => result += '6' }); + stack.adopt({}, () => { throw Error(5); }); + stack.defer(() => result += '4'); + stack.use({ [Symbol.asyncDispose]: () => Promise.reject(Error(3)) }); + stack.adopt({}, () => Promise.resolve(result += '2')); + stack.defer(() => Promise.resolve(result += '1')); + + return stack.disposeAsync().then(() => { + assert.avoid(); + }, error => { + assert.same(result, '1246'); + assert.true(error instanceof SuppressedError); + assert.same(error.error.message, '5'); + assert.same(error.suppressed.message, '3'); + }); +}); diff --git a/tests/unit-pure/esnext.async-iterator.async-dispose.js b/tests/unit-pure/esnext.async-iterator.async-dispose.js new file mode 100644 index 000000000000..edf5e29cc394 --- /dev/null +++ b/tests/unit-pure/esnext.async-iterator.async-dispose.js @@ -0,0 +1,26 @@ +import AsyncIterator from 'core-js-pure/full/async-iterator'; +import Symbol from 'core-js-pure/full/symbol'; +import create from 'core-js-pure/es/object/create'; + +QUnit.test('AsyncIterator#@@asyncDispose', assert => { + const asyncDispose = AsyncIterator.prototype[Symbol.asyncDispose]; + assert.isFunction(asyncDispose); + assert.arity(asyncDispose, 0); + + return create(AsyncIterator.prototype)[Symbol.asyncDispose]().then(result => { + assert.same(result, undefined); + }).then(() => { + let called = false; + const iterator2 = create(AsyncIterator.prototype); + iterator2.return = function () { + called = true; + assert.same(this, iterator2); + return 7; + }; + + return iterator2[Symbol.asyncDispose]().then(result => { + assert.same(result, undefined); + assert.true(called); + }); + }); +}); diff --git a/tests/unit-pure/esnext.disposable-stack.constructor.js b/tests/unit-pure/esnext.disposable-stack.constructor.js new file mode 100644 index 000000000000..c9f215b33850 --- /dev/null +++ b/tests/unit-pure/esnext.disposable-stack.constructor.js @@ -0,0 +1,180 @@ +import { STRICT } from '../helpers/constants'; + +import Symbol from 'core-js-pure/actual/symbol'; +import DisposableStack from 'core-js-pure/actual/disposable-stack'; +import SuppressedError from 'core-js-pure/actual/suppressed-error'; + +QUnit.test('DisposableStack constructor', assert => { + assert.isFunction(DisposableStack); + assert.arity(DisposableStack, 0); + assert.name(DisposableStack, 'DisposableStack'); + + assert.throws(() => DisposableStack(), 'throws w/o `new`'); + assert.true(new DisposableStack() instanceof DisposableStack); + + assert.same(DisposableStack.prototype.constructor, DisposableStack); +}); + +QUnit.test('DisposableStack#dispose', assert => { + assert.isFunction(DisposableStack.prototype.dispose); + assert.arity(DisposableStack.prototype.dispose, 0); + assert.name(DisposableStack.prototype.dispose, 'dispose'); + assert.nonEnumerable(DisposableStack.prototype, 'dispose'); +}); + +QUnit.test('DisposableStack#use', assert => { + assert.isFunction(DisposableStack.prototype.use); + assert.arity(DisposableStack.prototype.use, 1); + assert.name(DisposableStack.prototype.use, 'use'); + assert.nonEnumerable(DisposableStack.prototype, 'use'); + + let result = ''; + const stack = new DisposableStack(); + const resource = { + [Symbol.dispose]() { + result += '1'; + assert.same(this, resource); + assert.same(arguments.length, 0); + }, + }; + + assert.same(stack.use(resource), resource); + assert.same(stack.dispose(), undefined); + assert.same(result, '1'); +}); + +QUnit.test('DisposableStack#adopt', assert => { + assert.isFunction(DisposableStack.prototype.adopt); + assert.arity(DisposableStack.prototype.adopt, 2); + assert.name(DisposableStack.prototype.adopt, 'adopt'); + assert.nonEnumerable(DisposableStack.prototype, 'adopt'); + + let result = ''; + const stack = new DisposableStack(); + const resource = {}; + + assert.same(stack.adopt(resource, function (arg) { + result += '1'; + if (STRICT) assert.same(this, undefined); + assert.same(arguments.length, 1); + assert.same(arg, resource); + }), resource); + + assert.same(stack.dispose(), undefined); + assert.same(result, '1'); +}); + +QUnit.test('DisposableStack#defer', assert => { + assert.isFunction(DisposableStack.prototype.defer); + assert.arity(DisposableStack.prototype.defer, 1); + assert.name(DisposableStack.prototype.defer, 'defer'); + assert.nonEnumerable(DisposableStack.prototype, 'defer'); + + let result = ''; + const stack = new DisposableStack(); + + assert.same(stack.defer(function () { + result += '1'; + if (STRICT) assert.same(this, undefined); + assert.same(arguments.length, 0); + }), undefined); + + assert.same(stack.dispose(), undefined); + assert.same(result, '1'); +}); + +QUnit.test('DisposableStack#move', assert => { + assert.isFunction(DisposableStack.prototype.move); + assert.arity(DisposableStack.prototype.move, 0); + assert.name(DisposableStack.prototype.move, 'move'); + assert.nonEnumerable(DisposableStack.prototype, 'move'); + + let result = ''; + const stack1 = new DisposableStack(); + + stack1.defer(() => result += '2'); + stack1.defer(() => result += '1'); + + const stack2 = stack1.move(); + + assert.false(stack1.disposed); + + stack1.dispose(); + + assert.same(result, ''); + + assert.true(stack1.disposed); + + stack2.dispose(); + + assert.same(result, '12'); +}); + +QUnit.test('DisposableStack#@@dispose', assert => { + assert.same(DisposableStack.prototype[Symbol.dispose], DisposableStack.prototype.dispose); +}); + +QUnit.test('DisposableStack#@@toStringTag', assert => { + assert.same(DisposableStack.prototype[Symbol.toStringTag], 'DisposableStack', '@@toStringTag'); +}); + +QUnit.test('DisposableStack', assert => { + let result1 = ''; + const stack1 = new DisposableStack(); + + stack1.use({ [Symbol.dispose]: () => result1 += '6' }); + stack1.adopt({}, () => result1 += '5'); + stack1.defer(() => result1 += '4'); + stack1.use({ [Symbol.dispose]: () => result1 += '3' }); + stack1.adopt({}, () => result1 += '2'); + stack1.defer(() => result1 += '1'); + + assert.false(stack1.disposed); + assert.same(stack1.dispose(), undefined); + assert.same(result1, '123456'); + assert.true(stack1.disposed); + assert.same(stack1.dispose(), undefined); + + let result2 = ''; + const stack2 = new DisposableStack(); + let error2; + + stack2.use({ [Symbol.dispose]: () => result2 += '6' }); + stack2.adopt({}, () => { throw Error(5); }); + stack2.defer(() => result2 += '4'); + stack2.use({ [Symbol.dispose]: () => result2 += '3' }); + stack2.adopt({}, () => result2 += '2'); + stack2.defer(() => result2 += '1'); + + try { + stack2.dispose(); + } catch (error2$) { + error2 = error2$; + } + + assert.same(result2, '12346'); + assert.true(error2 instanceof Error); + assert.same(error2.message, '5'); + + let result3 = ''; + const stack3 = new DisposableStack(); + let error3; + + stack3.use({ [Symbol.dispose]: () => result3 += '6' }); + stack3.adopt({}, () => { throw Error(5); }); + stack3.defer(() => result3 += '4'); + stack3.use({ [Symbol.dispose]: () => { throw Error(3); } }); + stack3.adopt({}, () => result3 += '2'); + stack3.defer(() => result3 += '1'); + + try { + stack3.dispose(); + } catch (error3$) { + error3 = error3$; + } + + assert.same(result3, '1246'); + assert.true(error3 instanceof SuppressedError); + assert.same(error3.error.message, '5'); + assert.same(error3.suppressed.message, '3'); +}); diff --git a/tests/unit-pure/esnext.iterator.dispose.js b/tests/unit-pure/esnext.iterator.dispose.js new file mode 100644 index 000000000000..9f224fdeb0b6 --- /dev/null +++ b/tests/unit-pure/esnext.iterator.dispose.js @@ -0,0 +1,21 @@ +import Iterator from 'core-js-pure/full/iterator'; +import Symbol from 'core-js-pure/actual/symbol'; +import create from 'core-js-pure/es/object/create'; + +QUnit.test('Iterator#@@dispose', assert => { + const dispose = Iterator.prototype[Symbol.dispose]; + assert.isFunction(dispose); + assert.arity(dispose, 0); + + assert.same(create(Iterator.prototype)[Symbol.dispose](), undefined); + + let called = false; + const iterator2 = create(Iterator.prototype); + iterator2.return = function () { + called = true; + assert.same(this, iterator2); + return 7; + }; + assert.same(iterator2[Symbol.dispose](), undefined); + assert.true(called); +}); diff --git a/tests/unit-pure/esnext.suppressed-error.constructor.js b/tests/unit-pure/esnext.suppressed-error.constructor.js new file mode 100644 index 000000000000..8f03d519e509 --- /dev/null +++ b/tests/unit-pure/esnext.suppressed-error.constructor.js @@ -0,0 +1,49 @@ +import SuppressedError from 'core-js-pure/actual/suppressed-error'; +import Symbol from 'core-js-pure/es/symbol'; +import toString from 'core-js-pure/es/object/to-string'; + +QUnit.test('SuppressedError', assert => { + assert.isFunction(SuppressedError); + assert.arity(SuppressedError, 3); + assert.name(SuppressedError, 'SuppressedError'); + assert.true(new SuppressedError() instanceof SuppressedError); + assert.true(new SuppressedError() instanceof Error); + assert.true(SuppressedError() instanceof SuppressedError); + assert.true(SuppressedError() instanceof Error); + + assert.same(SuppressedError().error, undefined); + assert.same(SuppressedError().suppressed, undefined); + assert.same(SuppressedError().message, ''); + assert.same(SuppressedError().cause, undefined); + assert.false('cause' in SuppressedError()); + assert.same(SuppressedError().name, 'SuppressedError'); + + assert.same(new SuppressedError().error, undefined); + assert.same(new SuppressedError().suppressed, undefined); + assert.same(new SuppressedError().message, ''); + assert.same(new SuppressedError().cause, undefined); + assert.false('cause' in new SuppressedError()); + assert.same(new SuppressedError().name, 'SuppressedError'); + + const error1 = SuppressedError(1, 2, 3, { cause: 4 }); + + assert.same(error1.error, 1); + assert.same(error1.suppressed, 2); + assert.same(error1.message, '3'); + assert.same(error1.cause, 4); + assert.same(error1.name, 'SuppressedError'); + + const error2 = new SuppressedError(1, 2, 3, { cause: 4 }); + + assert.same(error2.error, 1); + assert.same(error2.suppressed, 2); + assert.same(error2.message, '3'); + assert.same(error2.cause, 4); + assert.same(error2.name, 'SuppressedError'); + + assert.throws(() => SuppressedError(1, 2, Symbol()), 'throws on symbol as a message'); + assert.same(toString(SuppressedError()), '[object Error]', 'Object#toString'); + + // eslint-disable-next-line no-prototype-builtins -- safe + assert.false(SuppressedError.prototype.hasOwnProperty('cause'), 'prototype has not cause'); +}); diff --git a/tests/unit-pure/esnext.symbol.dispose.js b/tests/unit-pure/esnext.symbol.dispose.js index 12cb13730c05..e9bb124b3ff9 100644 --- a/tests/unit-pure/esnext.symbol.dispose.js +++ b/tests/unit-pure/esnext.symbol.dispose.js @@ -1,4 +1,4 @@ -import Symbol from 'core-js-pure/full/symbol'; +import Symbol from 'core-js-pure/actual/symbol'; QUnit.test('Symbol.dispose', assert => { assert.true('dispose' in Symbol, 'Symbol.dispose available');