Skip to content

Commit

Permalink
Merge pull request #1155 from trosos/issue1154
Browse files Browse the repository at this point in the history
  • Loading branch information
zloirock committed Dec 10, 2022
2 parents 4a50a27 + 91e90bd commit d32deca
Show file tree
Hide file tree
Showing 6 changed files with 300 additions and 20 deletions.
3 changes: 2 additions & 1 deletion CHANGELOG.md
Expand Up @@ -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`**.
Expand All @@ -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
Expand Down
2 changes: 1 addition & 1 deletion README.md
Expand Up @@ -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
Expand Down

This file was deleted.

3 changes: 2 additions & 1 deletion 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;
44 changes: 28 additions & 16 deletions 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');
Expand All @@ -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);
Expand Down Expand Up @@ -60,29 +63,38 @@ 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):
'EPSILON,MAX_SAFE_INTEGER,MIN_SAFE_INTEGER,isFinite,isInteger,isNaN,isSafeInteger,parseFloat,parseInt,' +
// 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);
267 changes: 267 additions & 0 deletions 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);
});

0 comments on commit d32deca

Please sign in to comment.