From 19ff49dafe87a3dc37ca33beb7e0b864c8492a9a Mon Sep 17 00:00:00 2001 From: Denis Pushkarev Date: Wed, 26 Jul 2023 15:42:42 +0700 Subject: [PATCH] `Iterator` is not constructible from the active function object (works as an abstract class) --- CHANGELOG.md | 5 +++-- .../core-js/modules/esnext.async-iterator.constructor.js | 4 ++++ packages/core-js/modules/esnext.iterator.constructor.js | 3 +++ tests/unit-global/esnext.async-iterator.constructor.js | 9 ++++++++- tests/unit-global/esnext.async-iterator.from.js | 4 ++-- tests/unit-global/esnext.iterator.constructor.js | 9 +++++++-- tests/unit-pure/esnext.async-iterator.constructor.js | 9 ++++++++- tests/unit-pure/esnext.async-iterator.from.js | 3 ++- tests/unit-pure/esnext.iterator.constructor.js | 9 +++++++-- 9 files changed, 44 insertions(+), 11 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 273aa800b57b..8e29e63cafc5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,8 +9,9 @@ - [`Set` methods stage 3 proposal](https://github.com/tc39/proposal-set-methods), July 2023 TC39 meeting updates:: - Throw on negative `Set` sizes, [proposal-set-methods/88](https://github.com/tc39/proposal-set-methods/pull/88) - Removed `IsCallable` check in `GetKeysIterator`, [proposal-set-methods/101](https://github.com/tc39/proposal-set-methods/pull/101) -- [Iterator Helpers stage 3 proposal](https://github.com/tc39/proposal-iterator-helpers), July 2023 TC39 meeting updates:: - - Avoid creating observable `String` wrapper objects, [proposal-iterator-helpers/281](https://github.com/tc39/proposal-iterator-helpers/pull/281) +- [Iterator Helpers stage 3 proposal](https://github.com/tc39/proposal-iterator-helpers): + - Avoid creating observable `String` wrapper objects, July 2023 TC39 meeting update, [proposal-iterator-helpers/281](https://github.com/tc39/proposal-iterator-helpers/pull/281) + - `Iterator` is not constructible from the active function object (works as an abstract class) - Async explicit resource management: - Built-ins: - `Symbol.asyncDispose` diff --git a/packages/core-js/modules/esnext.async-iterator.constructor.js b/packages/core-js/modules/esnext.async-iterator.constructor.js index e83aae9f65b9..010bf05a74a8 100644 --- a/packages/core-js/modules/esnext.async-iterator.constructor.js +++ b/packages/core-js/modules/esnext.async-iterator.constructor.js @@ -1,6 +1,7 @@ 'use strict'; var $ = require('../internals/export'); var anInstance = require('../internals/an-instance'); +var getPrototypeOf = require('../internals/object-get-prototype-of'); var createNonEnumerableProperty = require('../internals/create-non-enumerable-property'); var hasOwn = require('../internals/has-own-property'); var wellKnownSymbol = require('../internals/well-known-symbol'); @@ -9,8 +10,11 @@ var IS_PURE = require('../internals/is-pure'); var TO_STRING_TAG = wellKnownSymbol('toStringTag'); +var $TypeError = TypeError; + var AsyncIteratorConstructor = function AsyncIterator() { anInstance(this, AsyncIteratorPrototype); + if (getPrototypeOf(this) === AsyncIteratorPrototype) throw $TypeError('Abstract class AsyncIterator not directly constructable'); }; AsyncIteratorConstructor.prototype = AsyncIteratorPrototype; diff --git a/packages/core-js/modules/esnext.iterator.constructor.js b/packages/core-js/modules/esnext.iterator.constructor.js index 8c833b47d868..76571d29b775 100644 --- a/packages/core-js/modules/esnext.iterator.constructor.js +++ b/packages/core-js/modules/esnext.iterator.constructor.js @@ -3,6 +3,7 @@ var $ = require('../internals/export'); var global = require('../internals/global'); var anInstance = require('../internals/an-instance'); var isCallable = require('../internals/is-callable'); +var getPrototypeOf = require('../internals/object-get-prototype-of'); var createNonEnumerableProperty = require('../internals/create-non-enumerable-property'); var fails = require('../internals/fails'); var hasOwn = require('../internals/has-own-property'); @@ -12,6 +13,7 @@ var IS_PURE = require('../internals/is-pure'); var TO_STRING_TAG = wellKnownSymbol('toStringTag'); +var $TypeError = TypeError; var NativeIterator = global.Iterator; // FF56- have non-standard global helper `Iterator` @@ -23,6 +25,7 @@ var FORCED = IS_PURE var IteratorConstructor = function Iterator() { anInstance(this, IteratorPrototype); + if (getPrototypeOf(this) === IteratorPrototype) throw $TypeError('Abstract class Iterator not directly constructable'); }; if (!hasOwn(IteratorPrototype, TO_STRING_TAG)) { diff --git a/tests/unit-global/esnext.async-iterator.constructor.js b/tests/unit-global/esnext.async-iterator.constructor.js index e8a39151ba28..2b33fe300226 100644 --- a/tests/unit-global/esnext.async-iterator.constructor.js +++ b/tests/unit-global/esnext.async-iterator.constructor.js @@ -1,3 +1,5 @@ +import { nativeSubclass } from '../helpers/helpers'; + const { getPrototypeOf } = Object; QUnit.test('AsyncIterator', assert => { @@ -22,7 +24,12 @@ QUnit.test('AsyncIterator', assert => { assert.true(AsyncIterator.from([1, 2, 3]) instanceof AsyncIterator, 'Async From Proxy'); assert.true(AsyncIterator.from([1, 2, 3]).drop(1) instanceof AsyncIterator, 'Async Drop Proxy'); - assert.true(new AsyncIterator() instanceof AsyncIterator, 'constructor'); + if (nativeSubclass) { + const Sub = nativeSubclass(AsyncIterator); + assert.true(new Sub() instanceof AsyncIterator, 'abstract constructor'); + } + + assert.throws(() => new AsyncIterator(), 'direct constructor throws'); assert.throws(() => AsyncIterator(), 'throws w/o `new`'); }); diff --git a/tests/unit-global/esnext.async-iterator.from.js b/tests/unit-global/esnext.async-iterator.from.js index 0b1efd7c7a92..bc1b59330ee1 100644 --- a/tests/unit-global/esnext.async-iterator.from.js +++ b/tests/unit-global/esnext.async-iterator.from.js @@ -1,4 +1,4 @@ -const { assign } = Object; +const { assign, create } = Object; QUnit.test('AsyncIterator.from', assert => { const { from } = AsyncIterator; @@ -13,7 +13,7 @@ QUnit.test('AsyncIterator.from', assert => { assert.true(AsyncIterator.from([]) instanceof AsyncIterator, 'proxy, iterable'); - const asyncIterator = assign(new AsyncIterator(), { + const asyncIterator = assign(create(AsyncIterator.prototype), { next: () => { /* empty */ }, }); diff --git a/tests/unit-global/esnext.iterator.constructor.js b/tests/unit-global/esnext.iterator.constructor.js index 48956bc3d2d3..ad3cadb518d5 100644 --- a/tests/unit-global/esnext.iterator.constructor.js +++ b/tests/unit-global/esnext.iterator.constructor.js @@ -1,4 +1,4 @@ -import { createIterator } from '../helpers/helpers'; +import { createIterator, nativeSubclass } from '../helpers/helpers'; const { getPrototypeOf } = Object; @@ -28,7 +28,12 @@ QUnit.test('Iterator', assert => { assert.true(Iterator.from(createIterator([1, 2, 3])) instanceof Iterator, 'From Proxy'); assert.true([].values().drop(1) instanceof Iterator, 'Drop Proxy'); - assert.true(new Iterator() instanceof Iterator, 'constructor'); + if (nativeSubclass) { + const Sub = nativeSubclass(Iterator); + assert.true(new Sub() instanceof Iterator, 'abstract constructor'); + } + + assert.throws(() => new Iterator(), 'direct constructor throws'); assert.throws(() => Iterator(), 'throws w/o `new`'); }); diff --git a/tests/unit-pure/esnext.async-iterator.constructor.js b/tests/unit-pure/esnext.async-iterator.constructor.js index d06025bf46fe..2a1f3f7e7da2 100644 --- a/tests/unit-pure/esnext.async-iterator.constructor.js +++ b/tests/unit-pure/esnext.async-iterator.constructor.js @@ -1,3 +1,5 @@ +import { nativeSubclass } from '../helpers/helpers'; + import Symbol from 'core-js-pure/es/symbol'; import AsyncIterator from 'core-js-pure/actual/async-iterator'; @@ -9,7 +11,12 @@ QUnit.test('AsyncIterator', assert => { assert.true(AsyncIterator.from([1, 2, 3]) instanceof AsyncIterator, 'Async From Proxy'); assert.true(AsyncIterator.from([1, 2, 3]).drop(1) instanceof AsyncIterator, 'Async Drop Proxy'); - assert.true(new AsyncIterator() instanceof AsyncIterator, 'constructor'); + if (nativeSubclass) { + const Sub = nativeSubclass(AsyncIterator); + assert.true(new Sub() instanceof AsyncIterator, 'abstract constructor'); + } + + assert.throws(() => new AsyncIterator(), 'direct constructor throws'); assert.throws(() => AsyncIterator(), 'throws w/o `new`'); }); diff --git a/tests/unit-pure/esnext.async-iterator.from.js b/tests/unit-pure/esnext.async-iterator.from.js index 202efa57030c..9910ed9bde99 100644 --- a/tests/unit-pure/esnext.async-iterator.from.js +++ b/tests/unit-pure/esnext.async-iterator.from.js @@ -1,5 +1,6 @@ import Promise from 'core-js-pure/es/promise'; import assign from 'core-js-pure/es/object/assign'; +import create from 'core-js-pure/es/object/create'; import values from 'core-js-pure/es/array/values'; import AsyncIterator from 'core-js-pure/actual/async-iterator'; @@ -13,7 +14,7 @@ QUnit.test('AsyncIterator.from', assert => { assert.true(AsyncIterator.from([]) instanceof AsyncIterator, 'proxy, iterable'); - const asyncIterator = assign(new AsyncIterator(), { + const asyncIterator = assign(create(AsyncIterator.prototype), { next: () => { /* empty */ }, }); diff --git a/tests/unit-pure/esnext.iterator.constructor.js b/tests/unit-pure/esnext.iterator.constructor.js index dc27e7b54b68..efcc299e7357 100644 --- a/tests/unit-pure/esnext.iterator.constructor.js +++ b/tests/unit-pure/esnext.iterator.constructor.js @@ -1,4 +1,4 @@ -import { createIterator } from '../helpers/helpers'; +import { createIterator, nativeSubclass } from '../helpers/helpers'; import Symbol from 'core-js-pure/es/symbol'; import Iterator from 'core-js-pure/actual/iterator'; @@ -9,7 +9,12 @@ QUnit.test('Iterator', assert => { assert.true(Iterator.from(createIterator([1, 2, 3])) instanceof Iterator, 'From Proxy'); - assert.true(new Iterator() instanceof Iterator, 'constructor'); + if (nativeSubclass) { + const Sub = nativeSubclass(Iterator); + assert.true(new Sub() instanceof Iterator, 'abstract constructor'); + } + + assert.throws(() => new Iterator(), 'direct constructor throws'); assert.throws(() => Iterator(), 'throws w/o `new`'); });