fix corner cases with new.target (#4784)
This commit is contained in:
@@ -55,9 +55,10 @@ function DEFNODE(type, props, methods, base) {
|
|||||||
props.forEach(function(prop) {
|
props.forEach(function(prop) {
|
||||||
code.push("this.", prop, "=props.", prop, ";");
|
code.push("this.", prop, "=props.", prop, ";");
|
||||||
});
|
});
|
||||||
|
code.push("}");
|
||||||
var proto = base && new base;
|
var proto = base && new base;
|
||||||
if (proto && proto.initialize || methods && methods.initialize) code.push("this.initialize();");
|
if (proto && proto.initialize || methods && methods.initialize) code.push("this.initialize();");
|
||||||
code.push("}}");
|
code.push("}");
|
||||||
var ctor = new Function(code.join(""))();
|
var ctor = new Function(code.join(""))();
|
||||||
if (proto) {
|
if (proto) {
|
||||||
ctor.prototype = proto;
|
ctor.prototype = proto;
|
||||||
@@ -1818,6 +1819,9 @@ var AST_This = DEFNODE("This", null, {
|
|||||||
|
|
||||||
var AST_NewTarget = DEFNODE("NewTarget", null, {
|
var AST_NewTarget = DEFNODE("NewTarget", null, {
|
||||||
$documentation: "The `new.target` symbol",
|
$documentation: "The `new.target` symbol",
|
||||||
|
initialize: function() {
|
||||||
|
this.name = "new.target";
|
||||||
|
},
|
||||||
_validate: function() {
|
_validate: function() {
|
||||||
if (this.name !== "new.target") throw new Error('name must be "new.target": ' + this.name);
|
if (this.name !== "new.target") throw new Error('name must be "new.target": ' + this.name);
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -916,7 +916,7 @@ merge(Compressor.prototype, {
|
|||||||
tw.find_parent(AST_Scope).may_call_this();
|
tw.find_parent(AST_Scope).may_call_this();
|
||||||
var exp = this.expression;
|
var exp = this.expression;
|
||||||
if (exp instanceof AST_LambdaExpression) {
|
if (exp instanceof AST_LambdaExpression) {
|
||||||
var iife = !exp.name;
|
var iife = is_iife_single(this);
|
||||||
this.args.forEach(function(arg) {
|
this.args.forEach(function(arg) {
|
||||||
arg.walk(tw);
|
arg.walk(tw);
|
||||||
if (arg instanceof AST_Spread) iife = false;
|
if (arg instanceof AST_Spread) iife = false;
|
||||||
@@ -1591,6 +1591,19 @@ merge(Compressor.prototype, {
|
|||||||
return node instanceof AST_LambdaExpression ? !is_arrow(node) : is_iife_call(node);
|
return node instanceof AST_LambdaExpression ? !is_arrow(node) : is_iife_call(node);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function is_iife_single(call) {
|
||||||
|
var exp = call.expression;
|
||||||
|
if (exp.name) return false;
|
||||||
|
if (!(call instanceof AST_New)) return true;
|
||||||
|
var found = false;
|
||||||
|
exp.walk(new TreeWalker(function(node) {
|
||||||
|
if (found) return true;
|
||||||
|
if (node instanceof AST_NewTarget) return found = true;
|
||||||
|
if (node instanceof AST_Scope && node !== exp) return true;
|
||||||
|
}));
|
||||||
|
return !found;
|
||||||
|
}
|
||||||
|
|
||||||
function is_undeclared_ref(node) {
|
function is_undeclared_ref(node) {
|
||||||
return node instanceof AST_SymbolRef && node.definition().undeclared;
|
return node instanceof AST_SymbolRef && node.definition().undeclared;
|
||||||
}
|
}
|
||||||
@@ -2127,11 +2140,11 @@ merge(Compressor.prototype, {
|
|||||||
var iife, fn = compressor.self();
|
var iife, fn = compressor.self();
|
||||||
if (fn instanceof AST_LambdaExpression
|
if (fn instanceof AST_LambdaExpression
|
||||||
&& !is_generator(fn)
|
&& !is_generator(fn)
|
||||||
&& !fn.name
|
|
||||||
&& !fn.uses_arguments
|
&& !fn.uses_arguments
|
||||||
&& !fn.pinned()
|
&& !fn.pinned()
|
||||||
&& (iife = compressor.parent()) instanceof AST_Call
|
&& (iife = compressor.parent()) instanceof AST_Call
|
||||||
&& iife.expression === fn
|
&& iife.expression === fn
|
||||||
|
&& is_iife_single(iife)
|
||||||
&& all(iife.args, function(arg) {
|
&& all(iife.args, function(arg) {
|
||||||
return !(arg instanceof AST_Spread);
|
return !(arg instanceof AST_Spread);
|
||||||
})) {
|
})) {
|
||||||
|
|||||||
29
lib/parse.js
29
lib/parse.js
@@ -1737,28 +1737,23 @@ function parse($TEXT, options) {
|
|||||||
var new_ = function(allow_calls) {
|
var new_ = function(allow_calls) {
|
||||||
var start = S.token;
|
var start = S.token;
|
||||||
expect_token("operator", "new");
|
expect_token("operator", "new");
|
||||||
|
var call;
|
||||||
if (is("punc", ".") && is_token(peek(), "name", "target")) {
|
if (is("punc", ".") && is_token(peek(), "name", "target")) {
|
||||||
next();
|
next();
|
||||||
next();
|
next();
|
||||||
return new AST_NewTarget({
|
call = new AST_NewTarget();
|
||||||
name: "new.target",
|
|
||||||
start: start,
|
|
||||||
end: prev(),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
var newexp = expr_atom(false), args;
|
|
||||||
if (is("punc", "(")) {
|
|
||||||
next();
|
|
||||||
args = expr_list(")", !options.strict);
|
|
||||||
} else {
|
} else {
|
||||||
args = [];
|
var exp = expr_atom(false), args;
|
||||||
|
if (is("punc", "(")) {
|
||||||
|
next();
|
||||||
|
args = expr_list(")", !options.strict);
|
||||||
|
} else {
|
||||||
|
args = [];
|
||||||
|
}
|
||||||
|
call = new AST_New({ expression: exp, args: args });
|
||||||
}
|
}
|
||||||
var call = new AST_New({
|
call.start = start;
|
||||||
start : start,
|
call.end = prev();
|
||||||
expression : newexp,
|
|
||||||
args : args,
|
|
||||||
end : prev()
|
|
||||||
});
|
|
||||||
return subscripts(call, allow_calls);
|
return subscripts(call, allow_calls);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -5753,22 +5753,102 @@ issue_4725_2: {
|
|||||||
node_version: ">=4"
|
node_version: ">=4"
|
||||||
}
|
}
|
||||||
|
|
||||||
new_target: {
|
new_target_1: {
|
||||||
input: {
|
input: {
|
||||||
console.log(typeof new function() {
|
new function f() {
|
||||||
return new.target;
|
console.log(new.target === f);
|
||||||
}, function() {
|
}();
|
||||||
|
console.log(function() {
|
||||||
return new.target;
|
return new.target;
|
||||||
}());
|
}());
|
||||||
}
|
}
|
||||||
expect: {
|
expect: {
|
||||||
console.log(typeof new function() {
|
new function f() {
|
||||||
return new.target;
|
console.log(new.target === f);
|
||||||
}(), function() {
|
}();
|
||||||
|
console.log(function() {
|
||||||
return new.target;
|
return new.target;
|
||||||
}());
|
}());
|
||||||
}
|
}
|
||||||
expect_stdout: "function undefined"
|
expect_stdout: [
|
||||||
|
"true",
|
||||||
|
"undefined",
|
||||||
|
]
|
||||||
|
node_version: ">=6"
|
||||||
|
}
|
||||||
|
|
||||||
|
new_target_2: {
|
||||||
|
input: {
|
||||||
|
new function(a) {
|
||||||
|
if (!new.target)
|
||||||
|
console.log("FAIL");
|
||||||
|
else if (a)
|
||||||
|
console.log("PASS");
|
||||||
|
else
|
||||||
|
new new.target(new.target.length);
|
||||||
|
}();
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
new function(a) {
|
||||||
|
if (!new.target)
|
||||||
|
console.log("FAIL");
|
||||||
|
else if (a)
|
||||||
|
console.log("PASS");
|
||||||
|
else
|
||||||
|
new new.target(new.target.length);
|
||||||
|
}();
|
||||||
|
}
|
||||||
|
expect_stdout: "PASS"
|
||||||
|
node_version: ">=6"
|
||||||
|
}
|
||||||
|
|
||||||
|
new_target_collapse_vars: {
|
||||||
|
options = {
|
||||||
|
collapse_vars: true,
|
||||||
|
unused: true,
|
||||||
|
}
|
||||||
|
input: {
|
||||||
|
new function(a) {
|
||||||
|
if (a)
|
||||||
|
console.log("PASS");
|
||||||
|
else
|
||||||
|
new new.target(new.target.length);
|
||||||
|
}(0);
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
new function(a) {
|
||||||
|
if (a)
|
||||||
|
console.log("PASS");
|
||||||
|
else
|
||||||
|
new new.target(new.target.length);
|
||||||
|
}(0);
|
||||||
|
}
|
||||||
|
expect_stdout: "PASS"
|
||||||
|
node_version: ">=6"
|
||||||
|
}
|
||||||
|
|
||||||
|
new_target_reduce_vars: {
|
||||||
|
options = {
|
||||||
|
evaluate: true,
|
||||||
|
reduce_vars: true,
|
||||||
|
}
|
||||||
|
input: {
|
||||||
|
new function(a) {
|
||||||
|
if (a)
|
||||||
|
console.log("PASS");
|
||||||
|
else
|
||||||
|
new new.target(new.target.length);
|
||||||
|
}(0);
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
new function(a) {
|
||||||
|
if (a)
|
||||||
|
console.log("PASS");
|
||||||
|
else
|
||||||
|
new new.target(new.target.length);
|
||||||
|
}(0);
|
||||||
|
}
|
||||||
|
expect_stdout: "PASS"
|
||||||
node_version: ">=6"
|
node_version: ">=6"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -149,6 +149,7 @@ var SUPPORT = function(matrix) {
|
|||||||
for_of: "for (var a of []);",
|
for_of: "for (var a of []);",
|
||||||
generator: "function* f(){}",
|
generator: "function* f(){}",
|
||||||
let: "let a;",
|
let: "let a;",
|
||||||
|
new_target: "function f() { new.target; }",
|
||||||
nullish: "0 ?? 0",
|
nullish: "0 ?? 0",
|
||||||
rest: "var [...a] = [];",
|
rest: "var [...a] = [];",
|
||||||
rest_object: "var {...a} = {};",
|
rest_object: "var {...a} = {};",
|
||||||
@@ -1401,13 +1402,16 @@ function _createExpression(recurmax, noComma, stmtDepth, canThrow) {
|
|||||||
createBlockVariables(recurmax, stmtDepth, canThrow, function(defns) {
|
createBlockVariables(recurmax, stmtDepth, canThrow, function(defns) {
|
||||||
s.push(
|
s.push(
|
||||||
instantiate + makeFunction(name) + "(" + createParams(save_async, save_generator) + "){",
|
instantiate + makeFunction(name) + "(" + createParams(save_async, save_generator) + "){",
|
||||||
strictMode(),
|
strictMode()
|
||||||
defns()
|
|
||||||
);
|
);
|
||||||
|
var add_new_target = SUPPORT.new_target && VALUES.indexOf("new.target") < 0;
|
||||||
|
if (add_new_target) VALUES.push("new.target");
|
||||||
|
s.push(defns());
|
||||||
if (instantiate) for (var i = rng(4); --i >= 0;) {
|
if (instantiate) for (var i = rng(4); --i >= 0;) {
|
||||||
s.push((in_class ? "if (this) " : "") + createThisAssignment(recurmax, stmtDepth, canThrow));
|
s.push((in_class ? "if (this) " : "") + createThisAssignment(recurmax, stmtDepth, canThrow));
|
||||||
}
|
}
|
||||||
s.push(_createStatements(rng(5) + 1, recurmax, canThrow, CANNOT_BREAK, CANNOT_CONTINUE, CAN_RETURN, stmtDepth));
|
s.push(_createStatements(rng(5) + 1, recurmax, canThrow, CANNOT_BREAK, CANNOT_CONTINUE, CAN_RETURN, stmtDepth));
|
||||||
|
if (add_new_target) VALUES.splice(VALUES.indexOf("new.target"), 1);
|
||||||
});
|
});
|
||||||
generator = save_generator;
|
generator = save_generator;
|
||||||
async = save_async;
|
async = save_async;
|
||||||
|
|||||||
Reference in New Issue
Block a user