mirror of
https://github.com/vercel-labs/scriptc.git
synced 2026-10-02 00:25:34 +08:00
Handle scalar unions in static Number conversion (#444)
This commit is contained in:
@@ -3864,13 +3864,13 @@ export function lowerCall(lowerer: Lowerer, expr: ts.CallExpression): IrExpr {
|
||||
}
|
||||
const optionalNumber = lowerOptionalStringNumber(lowerer, arg, loc);
|
||||
if (optionalNumber) return optionalNumber;
|
||||
const nullishNumber = lowerNullishNumber(lowerer, arg, argNode, loc);
|
||||
if (nullishNumber) return nullishNumber;
|
||||
const scalarUnionNumber = lowerScalarUnionNumber(lowerer, arg, argNode, loc);
|
||||
if (scalarUnionNumber) return scalarUnionNumber;
|
||||
lowerer.noLowering(
|
||||
`Number of ${lowerer.fmt(arg.type)} values`,
|
||||
argNode,
|
||||
arg.type.kind === "union"
|
||||
? "numbers, booleans, and strings lower (the full ToNumber string grammar included) — narrow the union first"
|
||||
? "unions of numbers, booleans, strings, null, and undefined lower — narrow other arms first"
|
||||
: undefined,
|
||||
);
|
||||
}
|
||||
@@ -5067,12 +5067,14 @@ function lowerStringMethodCallWithOptionalArgs(
|
||||
return changed ? { ...lowered, args } : lowered;
|
||||
}
|
||||
|
||||
function lowerNullishNumber(lowerer: Lowerer, arg: IrExpr, node: ts.Expression, loc: SrcLoc): IrExpr | null {
|
||||
if (arg.type.kind !== "union" || !lowerer.unions.get(arg.type.unionId)?.arms.every(isUnitType)) return null;
|
||||
const key = `number.nullish:${arg.type.unionId}`;
|
||||
function lowerScalarUnionNumber(lowerer: Lowerer, arg: IrExpr, node: ts.Expression, loc: SrcLoc): IrExpr | null {
|
||||
if (arg.type.kind !== "union" || !lowerer.unions.get(arg.type.unionId)?.arms.every(
|
||||
(arm) => arm.kind === "f64" || arm.kind === "string" || arm.kind === "bool" || isUnitType(arm)
|
||||
)) return null;
|
||||
const key = `number.scalar:${arg.type.unionId}`;
|
||||
let helper = lowerer.widthHelpers.get(key);
|
||||
if (!helper) {
|
||||
helper = `%number.nullish.${lowerer.widthHelpers.size}`;
|
||||
helper = `%number.scalar.${lowerer.widthHelpers.size}`;
|
||||
lowerer.widthHelpers.set(key, helper);
|
||||
const value = varRef("value.0", arg.type, loc);
|
||||
const nan: IrExpr = {
|
||||
|
||||
@@ -7003,6 +7003,12 @@
|
||||
],
|
||||
"diags": []
|
||||
},
|
||||
"<repo>/tests/corpus/3038-number-scalar-unions.ts": {
|
||||
"order": [
|
||||
"<repo>/tests/corpus/3038-number-scalar-unions.ts"
|
||||
],
|
||||
"diags": []
|
||||
},
|
||||
"<repo>/tests/corpus/304-compound-incdec.ts": {
|
||||
"order": [
|
||||
"<repo>/tests/corpus/304-compound-incdec.ts"
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
let calls = 0;
|
||||
|
||||
function candidate(index: number): number | string | boolean | null | undefined {
|
||||
calls++;
|
||||
if (index === 0) return -0;
|
||||
if (index === 1) return 3.5;
|
||||
if (index === 2) return "0x10";
|
||||
if (index === 3) return true;
|
||||
if (index === 4) return false;
|
||||
if (index === 5) return null;
|
||||
if (index === 6) return undefined;
|
||||
return "invalid";
|
||||
}
|
||||
|
||||
for (let index = 0; index < 8; index++) {
|
||||
const value = Number(candidate(index));
|
||||
console.log(index, String(value), String(1 / value), calls);
|
||||
}
|
||||
|
||||
function stringOrBoolean(flag: boolean): string | boolean {
|
||||
return flag ? " 7 " : false;
|
||||
}
|
||||
|
||||
console.log(Number(stringOrBoolean(true)), Number(stringOrBoolean(false)));
|
||||
@@ -47,6 +47,9 @@
|
||||
"test/built-ins/Math/floor/S15.8.2.9_A3.js",
|
||||
"test/built-ins/Number/S9.3_A1_T1.js",
|
||||
"test/built-ins/Number/S9.3_A2_T1.js",
|
||||
"test/built-ins/Number/S9.3_A3_T1.js",
|
||||
"test/built-ins/Number/S9.3_A4.1_T1.js",
|
||||
"test/built-ins/Number/S9.3_A4.2_T1.js",
|
||||
"test/built-ins/Number/isNaN/nan.js",
|
||||
"test/built-ins/RegExp/prototype/test/S15.10.6.3_A1_T16.js",
|
||||
"test/built-ins/String/prototype/charAt/S15.5.4.4_A1_T4.js",
|
||||
@@ -146,6 +149,9 @@
|
||||
"test/built-ins/Math/floor/S15.8.2.9_A3.js": "895ceeb2d4ae505d08e9246888c2e4a4526779d9edac16320ebf629330f56929",
|
||||
"test/built-ins/Number/S9.3_A1_T1.js": "fcbdfbe9aa72ee937fb45993fce18a022bc238f0abfd0bbe033530a6dcd5c0db",
|
||||
"test/built-ins/Number/S9.3_A2_T1.js": "fdcc56650335e1a4c2096afa6299ff8e4fc13453bb5bf1222b6530a565ad3fcb",
|
||||
"test/built-ins/Number/S9.3_A3_T1.js": "00778de5a3600592ddff6213354aefd2e20e019823b631b8f9d5e713df0d9b0a",
|
||||
"test/built-ins/Number/S9.3_A4.1_T1.js": "4f08a7fd71e72dc06e01adf249e7880a13ab1eedea4d0be3c8280aa758a45b66",
|
||||
"test/built-ins/Number/S9.3_A4.2_T1.js": "fdf435ae47263f0372cce90bf25eb8ddae8d0abdc9dce30d4a3b098727a9ce6b",
|
||||
"test/built-ins/Number/isNaN/nan.js": "d513a909b333f40762bc08a9e2d1435d4ca097ed8b6da76497d0201a7264589e",
|
||||
"test/built-ins/RegExp/prototype/test/S15.10.6.3_A1_T16.js": "c96f0402289d55e41a2e0ef322060c5a143b599f456b94dc5ebe0493bc689f42",
|
||||
"test/built-ins/String/prototype/charAt/S15.5.4.4_A1_T4.js": "5b69e2580d17931baf5c9780a1372f863d52077e7e75eae8d8f9f5e09e633a14",
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
// Copyright 2009 the Sputnik authors. All rights reserved.
|
||||
// This code is governed by the BSD license found in the LICENSE file.
|
||||
|
||||
/*---
|
||||
info: |
|
||||
Result of number conversion from boolean value is 1 if the argument is
|
||||
true, else is +0
|
||||
es5id: 9.3_A3_T1
|
||||
description: False and true convert to Number by explicit transformation
|
||||
---*/
|
||||
assert.sameValue(Number(false), +0, 'Number(false) must return +0');
|
||||
assert.sameValue(Number(true), 1, 'Number(true) must return 1');
|
||||
@@ -0,0 +1,16 @@
|
||||
// Copyright 2009 the Sputnik authors. All rights reserved.
|
||||
// This code is governed by the BSD license found in the LICENSE file.
|
||||
|
||||
/*---
|
||||
info: |
|
||||
Result of number conversion from number value equals to the input
|
||||
argument (no conversion)
|
||||
es5id: 9.3_A4.1_T1
|
||||
description: >
|
||||
Some numbers including Number.MAX_VALUE and Number.MIN_VALUE are
|
||||
converted to Number with explicit transformation
|
||||
---*/
|
||||
assert.sameValue(Number(13), 13, 'Number(13) must return 13');
|
||||
assert.sameValue(Number(-13), -13, 'Number(-13) must return -13');
|
||||
assert.sameValue(Number(1.3), 1.3, 'Number(1.3) must return 1.3');
|
||||
assert.sameValue(Number(-1.3), -1.3, 'Number(-1.3) must return -1.3');
|
||||
@@ -0,0 +1,34 @@
|
||||
// Copyright 2009 the Sputnik authors. All rights reserved.
|
||||
// This code is governed by the BSD license found in the LICENSE file.
|
||||
|
||||
/*---
|
||||
info: |
|
||||
Result of number conversion from number value equals to the input
|
||||
argument (no conversion)
|
||||
es5id: 9.3_A4.2_T1
|
||||
description: >
|
||||
Number.NaN, +0, -0, Number.POSITIVE_INFINITY,
|
||||
Number.NEGATIVE_INFINITY, Number.MAX_VALUE and Number.MIN_VALUE
|
||||
convert to Number by explicit transformation
|
||||
---*/
|
||||
|
||||
// CHECK#1
|
||||
assert.sameValue(Number(NaN), NaN, 'Number(true) returns NaN');
|
||||
|
||||
assert.sameValue(Number(+0), +0, 'Number(+0) must return +0');
|
||||
assert.sameValue(Number(-0), -0, 'Number(-0) must return -0');
|
||||
|
||||
assert.sameValue(
|
||||
Number(Number.POSITIVE_INFINITY),
|
||||
Number.POSITIVE_INFINITY,
|
||||
'Number(Number.POSITIVE_INFINITY) returns Number.POSITIVE_INFINITY'
|
||||
);
|
||||
|
||||
assert.sameValue(
|
||||
Number(Number.NEGATIVE_INFINITY),
|
||||
Number.NEGATIVE_INFINITY,
|
||||
'Number(Number.NEGATIVE_INFINITY) returns Number.NEGATIVE_INFINITY'
|
||||
);
|
||||
|
||||
assert.sameValue(Number(Number.MAX_VALUE), Number.MAX_VALUE, 'Number(Number.MAX_VALUE) returns Number.MAX_VALUE');
|
||||
assert.sameValue(Number(Number.MIN_VALUE), Number.MIN_VALUE, 'Number(Number.MIN_VALUE) returns Number.MIN_VALUE');
|
||||
Reference in New Issue
Block a user