support exponentiation operator (#4593)
This commit is contained in:
@@ -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.
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -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,
|
||||||
|
|||||||
58
test/compress/exponentiation.js
Normal file
58
test/compress/exponentiation.js
Normal 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"
|
||||||
|
}
|
||||||
@@ -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) {
|
||||||
|
|||||||
Reference in New Issue
Block a user