diff --git a/CHANGELOG.md b/CHANGELOG.md index be3d72cc151e..436a67e3707c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -41,7 +41,6 @@ - `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 -- Fixed a theoretically possible future conflict of polyfills definitions in the pure version - [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`**. @@ -57,6 +56,8 @@ - Added Samsung Internet 19.0 compat data mapping - Added Quest Browser 24.0 compat data mapping - Fixed the first version in the Chromium-based Edge compat data mapping +- Fixed a theoretically possible future conflict of polyfills definitions in the pure version +- Added pure version of the `Number` constructor, [#1154](https://github.com/zloirock/core-js/issues/1154), [#1155](https://github.com/zloirock/core-js/issues/1155), thanks [@trosos](https://github.com/trosos) ##### [3.26.1 - 2022.11.14](https://github.com/zloirock/core-js/releases/tag/v3.26.1) - Disabled forced replacing of `Array.fromAsync` since it's on Stage 3 diff --git a/README.md b/README.md index 09fe4d35b791..18e31624418e 100644 --- a/README.md +++ b/README.md @@ -1010,7 +1010,7 @@ function parseInt(string: string, radix?: number = 10): number; [*CommonJS entry points:*](#commonjs-api) ``` core-js(-pure)/es|stable|actual|full/number -core-js/es|stable|actual|full/number/constructor +core-js(-pure)/es|stable|actual|full/number/constructor core-js(-pure)/es|stable|actual|full/number/is-finite core-js(-pure)/es|stable|actual|full/number/is-nan core-js(-pure)/es|stable|actual|full/number/is-integer diff --git a/packages/core-js-pure/override/modules/es.number.constructor.js b/packages/core-js-pure/override/modules/es.number.constructor.js deleted file mode 100644 index 8b1a393741c9..000000000000 --- a/packages/core-js-pure/override/modules/es.number.constructor.js +++ /dev/null @@ -1 +0,0 @@ -// empty diff --git a/packages/core-js/es/number/constructor.js b/packages/core-js/es/number/constructor.js index a77a1aa8d6e6..7dbf25e24706 100644 --- a/packages/core-js/es/number/constructor.js +++ b/packages/core-js/es/number/constructor.js @@ -1,3 +1,4 @@ require('../../modules/es.number.constructor'); +var path = require('../../internals/path'); -module.exports = Number; +module.exports = path.Number; diff --git a/packages/core-js/modules/es.number.constructor.js b/packages/core-js/modules/es.number.constructor.js index db491a2057f1..7b54c713572e 100644 --- a/packages/core-js/modules/es.number.constructor.js +++ b/packages/core-js/modules/es.number.constructor.js @@ -1,9 +1,11 @@ 'use strict'; +var $ = require('../internals/export'); +var IS_PURE = require('../internals/is-pure'); var DESCRIPTORS = require('../internals/descriptors'); var global = require('../internals/global'); +var path = require('../internals/path'); var uncurryThis = require('../internals/function-uncurry-this'); var isForced = require('../internals/is-forced'); -var defineBuiltIn = require('../internals/define-built-in'); var hasOwn = require('../internals/has-own-property'); var inheritIfRequired = require('../internals/inherit-if-required'); var isPrototypeOf = require('../internals/object-is-prototype-of'); @@ -18,6 +20,7 @@ var trim = require('../internals/string-trim').trim; var NUMBER = 'Number'; var NativeNumber = global[NUMBER]; +var PureNumberNamespace = path[NUMBER]; var NumberPrototype = NativeNumber.prototype; var TypeError = global.TypeError; var arraySlice = uncurryThis(''.slice); @@ -60,17 +63,26 @@ var toNumber = function (argument) { } return +it; }; +var FORCED = isForced(NUMBER, !NativeNumber(' 0o1') || !NativeNumber('0b1') || NativeNumber('+0x1')); + // `Number` constructor // https://tc39.es/ecma262/#sec-number-constructor -if (isForced(NUMBER, !NativeNumber(' 0o1') || !NativeNumber('0b1') || NativeNumber('+0x1'))) { - var NumberWrapper = function Number(value) { - var n = arguments.length < 1 ? 0 : NativeNumber(toNumeric(value)); - var dummy = this; - // check on 1..constructor(foo) case - return isPrototypeOf(NumberPrototype, dummy) && fails(function () { thisNumberValue(dummy); }) - ? inheritIfRequired(Object(n), dummy, NumberWrapper) : n; - }; - for (var keys = DESCRIPTORS ? getOwnPropertyNames(NativeNumber) : ( +var NumberWrapper = function Number(value) { + var n = arguments.length < 1 ? 0 : NativeNumber(toNumeric(value)); + var dummy = this; + // check on 1..constructor(foo) case + return isPrototypeOf(NumberPrototype, dummy) && fails(function () { thisNumberValue(dummy); }) + ? inheritIfRequired(Object(n), dummy, NumberWrapper) : n; +}; +NumberWrapper.prototype = NumberPrototype; +if (FORCED && !IS_PURE) NumberPrototype.constructor = NumberWrapper; + +$({ global: true, constructor: true, wrap: true, forced: FORCED }, { + Number: NumberWrapper +}); + +var copyStaticProperties = function (target, source) { + for (var keys = DESCRIPTORS ? getOwnPropertyNames(source) : ( // ES3: 'MAX_VALUE,MIN_VALUE,NaN,NEGATIVE_INFINITY,POSITIVE_INFINITY,' + // ES2015 (in case, if modules with ES2015 Number statics required before): @@ -78,11 +90,11 @@ if (isForced(NUMBER, !NativeNumber(' 0o1') || !NativeNumber('0b1') || NativeNumb // ESNext 'fromString,range' ).split(','), j = 0, key; keys.length > j; j++) { - if (hasOwn(NativeNumber, key = keys[j]) && !hasOwn(NumberWrapper, key)) { - defineProperty(NumberWrapper, key, getOwnPropertyDescriptor(NativeNumber, key)); + if (hasOwn(source, key = keys[j]) && !hasOwn(target, key)) { + defineProperty(target, key, getOwnPropertyDescriptor(source, key)); } } - NumberWrapper.prototype = NumberPrototype; - NumberPrototype.constructor = NumberWrapper; - defineBuiltIn(global, NUMBER, NumberWrapper, { constructor: true }); -} +}; + +if (IS_PURE && PureNumberNamespace) copyStaticProperties(path[NUMBER], PureNumberNamespace); +if (FORCED || IS_PURE) copyStaticProperties(path[NUMBER], NativeNumber); diff --git a/tests/unit-pure/es.number.constructor.js b/tests/unit-pure/es.number.constructor.js new file mode 100644 index 000000000000..7520c26c65e8 --- /dev/null +++ b/tests/unit-pure/es.number.constructor.js @@ -0,0 +1,267 @@ +import globalThis from 'core-js-pure/es/global-this'; +import create from 'core-js-pure/es/object/create'; +import Number from 'core-js-pure/es/number'; +import Symbol from 'core-js-pure/es/symbol'; + +const NativeNumber = globalThis.Number; +const whitespaces = ' \t\u000B\f\u00A0\uFEFF\n\r\u2028\u2029\u1680\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200A\u202F\u205F\u3000'; + +function getCheck(assert) { + return function (a, b) { + assert.same(Number(a), b, `Number ${ typeof a } ${ a } -> ${ b }`); + const x = new Number(a); + assert.same(x, Object(x), `new Number ${ typeof a } ${ a } is object`); + assert.same({}.toString.call(x).slice(8, -1), 'Number', `classof new Number ${ typeof a } ${ a } is Number`); + assert.same(x.valueOf(), b, `new Number(${ typeof a } ${ a }).valueOf() -> ${ b }`); + }; +} + +QUnit.test('Number constructor: regression', assert => { + const check = getCheck(assert); + assert.isFunction(Number); + assert.same(Number.prototype.constructor, NativeNumber); + assert.same(1.0.constructor, NativeNumber); + const constants = ['MAX_VALUE', 'MIN_VALUE', 'NaN', 'NEGATIVE_INFINITY', 'POSITIVE_INFINITY']; + for (const constant of constants) { + assert.true(constant in Number, `Number.${ constant }`); + assert.nonEnumerable(Number, constant); + } + assert.same(Number(), 0); + assert.same(new Number().valueOf(), 0); + check(42, 42); + check(42.42, 42.42); + check(new Number(42), 42); + check(new Number(42.42), 42.42); + check('42', 42); + check('42.42', 42.42); + check('0x42', 66); + check('0X42', 66); + check('0xzzz', NaN); + check('0x1g', NaN); + check('+0x1', NaN); + check('-0x1', NaN); + check('+0X1', NaN); + check('-0X1', NaN); + check(new String('42'), 42); + check(new String('42.42'), 42.42); + check(new String('0x42'), 66); + check(null, 0); + check(undefined, NaN); + check(false, 0); + check(true, 1); + check(new Boolean(false), 0); + check(new Boolean(true), 1); + check({}, NaN); + check({ + valueOf: '1.1', + }, NaN); + check({ + valueOf: '1.1', + toString() { + return '2.2'; + }, + }, 2.2); + check({ + valueOf() { + return '1.1'; + }, + }, 1.1); + check({ + valueOf() { + return '1.1'; + }, + toString() { + return '2.2'; + }, + }, 1.1); + check({ + valueOf() { + return '-0x1a2b3c'; + }, + }, NaN); + check({ + toString() { + return '-0x1a2b3c'; + }, + }, NaN); + check({ + valueOf() { + return 42; + }, + }, 42); + check({ + valueOf() { + return '42'; + }, + }, 42); + check({ + valueOf() { + return null; + }, + }, 0); + check({ + toString() { + return 42; + }, + }, 42); + check({ + toString() { + return '42'; + }, + }, 42); + check({ + valueOf() { + return 1; + }, + toString() { + return 2; + }, + }, 1); + check({ + valueOf: 1, + toString() { + return 2; + }, + }, 2); + let number = 1; + assert.same(Number({ + valueOf() { + return ++number; + }, + }), 2, 'Number call valueOf only once #1'); + assert.same(number, 2, 'Number call valueOf only once #2'); + number = 1; + assert.same(Number({ + toString() { + return ++number; + }, + }), 2, 'Number call toString only once #1'); + assert.same(number, 2, 'Number call toString only once #2'); + number = 1; + assert.same(new Number({ + valueOf() { + return ++number; + }, + }).valueOf(), 2, 'new Number call valueOf only once #1'); + assert.same(number, 2, 'new Number call valueOf only once #2'); + number = 1; + assert.same(new Number({ + toString() { + return ++number; + }, + }).valueOf(), 2, 'new Number call toString only once #1'); + assert.same(number, 2, 'new Number call toString only once #2'); + assert.throws(() => Number(create(null)), TypeError, 'Number assert.throws on object w/o valueOf and toString'); + assert.throws(() => Number({ + valueOf: 1, + toString: 2, + }), TypeError, 'Number assert.throws on object then valueOf and toString are not functions'); + assert.throws(() => new Number(create(null)), TypeError, 'new Number assert.throws on object w/o valueOf and toString'); + assert.throws(() => new Number({ + valueOf: 1, + toString: 2, + }), TypeError, 'new Number assert.throws on object then valueOf and toString are not functions'); + + if (typeof Symbol == 'function' && !Symbol.sham) { + assert.throws(() => Number(Symbol()), 'throws on symbol argument'); + assert.throws(() => new Number(Symbol()), 'throws on symbol argument, new'); + } + + number = new Number(42); + assert.same(typeof number.constructor(number), 'number'); + check(`${ whitespaces }42`, 42); + check(`42${ whitespaces }`, 42); + check(`${ whitespaces }42${ whitespaces }`, 42); + check(`${ whitespaces }0x42`, 66); + check(`0x42${ whitespaces }`, 66); + check(`${ whitespaces }0x42${ whitespaces }`, 66); + check(`${ whitespaces }0X42`, 66); + check(`0X42${ whitespaces }`, 66); + check(`${ whitespaces }0X42${ whitespaces }`, 66); +}); + +QUnit.test('Number constructor: binary', assert => { + const check = getCheck(assert); + check('0b1', 1); + check('0B1', 1); + check('0b12', NaN); + check('0b234', NaN); + check('0b1!', NaN); + check('+0b1', NaN); + check('-0b1', NaN); + check(' 0b1', 1); + check('0b1\n', 1); + check('\n 0b1\n ', 1); + check(' 0B1', 1); + check('0B1\n', 1); + check('\n 0B1\n ', 1); + check({ + valueOf() { + return '0b11'; + }, + }, 3); + check({ + toString() { + return '0b111'; + }, + }, 7); + check({ + valueOf() { + return '0b101010'; + }, + }, 42); + check({ + toString() { + return '0b101010'; + }, + }, 42); + check(`${ whitespaces }0b11`, 3); + check(`0b11${ whitespaces }`, 3); + check(`${ whitespaces }0b11${ whitespaces }`, 3); + check(`${ whitespaces }0B11`, 3); + check(`0B11${ whitespaces }`, 3); + check(`${ whitespaces }0B11${ whitespaces }`, 3); +}); + +QUnit.test('Number constructor: octal', assert => { + const check = getCheck(assert); + check('0o7', 7); + check('0O7', 7); + check('0o18', NaN); + check('0o89a', NaN); + check('0o1!', NaN); + check('+0o1', NaN); + check('-0o1', NaN); + check(' 0o1', 1); + check('0o1\n', 1); + check('\n 0o1\n ', 1); + check(' 0O1', 1); + check('0O1\n', 1); + check('\n 0O1\n ', 1); + check({ + valueOf() { + return '0o77'; + }, + }, 63); + check({ + toString() { + return '0o777'; + }, + }, 511); + check({ + valueOf() { + return '0o12345'; + }, + }, 5349); + check({ + toString() { + return '0o12345'; + }, + }, 5349); + check(`${ whitespaces }0o11`, 9); + check(`0o11${ whitespaces }`, 9); + check(`${ whitespaces }0o11${ whitespaces }`, 9); + check(`${ whitespaces }0O11`, 9); + check(`0O11${ whitespaces }`, 9); + check(`${ whitespaces }0O11${ whitespaces }`, 9); +});