From 55b232c85f7b7e6e6d70809455fd365404668b74 Mon Sep 17 00:00:00 2001 From: Denis Pushkarev Date: Tue, 2 Nov 2021 01:36:03 +0700 Subject: [PATCH] add a workaround for FF26- bug where `ArrayBuffer`s are non-extensible, but `Object.isExtensible` does not report it --- CHANGELOG.md | 4 ++++ .../internals/array-buffer-non-extensible.js | 10 ++++++++++ packages/core-js/internals/internal-metadata.js | 6 +----- .../core-js/internals/object-is-extensible.js | 16 ++++++++++++++++ .../core-js/modules/es.object.is-extensible.js | 14 ++++---------- packages/core-js/modules/es.object.is-frozen.js | 8 ++++++-- packages/core-js/modules/es.object.is-sealed.js | 8 ++++++-- .../core-js/modules/es.reflect.is-extensible.js | 6 ++---- packages/core-js/modules/es.weak-map.js | 3 +-- tests/pure/es.map.js | 6 ++++++ tests/pure/es.set.js | 8 +++++++- tests/pure/es.weak-map.js | 6 ++++++ tests/pure/es.weak-set.js | 6 ++++++ tests/tests/es.map.js | 4 ++++ tests/tests/es.set.js | 6 +++++- tests/tests/es.weak-map.js | 4 ++++ tests/tests/es.weak-set.js | 4 ++++ 17 files changed, 92 insertions(+), 27 deletions(-) create mode 100644 packages/core-js/internals/array-buffer-non-extensible.js create mode 100644 packages/core-js/internals/object-is-extensible.js diff --git a/CHANGELOG.md b/CHANGELOG.md index 1405d1269733..639b175821a0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,10 @@ ## Changelog ##### Unreleased +- Added a workaround for FF26- bug where `ArrayBuffer`s are non-extensible, but `Object.isExtensible` does not report it: + - Fixed in `Object.{ isExtensible, isSealed, isFrozen }` and `Reflect.isExtensible` + - Fixed handling of `ArrayBuffer`s as collections keys - Fixed `Object#toString` on `AggregateError` in IE10- +- Fixed possible lack of dependencies of `WeakMap` in IE8- - `.findLast` methods family marked as supported [from Chrome 97](https://chromestatus.com/features#milestone%3D97) - Fixed inheritance of Electron compat data `web.` modules - Fixed Safari 15.1 compat data (some features were not added) diff --git a/packages/core-js/internals/array-buffer-non-extensible.js b/packages/core-js/internals/array-buffer-non-extensible.js new file mode 100644 index 000000000000..b66872d3377d --- /dev/null +++ b/packages/core-js/internals/array-buffer-non-extensible.js @@ -0,0 +1,10 @@ +// FF26- bug: ArrayBuffers are non-extensible, but Object.isExtensible does not report it +var fails = require('../internals/fails'); + +module.exports = fails(function () { + if (typeof ArrayBuffer == 'function') { + var buffer = new ArrayBuffer(8); + // eslint-disable-next-line es/no-object-isextensible, es/no-object-defineproperty -- safe + if (Object.isExtensible(buffer)) Object.defineProperty(buffer, 'a', { value: 8 }); + } +}); diff --git a/packages/core-js/internals/internal-metadata.js b/packages/core-js/internals/internal-metadata.js index fd3b3883e402..cb7d284e1dfb 100644 --- a/packages/core-js/internals/internal-metadata.js +++ b/packages/core-js/internals/internal-metadata.js @@ -6,6 +6,7 @@ var hasOwn = require('../internals/has-own-property'); var defineProperty = require('../internals/object-define-property').f; var getOwnPropertyNamesModule = require('../internals/object-get-own-property-names'); var getOwnPropertyNamesExternalModule = require('../internals/object-get-own-property-names-external'); +var isExtensible = require('../internals/object-is-extensible'); var uid = require('../internals/uid'); var FREEZING = require('../internals/freezing'); @@ -13,11 +14,6 @@ var REQUIRED = false; var METADATA = uid('meta'); var id = 0; -// eslint-disable-next-line es/no-object-isextensible -- safe -var isExtensible = Object.isExtensible || function () { - return true; -}; - var setMetadata = function (it) { defineProperty(it, METADATA, { value: { objectID: 'O' + id++, // object ID diff --git a/packages/core-js/internals/object-is-extensible.js b/packages/core-js/internals/object-is-extensible.js new file mode 100644 index 000000000000..93cadf333d67 --- /dev/null +++ b/packages/core-js/internals/object-is-extensible.js @@ -0,0 +1,16 @@ +var fails = require('../internals/fails'); +var isObject = require('../internals/is-object'); +var classof = require('../internals/classof-raw'); +var ARRAY_BUFFER_NON_EXTENSIBLE = require('../internals/array-buffer-non-extensible'); + +// eslint-disable-next-line es/no-object-isextensible -- safe +var $isExtensible = Object.isExtensible; +var FAILS_ON_PRIMITIVES = fails(function () { $isExtensible(1); }); + +// `Object.isExtensible` method +// https://tc39.es/ecma262/#sec-object.isextensible +module.exports = (FAILS_ON_PRIMITIVES || ARRAY_BUFFER_NON_EXTENSIBLE) ? function isExtensible(it) { + if (!isObject(it)) return false; + if (ARRAY_BUFFER_NON_EXTENSIBLE && classof(it) == 'ArrayBuffer') return false; + return $isExtensible ? $isExtensible(it) : true; +} : $isExtensible; diff --git a/packages/core-js/modules/es.object.is-extensible.js b/packages/core-js/modules/es.object.is-extensible.js index fb5122997db0..162327321b5c 100644 --- a/packages/core-js/modules/es.object.is-extensible.js +++ b/packages/core-js/modules/es.object.is-extensible.js @@ -1,15 +1,9 @@ var $ = require('../internals/export'); -var fails = require('../internals/fails'); -var isObject = require('../internals/is-object'); - -// eslint-disable-next-line es/no-object-isextensible -- safe -var $isExtensible = Object.isExtensible; -var FAILS_ON_PRIMITIVES = fails(function () { $isExtensible(1); }); +var $isExtensible = require('../internals/object-is-extensible'); // `Object.isExtensible` method // https://tc39.es/ecma262/#sec-object.isextensible -$({ target: 'Object', stat: true, forced: FAILS_ON_PRIMITIVES }, { - isExtensible: function isExtensible(it) { - return isObject(it) ? $isExtensible ? $isExtensible(it) : true : false; - } +// eslint-disable-next-line es/no-object-isextensible -- safe +$({ target: 'Object', stat: true, forced: Object.isExtensible !== $isExtensible }, { + isExtensible: $isExtensible }); diff --git a/packages/core-js/modules/es.object.is-frozen.js b/packages/core-js/modules/es.object.is-frozen.js index d292a98aca69..f261afa671d9 100644 --- a/packages/core-js/modules/es.object.is-frozen.js +++ b/packages/core-js/modules/es.object.is-frozen.js @@ -1,6 +1,8 @@ var $ = require('../internals/export'); var fails = require('../internals/fails'); var isObject = require('../internals/is-object'); +var classof = require('../internals/classof-raw'); +var ARRAY_BUFFER_NON_EXTENSIBLE = require('../internals/array-buffer-non-extensible'); // eslint-disable-next-line es/no-object-isfrozen -- safe var $isFrozen = Object.isFrozen; @@ -8,8 +10,10 @@ var FAILS_ON_PRIMITIVES = fails(function () { $isFrozen(1); }); // `Object.isFrozen` method // https://tc39.es/ecma262/#sec-object.isfrozen -$({ target: 'Object', stat: true, forced: FAILS_ON_PRIMITIVES }, { +$({ target: 'Object', stat: true, forced: FAILS_ON_PRIMITIVES || ARRAY_BUFFER_NON_EXTENSIBLE }, { isFrozen: function isFrozen(it) { - return isObject(it) ? $isFrozen ? $isFrozen(it) : false : true; + if (!isObject(it)) return true; + if (ARRAY_BUFFER_NON_EXTENSIBLE && classof(it) == 'ArrayBuffer') return true; + return $isFrozen ? $isFrozen(it) : false; } }); diff --git a/packages/core-js/modules/es.object.is-sealed.js b/packages/core-js/modules/es.object.is-sealed.js index 98b1538e8b15..ea8f146aef19 100644 --- a/packages/core-js/modules/es.object.is-sealed.js +++ b/packages/core-js/modules/es.object.is-sealed.js @@ -1,6 +1,8 @@ var $ = require('../internals/export'); var fails = require('../internals/fails'); var isObject = require('../internals/is-object'); +var classof = require('../internals/classof-raw'); +var ARRAY_BUFFER_NON_EXTENSIBLE = require('../internals/array-buffer-non-extensible'); // eslint-disable-next-line es/no-object-issealed -- safe var $isSealed = Object.isSealed; @@ -8,8 +10,10 @@ var FAILS_ON_PRIMITIVES = fails(function () { $isSealed(1); }); // `Object.isSealed` method // https://tc39.es/ecma262/#sec-object.issealed -$({ target: 'Object', stat: true, forced: FAILS_ON_PRIMITIVES }, { +$({ target: 'Object', stat: true, forced: FAILS_ON_PRIMITIVES || ARRAY_BUFFER_NON_EXTENSIBLE }, { isSealed: function isSealed(it) { - return isObject(it) ? $isSealed ? $isSealed(it) : false : true; + if (!isObject(it)) return true; + if (ARRAY_BUFFER_NON_EXTENSIBLE && classof(it) == 'ArrayBuffer') return true; + return $isSealed ? $isSealed(it) : false; } }); diff --git a/packages/core-js/modules/es.reflect.is-extensible.js b/packages/core-js/modules/es.reflect.is-extensible.js index 1bf2c9292643..b0474c289b2e 100644 --- a/packages/core-js/modules/es.reflect.is-extensible.js +++ b/packages/core-js/modules/es.reflect.is-extensible.js @@ -1,14 +1,12 @@ var $ = require('../internals/export'); var anObject = require('../internals/an-object'); - -// eslint-disable-next-line es/no-object-isextensible -- safe -var objectIsExtensible = Object.isExtensible; +var $isExtensible = require('../internals/object-is-extensible'); // `Reflect.isExtensible` method // https://tc39.es/ecma262/#sec-reflect.isextensible $({ target: 'Reflect', stat: true }, { isExtensible: function isExtensible(target) { anObject(target); - return objectIsExtensible ? objectIsExtensible(target) : true; + return $isExtensible(target); } }); diff --git a/packages/core-js/modules/es.weak-map.js b/packages/core-js/modules/es.weak-map.js index 9d7f077518b8..7044313a2cfe 100644 --- a/packages/core-js/modules/es.weak-map.js +++ b/packages/core-js/modules/es.weak-map.js @@ -6,12 +6,11 @@ var InternalMetadataModule = require('../internals/internal-metadata'); var collection = require('../internals/collection'); var collectionWeak = require('../internals/collection-weak'); var isObject = require('../internals/is-object'); +var isExtensible = require('../internals/object-is-extensible'); var enforceIternalState = require('../internals/internal-state').enforce; var NATIVE_WEAK_MAP = require('../internals/native-weak-map'); var IS_IE11 = !global.ActiveXObject && 'ActiveXObject' in global; -// eslint-disable-next-line es/no-object-isextensible -- safe -var isExtensible = Object.isExtensible; var InternalWeakMap; var wrapper = function (init) { diff --git a/tests/pure/es.map.js b/tests/pure/es.map.js index 8c2870c645eb..9f3baa0c8732 100644 --- a/tests/pure/es.map.js +++ b/tests/pure/es.map.js @@ -55,6 +55,12 @@ QUnit.test('Map', assert => { assert.ok(new Subclass() instanceof Map, 'correct subclassing with native classes #2'); assert.strictEqual(new Subclass().set(1, 2).get(1), 2, 'correct subclassing with native classes #3'); } + + if (typeof ArrayBuffer == 'function') { + const buffer = new ArrayBuffer(8); + const map = new Map([[buffer, 8]]); + assert.ok(map.has(buffer), 'works with ArrayBuffer keys'); + } }); QUnit.test('Map#clear', assert => { diff --git a/tests/pure/es.set.js b/tests/pure/es.set.js index 83125ab70657..24fe46079a05 100644 --- a/tests/pure/es.set.js +++ b/tests/pure/es.set.js @@ -18,7 +18,7 @@ QUnit.test('Set', assert => { assert.ok('forEach' in Set.prototype, 'forEach in Set.prototype'); assert.ok('has' in Set.prototype, 'has in Set.prototype'); assert.ok(new Set() instanceof Set, 'new Set instanceof Set'); - const set = new Set(); + let set = new Set(); set.add(1); set.add(2); set.add(3); @@ -74,6 +74,12 @@ QUnit.test('Set', assert => { assert.ok(new Subclass() instanceof Set, 'correct subclassing with native classes #2'); assert.ok(new Subclass().add(2).has(2), 'correct subclassing with native classes #3'); } + + if (typeof ArrayBuffer == 'function') { + const buffer = new ArrayBuffer(8); + set = new Set([buffer]); + assert.ok(set.has(buffer), 'works with ArrayBuffer keys'); + } }); QUnit.test('Set#add', assert => { diff --git a/tests/pure/es.weak-map.js b/tests/pure/es.weak-map.js index c71214c54016..0b404dbefcd3 100644 --- a/tests/pure/es.weak-map.js +++ b/tests/pure/es.weak-map.js @@ -63,6 +63,12 @@ QUnit.test('WeakMap', assert => { object = {}; assert.same(new Subclass().set(object, 2).get(object), 2, 'correct subclassing with native classes #3'); } + + if (typeof ArrayBuffer == 'function') { + const buffer = new ArrayBuffer(8); + const map = new WeakMap([[buffer, 8]]); + assert.ok(map.has(buffer), 'works with ArrayBuffer keys'); + } }); QUnit.test('WeakMap#delete', assert => { diff --git a/tests/pure/es.weak-set.js b/tests/pure/es.weak-set.js index a1f06a22d6f7..ec37846450b9 100644 --- a/tests/pure/es.weak-set.js +++ b/tests/pure/es.weak-set.js @@ -57,6 +57,12 @@ QUnit.test('WeakSet', assert => { object = {}; assert.ok(new Subclass().add(object).has(object), 'correct subclassing with native classes #3'); } + + if (typeof ArrayBuffer == 'function') { + const buffer = new ArrayBuffer(8); + const set = new WeakSet([buffer]); + assert.ok(set.has(buffer), 'works with ArrayBuffer keys'); + } }); QUnit.test('WeakSet#add', assert => { diff --git a/tests/tests/es.map.js b/tests/tests/es.map.js index d59c2a61e56b..3368323ae420 100644 --- a/tests/tests/es.map.js +++ b/tests/tests/es.map.js @@ -56,6 +56,10 @@ QUnit.test('Map', assert => { assert.ok(new Subclass() instanceof Map, 'correct subclassing with native classes #2'); assert.strictEqual(new Subclass().set(1, 2).get(1), 2, 'correct subclassing with native classes #3'); } + + const buffer = new ArrayBuffer(8); + const map = new Map([[buffer, 8]]); + assert.ok(map.has(buffer), 'works with ArrayBuffer keys'); }); QUnit.test('Map#clear', assert => { diff --git a/tests/tests/es.set.js b/tests/tests/es.set.js index cb6a4d1ce699..185dcc55327b 100644 --- a/tests/tests/es.set.js +++ b/tests/tests/es.set.js @@ -19,7 +19,7 @@ QUnit.test('Set', assert => { assert.ok('forEach' in Set.prototype, 'forEach in Set.prototype'); assert.ok('has' in Set.prototype, 'has in Set.prototype'); assert.ok(new Set() instanceof Set, 'new Set instanceof Set'); - const set = new Set(); + let set = new Set(); set.add(1); set.add(2); set.add(3); @@ -77,6 +77,10 @@ QUnit.test('Set', assert => { assert.ok(new Subclass() instanceof Set, 'correct subclassing with native classes #2'); assert.ok(new Subclass().add(2).has(2), 'correct subclassing with native classes #3'); } + + const buffer = new ArrayBuffer(8); + set = new Set([buffer]); + assert.ok(set.has(buffer), 'works with ArrayBuffer keys'); }); QUnit.test('Set#add', assert => { diff --git a/tests/tests/es.weak-map.js b/tests/tests/es.weak-map.js index 2e9d19cda4a0..3902462fce9b 100644 --- a/tests/tests/es.weak-map.js +++ b/tests/tests/es.weak-map.js @@ -65,6 +65,10 @@ QUnit.test('WeakMap', assert => { object = {}; assert.same(new Subclass().set(object, 2).get(object), 2, 'correct subclassing with native classes #3'); } + + const buffer = new ArrayBuffer(8); + const map = new WeakMap([[buffer, 8]]); + assert.ok(map.has(buffer), 'works with ArrayBuffer keys'); }); QUnit.test('WeakMap#delete', assert => { diff --git a/tests/tests/es.weak-set.js b/tests/tests/es.weak-set.js index cf59f048bb55..51103bb0971c 100644 --- a/tests/tests/es.weak-set.js +++ b/tests/tests/es.weak-set.js @@ -59,6 +59,10 @@ QUnit.test('WeakSet', assert => { object = {}; assert.ok(new Subclass().add(object).has(object), 'correct subclassing with native classes #3'); } + + const buffer = new ArrayBuffer(8); + const set = new WeakSet([buffer]); + assert.ok(set.has(buffer), 'works with ArrayBuffer keys'); }); QUnit.test('WeakSet#add', assert => {