support exponentiation operator (#4593)

This commit is contained in:
Alex Lam S.L
2021-01-24 21:48:51 +00:00
committed by GitHub
parent a08d42555a
commit 9d23ba0a22
6 changed files with 94 additions and 12 deletions

View File

@@ -1254,3 +1254,9 @@ To allow for better optimizations, the compiler makes various assumptions:
}()) => b)()); }()) => b)());
``` ```
UglifyJS may modify the input which in turn may suppress those errors. UglifyJS may modify the input which in turn may suppress those errors.
- Some arithmetic operations with `BigInt` may throw `TypeError`:
```javascript
1n + 1;
// TypeError: can't convert BigInt to number
```
UglifyJS may modify the input which in turn may suppress those errors.

View File

@@ -4168,6 +4168,9 @@ merge(Compressor.prototype, {
case "<=" : result = left <= right; break; case "<=" : result = left <= right; break;
case ">" : result = left > right; break; case ">" : result = left > right; break;
case ">=" : result = left >= right; break; case ">=" : result = left >= right; break;
case "**":
result = Math.pow(left, right);
break;
case "in": case "in":
if (right && typeof right == "object" && HOP(right, left)) { if (right && typeof right == "object" && HOP(right, left)) {
result = true; result = true;

View File

@@ -684,6 +684,10 @@ function OutputStream(options) {
PARENS(AST_Unary, function(output) { PARENS(AST_Unary, function(output) {
var p = output.parent(); var p = output.parent();
// (-x) ** y
if (p instanceof AST_Binary) return p.operator == "**" && p.left === this;
// (x++).toString(3)
// (typeof x).length
return (p instanceof AST_Call || p instanceof AST_PropAccess) && p.expression === this; return (p instanceof AST_Call || p instanceof AST_PropAccess) && p.expression === this;
}); });
@@ -722,11 +726,14 @@ function OutputStream(options) {
var p = output.parent(); var p = output.parent();
// await (foo && bar) // await (foo && bar)
if (p instanceof AST_Await) return true; if (p instanceof AST_Await) return true;
// this deals with precedence: 3 * (2 + 1) // this deals with precedence:
// 3 * (2 + 1)
// 3 - (2 - 1)
// (1 ** 2) ** 3
if (p instanceof AST_Binary) { if (p instanceof AST_Binary) {
var po = p.operator, pp = PRECEDENCE[po]; var po = p.operator, pp = PRECEDENCE[po];
var so = this.operator, sp = PRECEDENCE[so]; var so = this.operator, sp = PRECEDENCE[so];
return pp > sp || (pp == sp && this === p.right); return pp > sp || (pp == sp && this === p[po == "**" ? "left" : "right"]);
} }
// (foo && bar)() // (foo && bar)()
if (p instanceof AST_Call) return p.expression === this; if (p instanceof AST_Call) return p.expression === this;
@@ -818,6 +825,8 @@ function OutputStream(options) {
PARENS(AST_Await, function(output) { PARENS(AST_Await, function(output) {
var p = output.parent(); var p = output.parent();
// (await x) ** y
if (p instanceof AST_Binary) return p.operator == "**" && p.left === this;
// new (await foo) // new (await foo)
// (await foo)(bar) // (await foo)(bar)
if (p instanceof AST_Call) return p.expression === this; if (p instanceof AST_Call) return p.expression === this;

View File

@@ -81,6 +81,7 @@ var OPERATORS = makePredicate([
"*", "*",
"/", "/",
"%", "%",
"**",
">>", ">>",
"<<", "<<",
">>>", ">>>",
@@ -630,7 +631,8 @@ var PRECEDENCE = function(a, ret) {
["<", ">", "<=", ">=", "in", "instanceof"], ["<", ">", "<=", ">=", "in", "instanceof"],
[">>", "<<", ">>>"], [">>", "<<", ">>>"],
["+", "-"], ["+", "-"],
["*", "/", "%"] ["*", "/", "%"],
["**"],
], {}); ], {});
var ATOMIC_START_TOKEN = makePredicate("atom bigint num regexp string"); var ATOMIC_START_TOKEN = makePredicate("atom bigint num regexp string");
@@ -1860,7 +1862,7 @@ function parse($TEXT, options) {
var prec = op != null ? PRECEDENCE[op] : null; var prec = op != null ? PRECEDENCE[op] : null;
if (prec != null && prec > min_prec) { if (prec != null && prec > min_prec) {
next(); next();
var right = expr_op(maybe_await(), prec, no_in); var right = expr_op(maybe_await(), op == "**" ? prec - 1 : prec, no_in);
return expr_op(new AST_Binary({ return expr_op(new AST_Binary({
start : left.start, start : left.start,
left : left, left : left,

View File

@@ -0,0 +1,58 @@
precedence_1: {
input: {
console.log(-4 ** 3 ** 2);
}
expect_exact: "console.log((-4)**3**2);"
expect_stdout: "-262144"
node_version: ">=8"
}
precedence_2: {
input: {
console.log(-4 ** (3 ** 2));
}
expect_exact: "console.log((-4)**3**2);"
expect_stdout: "-262144"
node_version: ">=8"
}
precedence_3: {
input: {
console.log(-(4 ** 3) ** 2);
}
expect_exact: "console.log((-(4**3))**2);"
expect_stdout: "4096"
node_version: ">=8"
}
precedence_4: {
input: {
console.log((-4 ** 3) ** 2);
}
expect_exact: "console.log(((-4)**3)**2);"
expect_stdout: "4096"
node_version: ">=8"
}
await: {
input: {
(async a => a * await a ** ++a % a)(2).then(console.log);
}
expect_exact: "(async a=>a*(await a)**++a%a)(2).then(console.log);"
expect_stdout: "1"
node_version: ">=8"
}
evaluate: {
options = {
evaluate: true,
}
input: {
console.log(1 + 2 ** 3 - 4);
}
expect: {
console.log(5);
}
expect_stdout: "5"
node_version: ">=8"
}

View File

@@ -140,6 +140,7 @@ var SUPPORT = function(matrix) {
const_block: "var a; { const a = 0; }", const_block: "var a; { const a = 0; }",
default_value: "[ a = 0 ] = [];", default_value: "[ a = 0 ] = [];",
destructuring: "[] = [];", destructuring: "[] = [];",
exponentiation: "0 ** 0",
let: "let a;", let: "let a;",
rest: "var [...a] = [];", rest: "var [...a] = [];",
rest_object: "var {...a} = {};", rest_object: "var {...a} = {};",
@@ -168,7 +169,7 @@ var VALUES = [
"4", "4",
"5", "5",
"22", "22",
"-0", // 0/-0 !== 0 "(-0)", // 0/-0 !== 0
"23..toString()", "23..toString()",
"24 .toString()", "24 .toString()",
"25. ", "25. ",
@@ -190,7 +191,7 @@ var VALUES = [
"this", "this",
]; ];
if (SUPPORT.bigint) VALUES = VALUES.concat([ if (SUPPORT.bigint) VALUES = VALUES.concat([
"!0o644n", "(!0o644n)",
"([3n][0] > 2)", "([3n][0] > 2)",
"(-42n).toString()", "(-42n).toString()",
"Number(0XDEADn << 16n | 0xbeefn)", "Number(0XDEADn << 16n | 0xbeefn)",
@@ -224,6 +225,7 @@ var BINARY_OPS = [
BINARY_OPS = BINARY_OPS.concat(BINARY_OPS); BINARY_OPS = BINARY_OPS.concat(BINARY_OPS);
BINARY_OPS = BINARY_OPS.concat(BINARY_OPS); BINARY_OPS = BINARY_OPS.concat(BINARY_OPS);
BINARY_OPS = BINARY_OPS.concat(BINARY_OPS); BINARY_OPS = BINARY_OPS.concat(BINARY_OPS);
if (SUPPORT.exponentiation) BINARY_OPS.push("**");
BINARY_OPS = BINARY_OPS.concat(BINARY_OPS); BINARY_OPS = BINARY_OPS.concat(BINARY_OPS);
BINARY_OPS = BINARY_OPS.concat(BINARY_OPS); BINARY_OPS = BINARY_OPS.concat(BINARY_OPS);
BINARY_OPS.push(" in "); BINARY_OPS.push(" in ");
@@ -439,17 +441,19 @@ function createAssignmentPairs(recurmax, stmtDepth, canThrow, nameLenBefore, was
return pairs; return pairs;
function mapShuffled(values, fn) { function mapShuffled(values, fn) {
var ordered = []; var declare_only = [];
var shuffled = []; var side_effects = [];
values.forEach(function(value, index) { values.forEach(function(value, index) {
value = fn(value, index); value = fn(value, index);
if (/]:/.test(value) ? canThrow && rng(10) == 0 : rng(5)) { if (/]:|=/.test(value) ? canThrow && rng(10) == 0 : rng(5)) {
shuffled.splice(rng(shuffled.length + 1), 0, value); declare_only.splice(rng(declare_only.length + 1), 0, value);
} else if (canThrow && rng(5) == 0) {
side_effects.splice(rng(side_effects.length + 1), 0, value);
} else { } else {
ordered.push(value); side_effects.push(value);
} }
}); });
return shuffled.concat(ordered); return declare_only.concat(side_effects);
} }
function convertToRest(names) { function convertToRest(names) {