diff --git a/lib/ast.js b/lib/ast.js index e77ec09316..22942beba3 100644 --- a/lib/ast.js +++ b/lib/ast.js @@ -55,9 +55,10 @@ function DEFNODE(type, props, methods, base) { props.forEach(function(prop) { code.push("this.", prop, "=props.", prop, ";"); }); + code.push("}"); var proto = base && new base; if (proto && proto.initialize || methods && methods.initialize) code.push("this.initialize();"); - code.push("}}"); + code.push("}"); var ctor = new Function(code.join(""))(); if (proto) { ctor.prototype = proto; @@ -1818,6 +1819,9 @@ var AST_This = DEFNODE("This", null, { var AST_NewTarget = DEFNODE("NewTarget", null, { $documentation: "The `new.target` symbol", + initialize: function() { + this.name = "new.target"; + }, _validate: function() { if (this.name !== "new.target") throw new Error('name must be "new.target": ' + this.name); }, diff --git a/lib/compress.js b/lib/compress.js index da756774de..2dc6fabb5d 100644 --- a/lib/compress.js +++ b/lib/compress.js @@ -916,7 +916,7 @@ merge(Compressor.prototype, { tw.find_parent(AST_Scope).may_call_this(); var exp = this.expression; if (exp instanceof AST_LambdaExpression) { - var iife = !exp.name; + var iife = is_iife_single(this); this.args.forEach(function(arg) { arg.walk(tw); 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); } + 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) { return node instanceof AST_SymbolRef && node.definition().undeclared; } @@ -2127,11 +2140,11 @@ merge(Compressor.prototype, { var iife, fn = compressor.self(); if (fn instanceof AST_LambdaExpression && !is_generator(fn) - && !fn.name && !fn.uses_arguments && !fn.pinned() && (iife = compressor.parent()) instanceof AST_Call && iife.expression === fn + && is_iife_single(iife) && all(iife.args, function(arg) { return !(arg instanceof AST_Spread); })) { diff --git a/lib/parse.js b/lib/parse.js index aaaa714802..a7202ccb9d 100644 --- a/lib/parse.js +++ b/lib/parse.js @@ -1737,28 +1737,23 @@ function parse($TEXT, options) { var new_ = function(allow_calls) { var start = S.token; expect_token("operator", "new"); + var call; if (is("punc", ".") && is_token(peek(), "name", "target")) { next(); next(); - return 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); + call = new AST_NewTarget(); } 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({ - start : start, - expression : newexp, - args : args, - end : prev() - }); + call.start = start; + call.end = prev(); return subscripts(call, allow_calls); }; diff --git a/test/compress/functions.js b/test/compress/functions.js index 682b58449b..68ff709b26 100644 --- a/test/compress/functions.js +++ b/test/compress/functions.js @@ -5753,22 +5753,102 @@ issue_4725_2: { node_version: ">=4" } -new_target: { +new_target_1: { input: { - console.log(typeof new function() { - return new.target; - }, function() { + new function f() { + console.log(new.target === f); + }(); + console.log(function() { return new.target; }()); } expect: { - console.log(typeof new function() { - return new.target; - }(), function() { + new function f() { + console.log(new.target === f); + }(); + console.log(function() { 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" } diff --git a/test/ufuzz/index.js b/test/ufuzz/index.js index c5a2e75d1a..bc9e62a071 100644 --- a/test/ufuzz/index.js +++ b/test/ufuzz/index.js @@ -149,6 +149,7 @@ var SUPPORT = function(matrix) { for_of: "for (var a of []);", generator: "function* f(){}", let: "let a;", + new_target: "function f() { new.target; }", nullish: "0 ?? 0", rest: "var [...a] = [];", rest_object: "var {...a} = {};", @@ -1401,13 +1402,16 @@ function _createExpression(recurmax, noComma, stmtDepth, canThrow) { createBlockVariables(recurmax, stmtDepth, canThrow, function(defns) { s.push( instantiate + makeFunction(name) + "(" + createParams(save_async, save_generator) + "){", - strictMode(), - defns() + strictMode() ); + 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;) { 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)); + if (add_new_target) VALUES.splice(VALUES.indexOf("new.target"), 1); }); generator = save_generator; async = save_async;