From 901d97b00096fb66e991cb620918b4c02af0d101 Mon Sep 17 00:00:00 2001 From: Denis Pushkarev Date: Sun, 26 Dec 2021 02:27:24 +0700 Subject: [PATCH] add a fix of a V8 ~ Chrome 36- `Object.defineProperty` bug https://bugs.chromium.org/p/v8/issues/detail?id=3334 https://github.com/babel/babel/issues/14056 --- CHANGELOG.md | 2 +- packages/core-js-compat/src/data.mjs | 4 ++-- .../internals/numeric-range-iterator.js | 2 +- packages/core-js/internals/object-create.js | 4 ++-- .../internals/object-define-properties.js | 3 ++- .../internals/object-define-property.js | 23 ++++++++++++++++++- .../internals/v8-prototype-define-bug.js | 12 ++++++++++ .../modules/es.object.define-properties.js | 5 ++-- .../modules/es.object.define-property.js | 7 +++--- packages/core-js/modules/es.symbol.js | 2 ++ .../modules/web.dom-exception.constructor.js | 2 +- packages/core-js/modules/web.url.js | 2 +- tests/compat/tests.js | 15 +++++++++--- tests/pure/es.object.define-properties.js | 8 +++++++ tests/pure/es.object.define-property.js | 9 ++++++++ tests/tests/es.object.define-properties.js | 8 +++++++ tests/tests/es.object.define-property.js | 9 ++++++++ 17 files changed, 99 insertions(+), 18 deletions(-) create mode 100644 packages/core-js/internals/v8-prototype-define-bug.js diff --git a/CHANGELOG.md b/CHANGELOG.md index bb94756a6796..9c46694156fb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,6 @@ ## Changelog ##### Unreleased -- Nothing +- Added a fix of [a V8 ~ Chrome 36- `Object.defineProperty` bug](https://bugs.chromium.org/p/v8/issues/detail?id=3334), [Babel issue](https://github.com/babel/babel/issues/14056) ##### 3.20.1 - 2021.12.23 - Fixed the order of calling reactions of already fulfilled / rejected promises in `Promise.prototype.then`, [#1026](https://github.com/zloirock/core-js/issues/1026) diff --git a/packages/core-js-compat/src/data.mjs b/packages/core-js-compat/src/data.mjs index 8f87f6a939ee..ecac2173dbda 100644 --- a/packages/core-js-compat/src/data.mjs +++ b/packages/core-js-compat/src/data.mjs @@ -678,7 +678,7 @@ export const data = { rhino: '1.7.13', }, 'es.object.define-properties': { - chrome: '5', + chrome: '37', firefox: '4', ie: '9', opera: '12', @@ -686,7 +686,7 @@ export const data = { rhino: '1.7.13', }, 'es.object.define-property': { - chrome: '5', + chrome: '37', firefox: '4', ie: '9', opera: '12', diff --git a/packages/core-js/internals/numeric-range-iterator.js b/packages/core-js/internals/numeric-range-iterator.js index 648f45f20831..91702e8d2f90 100644 --- a/packages/core-js/internals/numeric-range-iterator.js +++ b/packages/core-js/internals/numeric-range-iterator.js @@ -3,7 +3,7 @@ var global = require('../internals/global'); var InternalStateModule = require('../internals/internal-state'); var createIteratorConstructor = require('../internals/create-iterator-constructor'); var isObject = require('../internals/is-object'); -var defineProperties = require('../internals/object-define-properties'); +var defineProperties = require('../internals/object-define-properties').f; var DESCRIPTORS = require('../internals/descriptors'); var INCORRECT_RANGE = 'Incorrect Number.range arguments'; diff --git a/packages/core-js/internals/object-create.js b/packages/core-js/internals/object-create.js index 24a27a65d879..076ee21510e7 100644 --- a/packages/core-js/internals/object-create.js +++ b/packages/core-js/internals/object-create.js @@ -1,6 +1,6 @@ /* global ActiveXObject -- old IE, WSH */ var anObject = require('../internals/an-object'); -var defineProperties = require('../internals/object-define-properties'); +var definePropertiesModule = require('../internals/object-define-properties'); var enumBugKeys = require('../internals/enum-bug-keys'); var hiddenKeys = require('../internals/hidden-keys'); var html = require('../internals/html'); @@ -78,5 +78,5 @@ module.exports = Object.create || function create(O, Properties) { // add "__proto__" for Object.getPrototypeOf polyfill result[IE_PROTO] = O; } else result = NullProtoObject(); - return Properties === undefined ? result : defineProperties(result, Properties); + return Properties === undefined ? result : definePropertiesModule.f(result, Properties); }; diff --git a/packages/core-js/internals/object-define-properties.js b/packages/core-js/internals/object-define-properties.js index 8825caa21301..30199d47061d 100644 --- a/packages/core-js/internals/object-define-properties.js +++ b/packages/core-js/internals/object-define-properties.js @@ -1,4 +1,5 @@ var DESCRIPTORS = require('../internals/descriptors'); +var V8_PROTOTYPE_DEFINE_BUG = require('../internals/v8-prototype-define-bug'); var definePropertyModule = require('../internals/object-define-property'); var anObject = require('../internals/an-object'); var toIndexedObject = require('../internals/to-indexed-object'); @@ -7,7 +8,7 @@ var objectKeys = require('../internals/object-keys'); // `Object.defineProperties` method // https://tc39.es/ecma262/#sec-object.defineproperties // eslint-disable-next-line es/no-object-defineproperties -- safe -module.exports = DESCRIPTORS ? Object.defineProperties : function defineProperties(O, Properties) { +exports.f = DESCRIPTORS && !V8_PROTOTYPE_DEFINE_BUG ? Object.defineProperties : function defineProperties(O, Properties) { anObject(O); var props = toIndexedObject(Properties); var keys = objectKeys(Properties); diff --git a/packages/core-js/internals/object-define-property.js b/packages/core-js/internals/object-define-property.js index 25b8428ca7cd..6c21097f93d1 100644 --- a/packages/core-js/internals/object-define-property.js +++ b/packages/core-js/internals/object-define-property.js @@ -1,16 +1,37 @@ var global = require('../internals/global'); var DESCRIPTORS = require('../internals/descriptors'); var IE8_DOM_DEFINE = require('../internals/ie8-dom-define'); +var V8_PROTOTYPE_DEFINE_BUG = require('../internals/v8-prototype-define-bug'); var anObject = require('../internals/an-object'); var toPropertyKey = require('../internals/to-property-key'); var TypeError = global.TypeError; // eslint-disable-next-line es/no-object-defineproperty -- safe var $defineProperty = Object.defineProperty; +// eslint-disable-next-line es/no-object-getownpropertydescriptor -- safe +var $getOwnPropertyDescriptor = Object.getOwnPropertyDescriptor; +var ENUMERABLE = 'enumerable'; +var CONFIGURABLE = 'configurable'; +var WRITABLE = 'writable'; // `Object.defineProperty` method // https://tc39.es/ecma262/#sec-object.defineproperty -exports.f = DESCRIPTORS ? $defineProperty : function defineProperty(O, P, Attributes) { +exports.f = DESCRIPTORS ? V8_PROTOTYPE_DEFINE_BUG ? function defineProperty(O, P, Attributes) { + anObject(O); + P = toPropertyKey(P); + anObject(Attributes); + if (typeof O === 'function' && P === 'prototype' && 'value' in Attributes && WRITABLE in Attributes && !Attributes[WRITABLE]) { + var current = $getOwnPropertyDescriptor(O, P); + if (current && current[WRITABLE]) { + O[P] = Attributes.value; + Attributes = { + configurable: CONFIGURABLE in Attributes ? Attributes[CONFIGURABLE] : current[CONFIGURABLE], + enumerable: ENUMERABLE in Attributes ? Attributes[ENUMERABLE] : current[ENUMERABLE], + writable: false + }; + } + } return $defineProperty(O, P, Attributes); +} : $defineProperty : function defineProperty(O, P, Attributes) { anObject(O); P = toPropertyKey(P); anObject(Attributes); diff --git a/packages/core-js/internals/v8-prototype-define-bug.js b/packages/core-js/internals/v8-prototype-define-bug.js new file mode 100644 index 000000000000..7c57aa99fbbb --- /dev/null +++ b/packages/core-js/internals/v8-prototype-define-bug.js @@ -0,0 +1,12 @@ +var DESCRIPTORS = require('../internals/descriptors'); +var fails = require('../internals/fails'); + +// V8 ~ Chrome 36- +// https://bugs.chromium.org/p/v8/issues/detail?id=3334 +module.exports = DESCRIPTORS && fails(function () { + // eslint-disable-next-line es/no-object-defineproperty -- required for testing + return Object.defineProperty(function () { /* empty */ }, 'prototype', { + value: 42, + writable: false + }).prototype != 42; +}); diff --git a/packages/core-js/modules/es.object.define-properties.js b/packages/core-js/modules/es.object.define-properties.js index ddc0e72ba3f7..f618b5a65188 100644 --- a/packages/core-js/modules/es.object.define-properties.js +++ b/packages/core-js/modules/es.object.define-properties.js @@ -1,9 +1,10 @@ var $ = require('../internals/export'); var DESCRIPTORS = require('../internals/descriptors'); -var defineProperties = require('../internals/object-define-properties'); +var defineProperties = require('../internals/object-define-properties').f; // `Object.defineProperties` method // https://tc39.es/ecma262/#sec-object.defineproperties -$({ target: 'Object', stat: true, forced: !DESCRIPTORS, sham: !DESCRIPTORS }, { +// eslint-disable-next-line es/no-object-defineproperties -- safe +$({ target: 'Object', stat: true, forced: Object.defineProperties !== defineProperties, sham: !DESCRIPTORS }, { defineProperties: defineProperties }); diff --git a/packages/core-js/modules/es.object.define-property.js b/packages/core-js/modules/es.object.define-property.js index 3021982ea67a..33b7d21fabdd 100644 --- a/packages/core-js/modules/es.object.define-property.js +++ b/packages/core-js/modules/es.object.define-property.js @@ -1,9 +1,10 @@ var $ = require('../internals/export'); var DESCRIPTORS = require('../internals/descriptors'); -var objectDefinePropertyModule = require('../internals/object-define-property'); +var defineProperty = require('../internals/object-define-property').f; // `Object.defineProperty` method // https://tc39.es/ecma262/#sec-object.defineproperty -$({ target: 'Object', stat: true, forced: !DESCRIPTORS, sham: !DESCRIPTORS }, { - defineProperty: objectDefinePropertyModule.f +// eslint-disable-next-line es/no-object-defineproperty -- safe +$({ target: 'Object', stat: true, forced: Object.defineProperty !== defineProperty, sham: !DESCRIPTORS }, { + defineProperty: defineProperty }); diff --git a/packages/core-js/modules/es.symbol.js b/packages/core-js/modules/es.symbol.js index 2f0776c659e3..5c2304c1af23 100644 --- a/packages/core-js/modules/es.symbol.js +++ b/packages/core-js/modules/es.symbol.js @@ -28,6 +28,7 @@ var getOwnPropertyNamesExternal = require('../internals/object-get-own-property- var getOwnPropertySymbolsModule = require('../internals/object-get-own-property-symbols'); var getOwnPropertyDescriptorModule = require('../internals/object-get-own-property-descriptor'); var definePropertyModule = require('../internals/object-define-property'); +var definePropertiesModule = require('../internals/object-define-properties'); var propertyIsEnumerableModule = require('../internals/object-property-is-enumerable'); var arraySlice = require('../internals/array-slice'); var redefine = require('../internals/redefine'); @@ -194,6 +195,7 @@ if (!NATIVE_SYMBOL) { propertyIsEnumerableModule.f = $propertyIsEnumerable; definePropertyModule.f = $defineProperty; + definePropertiesModule.f = $defineProperties; getOwnPropertyDescriptorModule.f = $getOwnPropertyDescriptor; getOwnPropertyNamesModule.f = getOwnPropertyNamesExternal.f = $getOwnPropertyNames; getOwnPropertySymbolsModule.f = $getOwnPropertySymbols; diff --git a/packages/core-js/modules/web.dom-exception.constructor.js b/packages/core-js/modules/web.dom-exception.constructor.js index 9bcf078a6a89..4b5576b07503 100644 --- a/packages/core-js/modules/web.dom-exception.constructor.js +++ b/packages/core-js/modules/web.dom-exception.constructor.js @@ -6,7 +6,7 @@ var fails = require('../internals/fails'); var create = require('../internals/object-create'); var createPropertyDescriptor = require('../internals/create-property-descriptor'); var defineProperty = require('../internals/object-define-property').f; -var defineProperties = require('../internals/object-define-properties'); +var defineProperties = require('../internals/object-define-properties').f; var redefine = require('../internals/redefine'); var hasOwn = require('../internals/has-own-property'); var anInstance = require('../internals/an-instance'); diff --git a/packages/core-js/modules/web.url.js b/packages/core-js/modules/web.url.js index c59aacdcb8cf..b39749460117 100644 --- a/packages/core-js/modules/web.url.js +++ b/packages/core-js/modules/web.url.js @@ -7,7 +7,7 @@ var USE_NATIVE_URL = require('../internals/native-url'); var global = require('../internals/global'); var bind = require('../internals/function-bind-context'); var uncurryThis = require('../internals/function-uncurry-this'); -var defineProperties = require('../internals/object-define-properties'); +var defineProperties = require('../internals/object-define-properties').f; var redefine = require('../internals/redefine'); var anInstance = require('../internals/an-instance'); var hasOwn = require('../internals/has-own-property'); diff --git a/tests/compat/tests.js b/tests/compat/tests.js index fccea43999e6..375af0d54a71 100644 --- a/tests/compat/tests.js +++ b/tests/compat/tests.js @@ -36,7 +36,16 @@ var IS_NODE = Object.prototype.toString.call(process) == '[object process]'; var WEBKIT_STRING_PAD_BUG = /Version\/10(?:\.\d+){1,2}(?: [\w./]+)?(?: Mobile\/\w+)? Safari\//.test(USERAGENT); var DESCRIPTORS_SUPPORT = function () { - return Object.defineProperty({}, 'a', { get: function () { return 7; } }).a == 7; + return Object.defineProperty({}, 'a', { + get: function () { return 7; } + }).a == 7; +}; + +var V8_PROTOTYPE_DEFINE_BUG = function () { + return Object.defineProperty(function () { /* empty */ }, 'prototype', { + value: 42, + writable: false + }).prototype == 42; }; var PROMISES_SUPPORT = function () { @@ -710,10 +719,10 @@ GLOBAL.tests = { return Object.create; }, 'es.object.define-getter': OBJECT_PROTOTYPE_ACCESSORS_SUPPORT, - 'es.object.define-properties': [DESCRIPTORS_SUPPORT, function () { + 'es.object.define-properties': [DESCRIPTORS_SUPPORT, V8_PROTOTYPE_DEFINE_BUG, function () { return Object.defineProperties; }], - 'es.object.define-property': DESCRIPTORS_SUPPORT, + 'es.object.define-property': [DESCRIPTORS_SUPPORT, V8_PROTOTYPE_DEFINE_BUG], 'es.object.define-setter': OBJECT_PROTOTYPE_ACCESSORS_SUPPORT, 'es.object.entries': function () { return Object.entries; diff --git a/tests/pure/es.object.define-properties.js b/tests/pure/es.object.define-properties.js index c0cf727c5ae9..ebcff8cbce38 100644 --- a/tests/pure/es.object.define-properties.js +++ b/tests/pure/es.object.define-properties.js @@ -10,6 +10,14 @@ QUnit.test('Object.defineProperties', assert => { assert.same(result, source); assert.same(result.q, 42); assert.same(result.w, 33); + + if (DESCRIPTORS) { + // eslint-disable-next-line prefer-arrow-callback -- required for testing + assert.same(defineProperties(function () { /* empty */ }, { prototype: { + value: 42, + writable: false, + } }).prototype, 42, 'function prototype with non-writable descriptor'); + } }); QUnit.test('Object.defineProperties.sham flag', assert => { diff --git a/tests/pure/es.object.define-property.js b/tests/pure/es.object.define-property.js index c7f8dfe495f5..13d28f2bea27 100644 --- a/tests/pure/es.object.define-property.js +++ b/tests/pure/es.object.define-property.js @@ -12,6 +12,15 @@ QUnit.test('Object.defineProperty', assert => { }); assert.same(result, source); assert.same(result.q, 42); + + if (DESCRIPTORS) { + // eslint-disable-next-line prefer-arrow-callback -- required for testing + assert.same(defineProperty(function () { /* empty */ }, 'prototype', { + value: 42, + writable: false, + }).prototype, 42, 'function prototype with non-writable descriptor'); + } + assert.throws(() => defineProperty(42, 1, {})); assert.throws(() => defineProperty({}, create(null), {})); assert.throws(() => defineProperty({}, 1, 1)); diff --git a/tests/tests/es.object.define-properties.js b/tests/tests/es.object.define-properties.js index 6b9088b16f42..655c8dd3ed37 100644 --- a/tests/tests/es.object.define-properties.js +++ b/tests/tests/es.object.define-properties.js @@ -12,6 +12,14 @@ QUnit.test('Object.defineProperties', assert => { assert.same(result, source); assert.same(result.q, 42); assert.same(result.w, 33); + + if (DESCRIPTORS) { + // eslint-disable-next-line prefer-arrow-callback -- required for testing + assert.same(defineProperties(function () { /* empty */ }, { prototype: { + value: 42, + writable: false, + } }).prototype, 42, 'function prototype with non-writable descriptor'); + } }); QUnit.test('Object.defineProperties.sham flag', assert => { diff --git a/tests/tests/es.object.define-property.js b/tests/tests/es.object.define-property.js index 2bc628cc182b..a09536c95164 100644 --- a/tests/tests/es.object.define-property.js +++ b/tests/tests/es.object.define-property.js @@ -13,6 +13,15 @@ QUnit.test('Object.defineProperty', assert => { }); assert.same(result, source); assert.same(result.q, 42); + + if (DESCRIPTORS) { + // eslint-disable-next-line prefer-arrow-callback -- required for testing + assert.same(defineProperty(function () { /* empty */ }, 'prototype', { + value: 42, + writable: false, + }).prototype, 42, 'function prototype with non-writable descriptor'); + } + assert.throws(() => defineProperty(42, 1, {})); assert.throws(() => defineProperty({}, create(null), {})); assert.throws(() => defineProperty({}, 1, 1));