diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 00000000000..6edcf864136 --- /dev/null +++ b/.gitattributes @@ -0,0 +1 @@ +*.js text eol=lf diff --git a/.gitignore b/.gitignore index 94fceeb2054..6000c6c35a5 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ +/node_modules/ +/npm-debug.log tmp/ -node_modules/ diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index 45cc71779b2..00000000000 --- a/.travis.yml +++ /dev/null @@ -1,6 +0,0 @@ -language: node_js -before_install: "npm install -g npm" -node_js: - - "0.8" - - "0.10" - - "0.11" diff --git a/LICENSE b/LICENSE index dd7706f0cbf..122e8fb97a8 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ UglifyJS is released under the BSD license: -Copyright 2012-2013 (c) Mihai Bazon +Copyright 2012-2019 (c) Mihai Bazon Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions diff --git a/README.md b/README.md index 7d2b6ba6bfc..f61eacf8687 100644 --- a/README.md +++ b/README.md @@ -54,7 +54,7 @@ Once the sources are downloaded, install all the dependencies using NPM: (Optional) Check that everything is installed correctly by running the tests: -> ./test/run-tests.js +> npm test (Optional) Package for the browser. The UglifyJS2 provides a quick way to build itself for the browser using followig command: diff --git a/bin/unuglifyjs b/bin/unuglifyjs index 668edfca181..293c725c5c1 100755 --- a/bin/unuglifyjs +++ b/bin/unuglifyjs @@ -74,22 +74,22 @@ if (ARGS.colors){ if (ARGS.version || ARGS.V) { //var json = require("../package.json"); - sys.puts(json.name + ' ' + json.version); + console.log(json.name + ' ' + json.version); process.exit(0); } if (ARGS.h || ARGS.help) { - sys.puts(yargs.help()); + console.log(yargs.help()); process.exit(0); } var files = ARGS._.slice(); if (files.length > 1) { - sys.error("WARNING: expected only single input file. Processing file '" + files[0] + "' while the rest is ignored."); + console.error("WARNING: expected only single input file. Processing file '" + files[0] + "' while the rest is ignored."); } if (ARGS.features === true) { - sys.error("ERROR: empty set of features."); + console.error("ERROR: empty set of features."); process.exit(1); } @@ -106,18 +106,16 @@ if (ARGS.extract_feeatures){ var features = ARGS.features.split(","); for (var i = 0; i < features.length; i++) { if (features[i] != "FNAMES" && features[i] != "ASTREL" && features[i] != "FSCOPE") { - sys.error("WARNING: ignoring not supported feature '" + features[i] + "'."); + console.error("WARNING: ignoring not supported feature '" + features[i] + "'."); } } if (ARGS.package) { if (files.length > 0) { - sys.error("WARN: ".yellow + "Ignoring input files since --package was passed"); + console.error("WARN: ".yellow + "Ignoring input files since --package was passed"); } files = UglifyJS.FILES; ARGS.wrap = "UglifyJS"; - ARGS.export_all = true; - } var json_rpc_id = 0; @@ -133,7 +131,7 @@ function processFile(file) { code = fs.readFileSync(file, "utf-8"); } catch (ex) { - sys.error("ERROR:".red + " can't read file '" + file + "'"); + console.error("ERROR:".red + " can't read file '" + file + "'"); return; } @@ -148,9 +146,9 @@ function processFile(file) { }); } catch(ex) { if (ex instanceof UglifyJS.JS_Parse_Error) { - sys.error("Parse error at " + file + ":" + ex.line + "," + ex.col); - sys.error(ex.message); - sys.error(ex.stack); + console.error("Parse error at " + file + ":" + ex.line + "," + ex.col); + console.error(ex.message); + console.error(ex.stack); process.exit(1); } throw ex; @@ -162,11 +160,11 @@ function processFile(file) { var output = UglifyJS.extractFeatures(code, file, ARGS.print_ast, ARGS.features, ARGS.skip_minified); } catch (ex){ if (ex instanceof UglifyJS.Parse_Error){ - sys.error("ERROR: ".red + "cannot parse file '" + file + "': " + ex.message); + console.error("ERROR: ".red + "cannot parse file '" + file + "': " + ex.message); } else if (ex instanceof UglifyJS.Minified_Error){ - //sys.error("WARN: ".yellow + "skipping minified file '" + file + "'"); + //console.error("WARN: ".yellow + "skipping minified file '" + file + "'"); } else { - sys.error("ERROR: ".red + "'" + file + "': " + ex); + console.error("ERROR: ".red + "'" + file + "': " + ex); } return; @@ -189,14 +187,14 @@ function processFile(file) { try { JSON.parse(output); } catch (e) { - sys.error("ERROR: ".red + "output is not valid JSON " + "'" + file + "'"); + console.error("ERROR: ".red + "output is not valid JSON " + "'" + file + "'"); throw e; } if (removeWhitespace(output) == '{"query":[],"assign":[]}') { - sys.error("WARN: ".yellow + " no features extracted '" + file + "'"); + console.error("WARN: ".yellow + " no features extracted '" + file + "'"); } else { - //sys.error("OK: ".green + "'" + file + "'"); + //console.error("OK: ".green + "'" + file + "'"); } if (ARGS.extract_features) { @@ -230,15 +228,15 @@ function processFile(file) { var renamed_js = UglifyJS.replaceMangled(code, file, inferred_names); console.log(renamed_js); } catch (ex) { - sys.error("ERROR: ".red + "failed rename '" + file + "': " + ex); + console.error("ERROR: ".red + "failed rename '" + file + "': " + ex); } } else { - sys.error("ERROR: ".red + "undefined response. Try to update unuglify-js to the latest version by 'npm update -g unuglify-js'"); + console.error("ERROR: ".red + "undefined response. Try to update unuglify-js to the latest version by 'npm update -g unuglify-js'"); } }, function(err) { - sys.error("ERROR: ".red + "connecting to server '" + HOST + ":" + PORT + "' " + err); + console.error("ERROR: ".red + "connecting to server '" + HOST + ":" + PORT + "' " + err); }); return; } @@ -249,19 +247,19 @@ if (ARGS.package){ var output = UglifyJS.OutputStream({}); } catch(ex) { if (ex instanceof UglifyJS.DefaultsError) { - sys.error(ex.msg); - sys.error("Supported options:"); - sys.error(sys.inspect(ex.defs)); + console.error(ex.msg); + console.error("Supported options:"); + console.error(sys.inspect(ex.defs)); process.exit(1); } } - TOPLEVEL = TOPLEVEL.wrap_commonjs(ARGS.wrap, ARGS.export_all); + TOPLEVEL = TOPLEVEL.wrap(ARGS.wrap); TOPLEVEL.print(output); output = output.get(); - sys.print(output); + console.log(output); } function callServer(server, port, methodName, params, success_cb, error_cb) { diff --git a/lib/ast.js b/lib/ast.js index 5aa1be30826..16ea9c805e5 100644 --- a/lib/ast.js +++ b/lib/ast.js @@ -1,7 +1,7 @@ /*********************************************************************** A JavaScript tokenizer / parser / beautifier / compressor. - https://github.com/mishoo/UglifyJS2 + https://github.com/mishoo/UglifyJS -------------------------------- (C) --------------------------------- @@ -44,188 +44,443 @@ "use strict"; function DEFNODE(type, props, methods, base) { - if (arguments.length < 4) base = AST_Node; - if (!props) props = []; - else props = props.split(/\s+/); + if (typeof base === "undefined") base = AST_Node; + props = props ? props.split(/\s+/) : []; var self_props = props; - if (base && base.PROPS) - props = props.concat(base.PROPS); - var code = "return function AST_" + type + "(props){ if (props) { "; - for (var i = props.length; --i >= 0;) { - code += "this." + props[i] + " = props." + props[i] + ";"; - } - var proto = base && new base; - if (proto && proto.initialize || (methods && methods.initialize)) - code += "this.initialize();"; - code += "}}"; - var ctor = new Function(code)(); - if (proto) { - ctor.prototype = proto; + if (base && base.PROPS) props = props.concat(base.PROPS); + var code = [ + "return function AST_", type, "(props){", + // not essential, but speeds up compress by a few percent + "this._bits=0;", + "if(props){", + ]; + props.forEach(function(prop) { + code.push("this.", prop, "=props.", prop, ";"); + }); + code.push("}"); + var proto = Object.create(base && base.prototype); + if (methods.initialize || proto.initialize) code.push("this.initialize();"); + code.push("};"); + var ctor = new Function(code.join(""))(); + ctor.prototype = proto; + ctor.prototype.CTOR = ctor; + ctor.prototype.TYPE = ctor.TYPE = type; + if (base) { ctor.BASE = base; + base.SUBCLASSES.push(ctor); } - if (base) base.SUBCLASSES.push(ctor); - ctor.prototype.CTOR = ctor; - ctor.PROPS = props || null; + ctor.DEFMETHOD = function(name, method) { + this.prototype[name] = method; + }; + ctor.PROPS = props; ctor.SELF_PROPS = self_props; ctor.SUBCLASSES = []; - if (type) { - ctor.prototype.TYPE = ctor.TYPE = type; - } - if (methods) for (i in methods) if (methods.hasOwnProperty(i)) { - if (/^\$/.test(i)) { - ctor[i.substr(1)] = methods[i]; + for (var name in methods) if (HOP(methods, name)) { + if (/^\$/.test(name)) { + ctor[name.substr(1)] = methods[name]; } else { - ctor.prototype[i] = methods[i]; + ctor.DEFMETHOD(name, methods[name]); } } - ctor.DEFMETHOD = function(name, method) { - this.prototype[name] = method; - }; + if (typeof exports !== "undefined") exports["AST_" + type] = ctor; return ctor; -}; +} -var AST_Token = DEFNODE("Token", "type value line col pos endline endcol endpos nlb comments_before file", { +var AST_Token = DEFNODE("Token", "type value line col pos endline endcol endpos nlb comments_before comments_after file raw", { }, null); var AST_Node = DEFNODE("Node", "start end", { - clone: function() { + _clone: function(deep) { + if (deep) { + var self = this.clone(); + return self.transform(new TreeTransformer(function(node) { + if (node !== self) { + return node.clone(true); + } + })); + } return new this.CTOR(this); }, + clone: function(deep) { + return this._clone(deep); + }, $documentation: "Base class of all AST nodes", $propdoc: { start: "[AST_Token] The first token of this node", end: "[AST_Token] The last token of this node" }, - _walk: function(visitor) { - return visitor._visit(this); + equals: function(node) { + return this.TYPE == node.TYPE && this._equals(node); }, walk: function(visitor) { - return this._walk(visitor); // not sure the indirection will be any help - } + visitor.visit(this); + }, + _validate: function() { + if (this.TYPE == "Node") throw new Error("should not instantiate AST_Node"); + }, + validate: function() { + var ctor = this.CTOR; + do { + ctor.prototype._validate.call(this); + } while (ctor = ctor.BASE); + }, + validate_ast: function() { + var marker = {}; + this.walk(new TreeWalker(function(node) { + if (node.validate_visited === marker) { + throw new Error(string_template("cannot reuse AST_{TYPE} from [{start}]", node)); + } + node.validate_visited = marker; + })); + }, }, null); -AST_Node.warn_function = null; -AST_Node.warn = function(txt, props) { - if (AST_Node.warn_function) - AST_Node.warn_function(string_template(txt, props)); +DEF_BITPROPS(AST_Node, [ + // AST_Node + "_optimized", + "_squeezed", + // AST_Call + "call_only", + // AST_Lambda + "collapse_scanning", + // AST_SymbolRef + "defined", + "evaluating", + "falsy", + // AST_SymbolRef + "in_arg", + // AST_Return + "in_bool", + // AST_SymbolRef + "is_undefined", + // AST_LambdaExpression + // AST_LambdaDefinition + "inlined", + // AST_Lambda + "length_read", + // AST_Yield + "nested", + // AST_Lambda + "new", + // AST_Call + // AST_PropAccess + "optional", + // AST_ClassProperty + "private", + // AST_Call + "pure", + // AST_Assign + "redundant", + // AST_Node + "single_use", + // AST_ClassProperty + "static", + // AST_Call + // AST_PropAccess + "terminal", + "truthy", + // AST_Scope + "uses_eval", + // AST_Scope + "uses_with", +]); + +(AST_Node.log_function = function(fn, verbose) { + if (typeof fn != "function") { + AST_Node.info = AST_Node.warn = noop; + return; + } + var printed = Object.create(null); + AST_Node.info = verbose ? function(text, props) { + log("INFO: " + string_template(text, props)); + } : noop; + AST_Node.warn = function(text, props) { + log("WARN: " + string_template(text, props)); + }; + + function log(msg) { + if (printed[msg]) return; + printed[msg] = true; + fn(msg); + } +})(); + +var restore_transforms = []; +AST_Node.enable_validation = function() { + AST_Node.disable_validation(); + (function validate_transform(ctor) { + ctor.SUBCLASSES.forEach(validate_transform); + if (!HOP(ctor.prototype, "transform")) return; + var transform = ctor.prototype.transform; + ctor.prototype.transform = function(tw, in_list) { + var node = transform.call(this, tw, in_list); + if (node instanceof AST_Node) { + node.validate(); + } else if (!(node === null || in_list && List.is_op(node))) { + throw new Error("invalid transformed value: " + node); + } + return node; + }; + restore_transforms.push(function() { + ctor.prototype.transform = transform; + }); + })(this); +}; + +AST_Node.disable_validation = function() { + var restore; + while (restore = restore_transforms.pop()) restore(); }; +function all_equals(k, l) { + return k.length == l.length && all(k, function(m, i) { + return m.equals(l[i]); + }); +} + +function list_equals(s, t) { + return s.length == t.length && all(s, function(u, i) { + return u == t[i]; + }); +} + +function prop_equals(u, v) { + if (u === v) return true; + if (u == null) return v == null; + return u instanceof AST_Node && v instanceof AST_Node && u.equals(v); +} + /* -----[ statements ]----- */ var AST_Statement = DEFNODE("Statement", null, { $documentation: "Base class of all statements", + _validate: function() { + if (this.TYPE == "Statement") throw new Error("should not instantiate AST_Statement"); + }, }); var AST_Debugger = DEFNODE("Debugger", null, { $documentation: "Represents a debugger statement", + _equals: return_true, }, AST_Statement); -var AST_Directive = DEFNODE("Directive", "value scope", { +var AST_Directive = DEFNODE("Directive", "quote value", { $documentation: "Represents a directive, like \"use strict\";", $propdoc: { + quote: "[string?] the original quote character", value: "[string] The value of this directive as a plain string (it's not an AST_String!)", - scope: "[AST_Scope/S] The scope that this directive affects" }, + _equals: function(node) { + return this.value == node.value; + }, + _validate: function() { + if (this.quote != null) { + if (typeof this.quote != "string") throw new Error("quote must be string"); + if (!/^["']$/.test(this.quote)) throw new Error("invalid quote: " + this.quote); + } + if (typeof this.value != "string") throw new Error("value must be string"); + }, +}, AST_Statement); + +var AST_EmptyStatement = DEFNODE("EmptyStatement", null, { + $documentation: "The empty statement (empty block or simply a semicolon)", + _equals: return_true, }, AST_Statement); +function is_statement(node) { + return node instanceof AST_Statement + && !(node instanceof AST_ClassExpression) + && !(node instanceof AST_LambdaExpression); +} + +function validate_expression(value, prop, multiple, allow_spread, allow_hole) { + multiple = multiple ? "contain" : "be"; + if (!(value instanceof AST_Node)) throw new Error(prop + " must " + multiple + " AST_Node"); + if (value instanceof AST_DefaultValue) throw new Error(prop + " cannot " + multiple + " AST_DefaultValue"); + if (value instanceof AST_Destructured) throw new Error(prop + " cannot " + multiple + " AST_Destructured"); + if (value instanceof AST_Hole && !allow_hole) throw new Error(prop + " cannot " + multiple + " AST_Hole"); + if (value instanceof AST_Spread && !allow_spread) throw new Error(prop + " cannot " + multiple + " AST_Spread"); + if (is_statement(value)) throw new Error(prop + " cannot " + multiple + " AST_Statement"); + if (value instanceof AST_SymbolDeclaration) { + throw new Error(prop + " cannot " + multiple + " AST_SymbolDeclaration"); + } +} + +function must_be_expression(node, prop) { + validate_expression(node[prop], prop); +} + var AST_SimpleStatement = DEFNODE("SimpleStatement", "body", { $documentation: "A statement consisting of an expression, i.e. a = 1 + 2", $propdoc: { - body: "[AST_Node] an expression node (should not be instanceof AST_Statement)" + body: "[AST_Node] an expression node (should not be instanceof AST_Statement)", + }, + _equals: function(node) { + return this.body.equals(node.body); }, - _walk: function(visitor) { - return visitor._visit(this, function(){ - this.body._walk(visitor); + walk: function(visitor) { + var node = this; + visitor.visit(node, function() { + node.body.walk(visitor); }); - } + }, + _validate: function() { + must_be_expression(this, "body"); + }, +}, AST_Statement); + +var AST_BlockScope = DEFNODE("BlockScope", "_var_names enclosed functions make_def parent_scope variables", { + $documentation: "Base class for all statements introducing a lexical scope", + $propdoc: { + enclosed: "[SymbolDef*/S] a list of all symbol definitions that are accessed from this scope or any inner scopes", + functions: "[Dictionary/S] like `variables`, but only lists function declarations", + parent_scope: "[AST_Scope?/S] link to the parent scope", + variables: "[Dictionary/S] a map of name ---> SymbolDef for all variables/functions defined in this scope", + }, + clone: function(deep) { + var node = this._clone(deep); + if (this.enclosed) node.enclosed = this.enclosed.slice(); + if (this.functions) node.functions = this.functions.clone(); + if (this.variables) node.variables = this.variables.clone(); + return node; + }, + pinned: function() { + return this.resolve().pinned(); + }, + resolve: function() { + return this.parent_scope.resolve(); + }, + _validate: function() { + if (this.TYPE == "BlockScope") throw new Error("should not instantiate AST_BlockScope"); + if (this.parent_scope == null) return; + if (!(this.parent_scope instanceof AST_BlockScope)) throw new Error("parent_scope must be AST_BlockScope"); + if (!(this.resolve() instanceof AST_Scope)) throw new Error("must be contained within AST_Scope"); + }, }, AST_Statement); function walk_body(node, visitor) { - if (node.body instanceof AST_Statement) { - node.body._walk(visitor); - } - else node.body.forEach(function(stat){ - stat._walk(visitor); + node.body.forEach(function(node) { + node.walk(visitor); }); -}; +} var AST_Block = DEFNODE("Block", "body", { - $documentation: "A body of statements (usually bracketed)", + $documentation: "A body of statements (usually braced)", $propdoc: { body: "[AST_Statement*] an array of statements" }, - _walk: function(visitor) { - return visitor._visit(this, function(){ - walk_body(this, visitor); + _equals: function(node) { + return all_equals(this.body, node.body); + }, + walk: function(visitor) { + var node = this; + visitor.visit(node, function() { + walk_body(node, visitor); + }); + }, + _validate: function() { + if (this.TYPE == "Block") throw new Error("should not instantiate AST_Block"); + this.body.forEach(function(node) { + if (!is_statement(node)) throw new Error("body must contain AST_Statement"); }); - } -}, AST_Statement); + }, +}, AST_BlockScope); var AST_BlockStatement = DEFNODE("BlockStatement", null, { $documentation: "A block statement", }, AST_Block); -var AST_EmptyStatement = DEFNODE("EmptyStatement", null, { - $documentation: "The empty statement (empty block or simply a semicolon)", - _walk: function(visitor) { - return visitor._visit(this); - } -}, AST_Statement); - var AST_StatementWithBody = DEFNODE("StatementWithBody", "body", { $documentation: "Base class for all statements that contain one nested body: `For`, `ForIn`, `Do`, `While`, `With`", $propdoc: { body: "[AST_Statement] the body; this should always be present, even if it's an AST_EmptyStatement" }, - _walk: function(visitor) { - return visitor._visit(this, function(){ - this.body._walk(visitor); - }); - } -}, AST_Statement); + _validate: function() { + if (this.TYPE == "StatementWithBody") throw new Error("should not instantiate AST_StatementWithBody"); + if (!is_statement(this.body)) throw new Error("body must be AST_Statement"); + }, +}, AST_BlockScope); var AST_LabeledStatement = DEFNODE("LabeledStatement", "label", { $documentation: "Statement with a label", $propdoc: { label: "[AST_Label] a label definition" }, - _walk: function(visitor) { - return visitor._visit(this, function(){ - this.label._walk(visitor); - this.body._walk(visitor); + _equals: function(node) { + return this.label.equals(node.label) + && this.body.equals(node.body); + }, + walk: function(visitor) { + var node = this; + visitor.visit(node, function() { + node.label.walk(visitor); + node.body.walk(visitor); }); - } + }, + clone: function(deep) { + var node = this._clone(deep); + if (deep) { + var label = node.label; + var def = this.label; + node.walk(new TreeWalker(function(node) { + if (node instanceof AST_LoopControl) { + if (!node.label || node.label.thedef !== def) return; + node.label.thedef = label; + label.references.push(node); + return true; + } + if (node instanceof AST_Scope) return true; + })); + } + return node; + }, + _validate: function() { + if (!(this.label instanceof AST_Label)) throw new Error("label must be AST_Label"); + }, }, AST_StatementWithBody); var AST_IterationStatement = DEFNODE("IterationStatement", null, { - $documentation: "Internal class. All loops inherit from it." + $documentation: "Internal class. All loops inherit from it.", + _validate: function() { + if (this.TYPE == "IterationStatement") throw new Error("should not instantiate AST_IterationStatement"); + }, }, AST_StatementWithBody); var AST_DWLoop = DEFNODE("DWLoop", "condition", { $documentation: "Base class for do/while statements", $propdoc: { condition: "[AST_Node] the loop condition. Should not be instanceof AST_Statement" - } + }, + _equals: function(node) { + return this.body.equals(node.body) + && this.condition.equals(node.condition); + }, + _validate: function() { + if (this.TYPE == "DWLoop") throw new Error("should not instantiate AST_DWLoop"); + must_be_expression(this, "condition"); + }, }, AST_IterationStatement); var AST_Do = DEFNODE("Do", null, { $documentation: "A `do` statement", - _walk: function(visitor) { - return visitor._visit(this, function(){ - this.body._walk(visitor); - this.condition._walk(visitor); + walk: function(visitor) { + var node = this; + visitor.visit(node, function() { + node.body.walk(visitor); + node.condition.walk(visitor); }); - } + }, }, AST_DWLoop); var AST_While = DEFNODE("While", null, { $documentation: "A `while` statement", - _walk: function(visitor) { - return visitor._visit(this, function(){ - this.condition._walk(visitor); - this.body._walk(visitor); + walk: function(visitor) { + var node = this; + visitor.visit(node, function() { + node.condition.walk(visitor); + node.body.walk(visitor); }); - } + }, }, AST_DWLoop); var AST_For = DEFNODE("For", "init condition step", { @@ -235,163 +490,539 @@ var AST_For = DEFNODE("For", "init condition step", { condition: "[AST_Node?] the `for` termination clause, or null if empty", step: "[AST_Node?] the `for` update clause, or null if empty" }, - _walk: function(visitor) { - return visitor._visit(this, function(){ - if (this.init) this.init._walk(visitor); - if (this.condition) this.condition._walk(visitor); - if (this.step) this.step._walk(visitor); - this.body._walk(visitor); + _equals: function(node) { + return prop_equals(this.init, node.init) + && prop_equals(this.condition, node.condition) + && prop_equals(this.step, node.step) + && this.body.equals(node.body); + }, + walk: function(visitor) { + var node = this; + visitor.visit(node, function() { + if (node.init) node.init.walk(visitor); + if (node.condition) node.condition.walk(visitor); + if (node.step) node.step.walk(visitor); + node.body.walk(visitor); }); - } + }, + _validate: function() { + if (this.init != null) { + if (!(this.init instanceof AST_Node)) throw new Error("init must be AST_Node"); + if (is_statement(this.init) && !(this.init instanceof AST_Definitions)) { + throw new Error("init cannot be AST_Statement"); + } + } + if (this.condition != null) must_be_expression(this, "condition"); + if (this.step != null) must_be_expression(this, "step"); + }, }, AST_IterationStatement); -var AST_ForIn = DEFNODE("ForIn", "init name object", { - $documentation: "A `for ... in` statement", +var AST_ForEnumeration = DEFNODE("ForEnumeration", "init object", { + $documentation: "Base class for enumeration loops, i.e. `for ... in`, `for ... of` & `for await ... of`", $propdoc: { - init: "[AST_Node] the `for/in` initialization code", - name: "[AST_SymbolRef?] the loop variable, only if `init` is AST_Var", - object: "[AST_Node] the object that we're looping through" + init: "[AST_Node] the assignment target during iteration", + object: "[AST_Node] the object to iterate over" }, - _walk: function(visitor) { - return visitor._visit(this, function(){ - this.init._walk(visitor); - this.object._walk(visitor); - this.body._walk(visitor); + _equals: function(node) { + return this.init.equals(node.init) + && this.object.equals(node.object) + && this.body.equals(node.body); + }, + walk: function(visitor) { + var node = this; + visitor.visit(node, function() { + node.init.walk(visitor); + node.object.walk(visitor); + node.body.walk(visitor); }); - } + }, + _validate: function() { + if (this.TYPE == "ForEnumeration") throw new Error("should not instantiate AST_ForEnumeration"); + if (this.init instanceof AST_Definitions) { + if (this.init.definitions.length != 1) throw new Error("init must have single declaration"); + } else { + validate_destructured(this.init, function(node) { + if (!(node instanceof AST_PropAccess || node instanceof AST_SymbolRef)) { + throw new Error("init must be assignable: " + node.TYPE); + } + }); + } + must_be_expression(this, "object"); + }, }, AST_IterationStatement); +var AST_ForIn = DEFNODE("ForIn", null, { + $documentation: "A `for ... in` statement", +}, AST_ForEnumeration); + +var AST_ForOf = DEFNODE("ForOf", null, { + $documentation: "A `for ... of` statement", +}, AST_ForEnumeration); + +var AST_ForAwaitOf = DEFNODE("ForAwaitOf", null, { + $documentation: "A `for await ... of` statement", +}, AST_ForOf); + var AST_With = DEFNODE("With", "expression", { $documentation: "A `with` statement", $propdoc: { expression: "[AST_Node] the `with` expression" }, - _walk: function(visitor) { - return visitor._visit(this, function(){ - this.expression._walk(visitor); - this.body._walk(visitor); + _equals: function(node) { + return this.expression.equals(node.expression) + && this.body.equals(node.body); + }, + walk: function(visitor) { + var node = this; + visitor.visit(node, function() { + node.expression.walk(visitor); + node.body.walk(visitor); }); - } + }, + _validate: function() { + must_be_expression(this, "expression"); + }, }, AST_StatementWithBody); /* -----[ scope and functions ]----- */ -var AST_Scope = DEFNODE("Scope", "directives variables functions uses_with uses_eval parent_scope enclosed cname", { - $documentation: "Base class for all statements introducing a lexical scope", +var AST_Scope = DEFNODE("Scope", "fn_defs may_call_this uses_eval uses_with", { + $documentation: "Base class for all statements introducing a lambda scope", $propdoc: { - directives: "[string*/S] an array of directives declared in this scope", - variables: "[Object/S] a map of name -> SymbolDef for all variables/functions defined in this scope", - functions: "[Object/S] like `variables`, but only lists function declarations", - uses_with: "[boolean/S] tells whether this scope uses the `with` statement", uses_eval: "[boolean/S] tells whether this scope contains a direct call to the global `eval`", - parent_scope: "[AST_Scope?/S] link to the parent scope", - enclosed: "[SymbolDef*/S] a list of all symbol definitions that are accessed from this scope or any subscopes", - cname: "[integer/S] current index for mangling variables (used internally by the mangler)", + uses_with: "[boolean/S] tells whether this scope uses the `with` statement", + }, + pinned: function() { + return this.uses_eval || this.uses_with; + }, + resolve: return_this, + _validate: function() { + if (this.TYPE == "Scope") throw new Error("should not instantiate AST_Scope"); }, }, AST_Block); var AST_Toplevel = DEFNODE("Toplevel", "globals", { $documentation: "The toplevel scope", $propdoc: { - globals: "[Object/S] a map of name -> SymbolDef for all undeclared names", + globals: "[Dictionary/S] a map of name ---> SymbolDef for all undeclared names", }, - wrap_enclose: function(arg_parameter_pairs) { - var self = this; - var args = []; - var parameters = []; - - arg_parameter_pairs.forEach(function(pair) { - var splitAt = pair.lastIndexOf(":"); - - args.push(pair.substr(0, splitAt)); - parameters.push(pair.substr(splitAt + 1)); - }); - - var wrapped_tl = "(function(" + parameters.join(",") + "){ '$ORIG'; })(" + args.join(",") + ")"; - wrapped_tl = parse(wrapped_tl); - wrapped_tl = wrapped_tl.transform(new TreeTransformer(function before(node){ + wrap: function(name) { + var body = this.body; + return parse([ + "(function(exports){'$ORIG';})(typeof ", + name, + "=='undefined'?(", + name, + "={}):", + name, + ");" + ].join(""), { + filename: "wrap=" + JSON.stringify(name) + }).transform(new TreeTransformer(function(node) { if (node instanceof AST_Directive && node.value == "$ORIG") { - return MAP.splice(self.body); + return List.splice(body); } })); - return wrapped_tl; - }, - wrap_commonjs: function(name, export_all) { - var self = this; - var to_export = []; - if (export_all) { - self.figure_out_scope(); - self.walk(new TreeWalker(function(node){ - if (node instanceof AST_SymbolDeclaration && node.definition().global) { - if (!find_if(function(n){ return n.name == node.name }, to_export)) - to_export.push(node); - } - })); - } - var wrapped_tl = "(function(exports, global){ global['" + name + "'] = exports; '$ORIG'; '$EXPORTS'; }({}, (function(){return this}())))"; - wrapped_tl = parse(wrapped_tl); - wrapped_tl = wrapped_tl.transform(new TreeTransformer(function before(node){ - if (node instanceof AST_SimpleStatement) { - node = node.body; - if (node instanceof AST_String) switch (node.getValue()) { - case "$ORIG": - return MAP.splice(self.body); - case "$EXPORTS": - var body = []; - to_export.forEach(function(sym){ - body.push(new AST_SimpleStatement({ - body: new AST_Assign({ - left: new AST_Sub({ - expression: new AST_SymbolRef({ name: "exports" }), - property: new AST_String({ value: sym.name }), - }), - operator: "=", - right: new AST_SymbolRef(sym), - }), - })); - }); - return MAP.splice(body); - } + }, + enclose: function(args_values) { + if (typeof args_values != "string") args_values = ""; + var index = args_values.indexOf(":"); + if (index < 0) index = args_values.length; + var body = this.body; + return parse([ + "(function(", + args_values.slice(0, index), + '){"$ORIG"})(', + args_values.slice(index + 1), + ")" + ].join(""), { + filename: "enclose=" + JSON.stringify(args_values) + }).transform(new TreeTransformer(function(node) { + if (node instanceof AST_Directive && node.value == "$ORIG") { + return List.splice(body); } })); - return wrapped_tl; } }, AST_Scope); -var AST_Lambda = DEFNODE("Lambda", "name argnames uses_arguments", { +var AST_ClassInitBlock = DEFNODE("ClassInitBlock", null, { + $documentation: "Value for `class` static initialization blocks", +}, AST_Scope); + +var AST_Lambda = DEFNODE("Lambda", "argnames length_read rest safe_ids uses_arguments", { $documentation: "Base class for functions", $propdoc: { - name: "[AST_SymbolDeclaration?] the name of this function", - argnames: "[AST_SymbolFunarg*] array of function arguments", - uses_arguments: "[boolean/S] tells whether this function accesses the arguments array" + argnames: "[(AST_DefaultValue|AST_Destructured|AST_SymbolFunarg)*] array of function arguments and/or destructured literals", + length_read: "[boolean/S] whether length property of this function is accessed", + rest: "[(AST_Destructured|AST_SymbolFunarg)?] rest parameter, or null if absent", + uses_arguments: "[boolean|number/S] whether this function accesses the arguments array", }, - _walk: function(visitor) { - return visitor._visit(this, function(){ - if (this.name) this.name._walk(visitor); - this.argnames.forEach(function(arg){ - arg._walk(visitor); + each_argname: function(visit) { + var tw = new TreeWalker(function(node) { + if (node instanceof AST_DefaultValue) { + node.name.walk(tw); + return true; + } + if (node instanceof AST_DestructuredKeyVal) { + node.value.walk(tw); + return true; + } + if (node instanceof AST_SymbolFunarg) visit(node); + }); + this.argnames.forEach(function(argname) { + argname.walk(tw); + }); + if (this.rest) this.rest.walk(tw); + }, + _equals: function(node) { + return prop_equals(this.rest, node.rest) + && prop_equals(this.name, node.name) + && prop_equals(this.value, node.value) + && all_equals(this.argnames, node.argnames) + && all_equals(this.body, node.body); + }, + walk: function(visitor) { + var node = this; + visitor.visit(node, function() { + if (node.name) node.name.walk(visitor); + node.argnames.forEach(function(argname) { + argname.walk(visitor); }); - walk_body(this, visitor); + if (node.rest) node.rest.walk(visitor); + walk_body(node, visitor); }); - } + }, + _validate: function() { + if (this.TYPE == "Lambda") throw new Error("should not instantiate AST_Lambda"); + this.argnames.forEach(function(node) { + validate_destructured(node, function(node) { + if (!(node instanceof AST_SymbolFunarg)) throw new Error("argnames must be AST_SymbolFunarg[]"); + }, true); + }); + if (this.rest != null) validate_destructured(this.rest, function(node) { + if (!(node instanceof AST_SymbolFunarg)) throw new Error("rest must be AST_SymbolFunarg"); + }); + }, }, AST_Scope); var AST_Accessor = DEFNODE("Accessor", null, { - $documentation: "A setter/getter function. The `name` property is always null." + $documentation: "A getter/setter function", + _validate: function() { + if (this.name != null) throw new Error("name must be null"); + }, }, AST_Lambda); -var AST_Function = DEFNODE("Function", null, { - $documentation: "A function expression" +var AST_LambdaExpression = DEFNODE("LambdaExpression", "inlined", { + $documentation: "Base class for function expressions", + $propdoc: { + inlined: "[boolean/S] whether this function has been inlined", + }, + _validate: function() { + if (this.TYPE == "LambdaExpression") throw new Error("should not instantiate AST_LambdaExpression"); + }, }, AST_Lambda); -var AST_Defun = DEFNODE("Defun", null, { - $documentation: "A function definition" +function is_arrow(node) { + return node instanceof AST_Arrow || node instanceof AST_AsyncArrow; +} + +function is_async(node) { + return node instanceof AST_AsyncArrow + || node instanceof AST_AsyncDefun + || node instanceof AST_AsyncFunction + || node instanceof AST_AsyncGeneratorDefun + || node instanceof AST_AsyncGeneratorFunction; +} + +function is_generator(node) { + return node instanceof AST_AsyncGeneratorDefun + || node instanceof AST_AsyncGeneratorFunction + || node instanceof AST_GeneratorDefun + || node instanceof AST_GeneratorFunction; +} + +function walk_lambda(node, tw) { + if (is_arrow(node) && node.value) { + node.value.walk(tw); + } else { + walk_body(node, tw); + } +} + +var AST_Arrow = DEFNODE("Arrow", "value", { + $documentation: "An arrow function expression", + $propdoc: { + value: "[AST_Node?] simple return expression, or null if using function body.", + }, + walk: function(visitor) { + var node = this; + visitor.visit(node, function() { + node.argnames.forEach(function(argname) { + argname.walk(visitor); + }); + if (node.rest) node.rest.walk(visitor); + if (node.value) { + node.value.walk(visitor); + } else { + walk_body(node, visitor); + } + }); + }, + _validate: function() { + if (this.name != null) throw new Error("name must be null"); + if (this.uses_arguments) throw new Error("uses_arguments must be false"); + if (this.value != null) { + must_be_expression(this, "value"); + if (this.body.length) throw new Error("body must be empty if value exists"); + } + }, +}, AST_LambdaExpression); + +var AST_AsyncArrow = DEFNODE("AsyncArrow", "value", { + $documentation: "An asynchronous arrow function expression", + $propdoc: { + value: "[AST_Node?] simple return expression, or null if using function body.", + }, + walk: function(visitor) { + var node = this; + visitor.visit(node, function() { + node.argnames.forEach(function(argname) { + argname.walk(visitor); + }); + if (node.rest) node.rest.walk(visitor); + if (node.value) { + node.value.walk(visitor); + } else { + walk_body(node, visitor); + } + }); + }, + _validate: function() { + if (this.name != null) throw new Error("name must be null"); + if (this.uses_arguments) throw new Error("uses_arguments must be false"); + if (this.value != null) { + must_be_expression(this, "value"); + if (this.body.length) throw new Error("body must be empty if value exists"); + } + }, +}, AST_LambdaExpression); + +var AST_AsyncFunction = DEFNODE("AsyncFunction", "name", { + $documentation: "An asynchronous function expression", + $propdoc: { + name: "[AST_SymbolLambda?] the name of this function, or null if not specified", + }, + _validate: function() { + if (this.name != null) { + if (!(this.name instanceof AST_SymbolLambda)) throw new Error("name must be AST_SymbolLambda"); + } + }, +}, AST_LambdaExpression); + +var AST_AsyncGeneratorFunction = DEFNODE("AsyncGeneratorFunction", "name", { + $documentation: "An asynchronous generator function expression", + $propdoc: { + name: "[AST_SymbolLambda?] the name of this function, or null if not specified", + }, + _validate: function() { + if (this.name != null) { + if (!(this.name instanceof AST_SymbolLambda)) throw new Error("name must be AST_SymbolLambda"); + } + }, +}, AST_LambdaExpression); + +var AST_Function = DEFNODE("Function", "name", { + $documentation: "A function expression", + $propdoc: { + name: "[AST_SymbolLambda?] the name of this function, or null if not specified", + }, + _validate: function() { + if (this.name != null) { + if (!(this.name instanceof AST_SymbolLambda)) throw new Error("name must be AST_SymbolLambda"); + } + }, +}, AST_LambdaExpression); + +var AST_GeneratorFunction = DEFNODE("GeneratorFunction", "name", { + $documentation: "A generator function expression", + $propdoc: { + name: "[AST_SymbolLambda?] the name of this function, or null if not specified", + }, + _validate: function() { + if (this.name != null) { + if (!(this.name instanceof AST_SymbolLambda)) throw new Error("name must be AST_SymbolLambda"); + } + }, +}, AST_LambdaExpression); + +var AST_LambdaDefinition = DEFNODE("LambdaDefinition", "inlined name", { + $documentation: "Base class for function definitions", + $propdoc: { + inlined: "[boolean/S] whether this function has been inlined", + name: "[AST_SymbolDefun] the name of this function", + }, + _validate: function() { + if (this.TYPE == "LambdaDefinition") throw new Error("should not instantiate AST_LambdaDefinition"); + if (!(this.name instanceof AST_SymbolDefun)) throw new Error("name must be AST_SymbolDefun"); + }, }, AST_Lambda); +var AST_AsyncDefun = DEFNODE("AsyncDefun", null, { + $documentation: "An asynchronous function definition", +}, AST_LambdaDefinition); + +var AST_AsyncGeneratorDefun = DEFNODE("AsyncGeneratorDefun", null, { + $documentation: "An asynchronous generator function definition", +}, AST_LambdaDefinition); + +var AST_Defun = DEFNODE("Defun", null, { + $documentation: "A function definition", +}, AST_LambdaDefinition); + +var AST_GeneratorDefun = DEFNODE("GeneratorDefun", null, { + $documentation: "A generator function definition", +}, AST_LambdaDefinition); + +/* -----[ classes ]----- */ + +var AST_Class = DEFNODE("Class", "extends name properties", { + $documentation: "Base class for class literals", + $propdoc: { + extends: "[AST_Node?] the super class, or null if not specified", + properties: "[AST_ClassProperty*] array of class properties", + }, + _equals: function(node) { + return prop_equals(this.name, node.name) + && prop_equals(this.extends, node.extends) + && all_equals(this.properties, node.properties); + }, + resolve: function(def_class) { + return def_class ? this : this.parent_scope.resolve(); + }, + walk: function(visitor) { + var node = this; + visitor.visit(node, function() { + if (node.name) node.name.walk(visitor); + if (node.extends) node.extends.walk(visitor); + node.properties.forEach(function(prop) { + prop.walk(visitor); + }); + }); + }, + _validate: function() { + if (this.TYPE == "Class") throw new Error("should not instantiate AST_Class"); + if (this.extends != null) must_be_expression(this, "extends"); + this.properties.forEach(function(node) { + if (!(node instanceof AST_ClassProperty)) throw new Error("properties must contain AST_ClassProperty"); + }); + }, +}, AST_BlockScope); + +var AST_DefClass = DEFNODE("DefClass", null, { + $documentation: "A class definition", + $propdoc: { + name: "[AST_SymbolDefClass] the name of this class", + }, + _validate: function() { + if (!(this.name instanceof AST_SymbolDefClass)) throw new Error("name must be AST_SymbolDefClass"); + }, +}, AST_Class); + +var AST_ClassExpression = DEFNODE("ClassExpression", null, { + $documentation: "A class expression", + $propdoc: { + name: "[AST_SymbolClass?] the name of this class, or null if not specified", + }, + _validate: function() { + if (this.name != null) { + if (!(this.name instanceof AST_SymbolClass)) throw new Error("name must be AST_SymbolClass"); + } + }, +}, AST_Class); + +var AST_ClassProperty = DEFNODE("ClassProperty", "key private static value", { + $documentation: "Base class for `class` properties", + $propdoc: { + key: "[string|AST_Node?] property name (AST_Node for computed property, null for initialization block)", + private: "[boolean] whether this is a private property", + static: "[boolean] whether this is a static property", + value: "[AST_Node?] property value (AST_Accessor for getters/setters, AST_LambdaExpression for methods, null if not specified for fields)", + }, + _equals: function(node) { + return !this.private == !node.private + && !this.static == !node.static + && prop_equals(this.key, node.key) + && prop_equals(this.value, node.value); + }, + walk: function(visitor) { + var node = this; + visitor.visit(node, function() { + if (node.key instanceof AST_Node) node.key.walk(visitor); + if (node.value) node.value.walk(visitor); + }); + }, + _validate: function() { + if (this.TYPE == "ClassProperty") throw new Error("should not instantiate AST_ClassProperty"); + if (this instanceof AST_ClassInit) { + if (this.key != null) throw new Error("key must be null"); + } else if (typeof this.key != "string") { + if (!(this.key instanceof AST_Node)) throw new Error("key must be string or AST_Node"); + must_be_expression(this, "key"); + } + if(this.value != null) { + if (!(this.value instanceof AST_Node)) throw new Error("value must be AST_Node"); + } + }, +}); + +var AST_ClassField = DEFNODE("ClassField", null, { + $documentation: "A `class` field", + _validate: function() { + if(this.value != null) must_be_expression(this, "value"); + }, +}, AST_ClassProperty); + +var AST_ClassGetter = DEFNODE("ClassGetter", null, { + $documentation: "A `class` getter", + _validate: function() { + if (!(this.value instanceof AST_Accessor)) throw new Error("value must be AST_Accessor"); + }, +}, AST_ClassProperty); + +var AST_ClassSetter = DEFNODE("ClassSetter", null, { + $documentation: "A `class` setter", + _validate: function() { + if (!(this.value instanceof AST_Accessor)) throw new Error("value must be AST_Accessor"); + }, +}, AST_ClassProperty); + +var AST_ClassMethod = DEFNODE("ClassMethod", null, { + $documentation: "A `class` method", + _validate: function() { + if (!(this.value instanceof AST_LambdaExpression)) throw new Error("value must be AST_LambdaExpression"); + if (is_arrow(this.value)) throw new Error("value cannot be AST_Arrow or AST_AsyncArrow"); + if (this.value.name != null) throw new Error("name of class method's lambda must be null"); + }, +}, AST_ClassProperty); + +var AST_ClassInit = DEFNODE("ClassInit", null, { + $documentation: "A `class` static initialization block", + _validate: function() { + if (!this.static) throw new Error("static must be true"); + if (!(this.value instanceof AST_ClassInitBlock)) throw new Error("value must be AST_ClassInitBlock"); + }, + initialize: function() { + this.static = true; + }, +}, AST_ClassProperty); + /* -----[ JUMPS ]----- */ var AST_Jump = DEFNODE("Jump", null, { - $documentation: "Base class for “jumps” (for now that's `return`, `throw`, `break` and `continue`)" + $documentation: "Base class for “jumps” (for now that's `return`, `throw`, `break` and `continue`)", + _validate: function() { + if (this.TYPE == "Jump") throw new Error("should not instantiate AST_Jump"); + }, }, AST_Statement); var AST_Exit = DEFNODE("Exit", "value", { @@ -399,19 +1030,32 @@ var AST_Exit = DEFNODE("Exit", "value", { $propdoc: { value: "[AST_Node?] the value returned or thrown by this statement; could be null for AST_Return" }, - _walk: function(visitor) { - return visitor._visit(this, this.value && function(){ - this.value._walk(visitor); + _equals: function(node) { + return prop_equals(this.value, node.value); + }, + walk: function(visitor) { + var node = this; + visitor.visit(node, function() { + if (node.value) node.value.walk(visitor); }); - } + }, + _validate: function() { + if (this.TYPE == "Exit") throw new Error("should not instantiate AST_Exit"); + }, }, AST_Jump); var AST_Return = DEFNODE("Return", null, { - $documentation: "A `return` statement" + $documentation: "A `return` statement", + _validate: function() { + if (this.value != null) must_be_expression(this, "value"); + }, }, AST_Exit); var AST_Throw = DEFNODE("Throw", null, { - $documentation: "A `throw` statement" + $documentation: "A `throw` statement", + _validate: function() { + must_be_expression(this, "value"); + }, }, AST_Exit); var AST_LoopControl = DEFNODE("LoopControl", "label", { @@ -419,11 +1063,21 @@ var AST_LoopControl = DEFNODE("LoopControl", "label", { $propdoc: { label: "[AST_LabelRef?] the label, or null if none", }, - _walk: function(visitor) { - return visitor._visit(this, this.label && function(){ - this.label._walk(visitor); + _equals: function(node) { + return prop_equals(this.label, node.label); + }, + walk: function(visitor) { + var node = this; + visitor.visit(node, function() { + if (node.label) node.label.walk(visitor); }); - } + }, + _validate: function() { + if (this.TYPE == "LoopControl") throw new Error("should not instantiate AST_LoopControl"); + if (this.label != null) { + if (!(this.label instanceof AST_LabelRef)) throw new Error("label must be AST_LabelRef"); + } + }, }, AST_Jump); var AST_Break = DEFNODE("Break", null, { @@ -442,13 +1096,25 @@ var AST_If = DEFNODE("If", "condition alternative", { condition: "[AST_Node] the `if` condition", alternative: "[AST_Statement?] the `else` part, or null if not present" }, - _walk: function(visitor) { - return visitor._visit(this, function(){ - this.condition._walk(visitor); - this.body._walk(visitor); - if (this.alternative) this.alternative._walk(visitor); + _equals: function(node) { + return this.body.equals(node.body) + && this.condition.equals(node.condition) + && prop_equals(this.alternative, node.alternative); + }, + walk: function(visitor) { + var node = this; + visitor.visit(node, function() { + node.condition.walk(visitor); + node.body.walk(visitor); + if (node.alternative) node.alternative.walk(visitor); }); - } + }, + _validate: function() { + must_be_expression(this, "condition"); + if (this.alternative != null) { + if (!is_statement(this.alternative)) throw new Error("alternative must be AST_Statement"); + } + }, }, AST_StatementWithBody); /* -----[ SWITCH ]----- */ @@ -458,16 +1124,30 @@ var AST_Switch = DEFNODE("Switch", "expression", { $propdoc: { expression: "[AST_Node] the `switch` “discriminant”" }, - _walk: function(visitor) { - return visitor._visit(this, function(){ - this.expression._walk(visitor); - walk_body(this, visitor); + _equals: function(node) { + return this.expression.equals(node.expression) + && all_equals(this.body, node.body); + }, + walk: function(visitor) { + var node = this; + visitor.visit(node, function() { + node.expression.walk(visitor); + walk_body(node, visitor); + }); + }, + _validate: function() { + must_be_expression(this, "expression"); + this.body.forEach(function(node) { + if (!(node instanceof AST_SwitchBranch)) throw new Error("body must be AST_SwitchBranch[]"); }); - } + }, }, AST_Block); var AST_SwitchBranch = DEFNODE("SwitchBranch", null, { $documentation: "Base class for `switch` branches", + _validate: function() { + if (this.TYPE == "SwitchBranch") throw new Error("should not instantiate AST_SwitchBranch"); + }, }, AST_Block); var AST_Default = DEFNODE("Default", null, { @@ -479,12 +1159,20 @@ var AST_Case = DEFNODE("Case", "expression", { $propdoc: { expression: "[AST_Node] the `case` expression" }, - _walk: function(visitor) { - return visitor._visit(this, function(){ - this.expression._walk(visitor); - walk_body(this, visitor); + _equals: function(node) { + return this.expression.equals(node.expression) + && all_equals(this.body, node.body); + }, + walk: function(visitor) { + var node = this; + visitor.visit(node, function() { + node.expression.walk(visitor); + walk_body(node, visitor); }); - } + }, + _validate: function() { + must_be_expression(this, "expression"); + }, }, AST_SwitchBranch); /* -----[ EXCEPTIONS ]----- */ @@ -495,189 +1183,461 @@ var AST_Try = DEFNODE("Try", "bcatch bfinally", { bcatch: "[AST_Catch?] the catch block, or null if not present", bfinally: "[AST_Finally?] the finally block, or null if not present" }, - _walk: function(visitor) { - return visitor._visit(this, function(){ - walk_body(this, visitor); - if (this.bcatch) this.bcatch._walk(visitor); - if (this.bfinally) this.bfinally._walk(visitor); + _equals: function(node) { + return all_equals(this.body, node.body) + && prop_equals(this.bcatch, node.bcatch) + && prop_equals(this.bfinally, node.bfinally); + }, + walk: function(visitor) { + var node = this; + visitor.visit(node, function() { + walk_body(node, visitor); + if (node.bcatch) node.bcatch.walk(visitor); + if (node.bfinally) node.bfinally.walk(visitor); }); - } + }, + _validate: function() { + if (this.bcatch != null) { + if (!(this.bcatch instanceof AST_Catch)) throw new Error("bcatch must be AST_Catch"); + } + if (this.bfinally != null) { + if (!(this.bfinally instanceof AST_Finally)) throw new Error("bfinally must be AST_Finally"); + } + }, }, AST_Block); var AST_Catch = DEFNODE("Catch", "argname", { $documentation: "A `catch` node; only makes sense as part of a `try` statement", $propdoc: { - argname: "[AST_SymbolCatch] symbol for the exception" + argname: "[(AST_Destructured|AST_SymbolCatch)?] symbol for the exception, or null if not present", + }, + _equals: function(node) { + return prop_equals(this.argname, node.argname) + && all_equals(this.body, node.body); }, - _walk: function(visitor) { - return visitor._visit(this, function(){ - this.argname._walk(visitor); - walk_body(this, visitor); + walk: function(visitor) { + var node = this; + visitor.visit(node, function() { + if (node.argname) node.argname.walk(visitor); + walk_body(node, visitor); }); - } + }, + _validate: function() { + if (this.argname != null) validate_destructured(this.argname, function(node) { + if (!(node instanceof AST_SymbolCatch)) throw new Error("argname must be AST_SymbolCatch"); + }); + }, }, AST_Block); var AST_Finally = DEFNODE("Finally", null, { $documentation: "A `finally` node; only makes sense as part of a `try` statement" }, AST_Block); -/* -----[ VAR/CONST ]----- */ +/* -----[ VAR ]----- */ var AST_Definitions = DEFNODE("Definitions", "definitions", { - $documentation: "Base class for `var` or `const` nodes (variable declarations/initializations)", + $documentation: "Base class for `var` nodes (variable declarations/initializations)", + $propdoc: { + definitions: "[AST_VarDef*] array of variable definitions" + }, + _equals: function(node) { + return all_equals(this.definitions, node.definitions); + }, + walk: function(visitor) { + var node = this; + visitor.visit(node, function() { + node.definitions.forEach(function(defn) { + defn.walk(visitor); + }); + }); + }, + _validate: function() { + if (this.TYPE == "Definitions") throw new Error("should not instantiate AST_Definitions"); + if (this.definitions.length < 1) throw new Error("must have at least one definition"); + }, +}, AST_Statement); + +var AST_Const = DEFNODE("Const", null, { + $documentation: "A `const` statement", + _validate: function() { + this.definitions.forEach(function(node) { + if (!(node instanceof AST_VarDef)) throw new Error("definitions must be AST_VarDef[]"); + validate_destructured(node.name, function(node) { + if (!(node instanceof AST_SymbolConst)) throw new Error("name must be AST_SymbolConst"); + }); + }); + }, +}, AST_Definitions); + +var AST_Let = DEFNODE("Let", null, { + $documentation: "A `let` statement", + _validate: function() { + this.definitions.forEach(function(node) { + if (!(node instanceof AST_VarDef)) throw new Error("definitions must be AST_VarDef[]"); + validate_destructured(node.name, function(node) { + if (!(node instanceof AST_SymbolLet)) throw new Error("name must be AST_SymbolLet"); + }); + }); + }, +}, AST_Definitions); + +var AST_Var = DEFNODE("Var", null, { + $documentation: "A `var` statement", + _validate: function() { + this.definitions.forEach(function(node) { + if (!(node instanceof AST_VarDef)) throw new Error("definitions must be AST_VarDef[]"); + validate_destructured(node.name, function(node) { + if (!(node instanceof AST_SymbolVar)) throw new Error("name must be AST_SymbolVar"); + }); + }); + }, +}, AST_Definitions); + +var AST_VarDef = DEFNODE("VarDef", "name value", { + $documentation: "A variable declaration; only appears in a AST_Definitions node", + $propdoc: { + name: "[AST_Destructured|AST_SymbolVar] name of the variable", + value: "[AST_Node?] initializer, or null of there's no initializer", + }, + _equals: function(node) { + return this.name.equals(node.name) + && prop_equals(this.value, node.value); + }, + walk: function(visitor) { + var node = this; + visitor.visit(node, function() { + node.name.walk(visitor); + if (node.value) node.value.walk(visitor); + }); + }, + _validate: function() { + if (this.value != null) must_be_expression(this, "value"); + }, +}); + +/* -----[ OTHER ]----- */ + +var AST_ExportDeclaration = DEFNODE("ExportDeclaration", "body", { + $documentation: "An `export` statement", + $propdoc: { + body: "[AST_DefClass|AST_Definitions|AST_LambdaDefinition] the statement to export", + }, + _equals: function(node) { + return this.body.equals(node.body); + }, + walk: function(visitor) { + var node = this; + visitor.visit(node, function() { + node.body.walk(visitor); + }); + }, + _validate: function() { + if (!(this.body instanceof AST_DefClass + || this.body instanceof AST_Definitions + || this.body instanceof AST_LambdaDefinition)) { + throw new Error("body must be AST_DefClass, AST_Definitions or AST_LambdaDefinition"); + } + }, +}, AST_Statement); + +var AST_ExportDefault = DEFNODE("ExportDefault", "body", { + $documentation: "An `export default` statement", + $propdoc: { + body: "[AST_Node] the default export", + }, + _equals: function(node) { + return this.body.equals(node.body); + }, + walk: function(visitor) { + var node = this; + visitor.visit(node, function() { + node.body.walk(visitor); + }); + }, + _validate: function() { + if (!(this.body instanceof AST_DefClass || this.body instanceof AST_LambdaDefinition)) { + must_be_expression(this, "body"); + } + }, +}, AST_Statement); + +var AST_ExportForeign = DEFNODE("ExportForeign", "aliases keys path", { + $documentation: "An `export ... from '...'` statement", + $propdoc: { + aliases: "[AST_String*] array of aliases to export", + keys: "[AST_String*] array of keys to import", + path: "[AST_String] the path to import module", + }, + _equals: function(node) { + return this.path.equals(node.path) + && all_equals(this.aliases, node.aliases) + && all_equals(this.keys, node.keys); + }, + _validate: function() { + if (this.aliases.length != this.keys.length) { + throw new Error("aliases:key length mismatch: " + this.aliases.length + " != " + this.keys.length); + } + this.aliases.forEach(function(name) { + if (!(name instanceof AST_String)) throw new Error("aliases must contain AST_String"); + }); + this.keys.forEach(function(name) { + if (!(name instanceof AST_String)) throw new Error("keys must contain AST_String"); + }); + if (!(this.path instanceof AST_String)) throw new Error("path must be AST_String"); + }, +}, AST_Statement); + +var AST_ExportReferences = DEFNODE("ExportReferences", "properties", { + $documentation: "An `export { ... }` statement", + $propdoc: { + properties: "[AST_SymbolExport*] array of aliases to export", + }, + _equals: function(node) { + return all_equals(this.properties, node.properties); + }, + walk: function(visitor) { + var node = this; + visitor.visit(node, function() { + node.properties.forEach(function(prop) { + prop.walk(visitor); + }); + }); + }, + _validate: function() { + this.properties.forEach(function(prop) { + if (!(prop instanceof AST_SymbolExport)) throw new Error("properties must contain AST_SymbolExport"); + }); + }, +}, AST_Statement); + +var AST_Import = DEFNODE("Import", "all default path properties", { + $documentation: "An `import` statement", $propdoc: { - definitions: "[AST_VarDef*] array of variable definitions" + all: "[AST_SymbolImport?] the imported namespace, or null if not specified", + default: "[AST_SymbolImport?] the alias for default `export`, or null if not specified", + path: "[AST_String] the path to import module", + properties: "[(AST_SymbolImport*)?] array of aliases, or null if not specified", + }, + _equals: function(node) { + return this.path.equals(node.path) + && prop_equals(this.all, node.all) + && prop_equals(this.default, node.default) + && !this.properties == !node.properties + && (!this.properties || all_equals(this.properties, node.properties)); }, - _walk: function(visitor) { - return visitor._visit(this, function(){ - this.definitions.forEach(function(def){ - def._walk(visitor); + walk: function(visitor) { + var node = this; + visitor.visit(node, function() { + if (node.all) node.all.walk(visitor); + if (node.default) node.default.walk(visitor); + if (node.properties) node.properties.forEach(function(prop) { + prop.walk(visitor); }); }); - } + }, + _validate: function() { + if (this.all != null) { + if (!(this.all instanceof AST_SymbolImport)) throw new Error("all must be AST_SymbolImport"); + if (this.properties != null) throw new Error("cannot import both * and {} in the same statement"); + } + if (this.default != null) { + if (!(this.default instanceof AST_SymbolImport)) throw new Error("default must be AST_SymbolImport"); + if (this.default.key.value !== "") throw new Error("invalid default key: " + this.default.key.value); + } + if (!(this.path instanceof AST_String)) throw new Error("path must be AST_String"); + if (this.properties != null) this.properties.forEach(function(node) { + if (!(node instanceof AST_SymbolImport)) throw new Error("properties must contain AST_SymbolImport"); + }); + }, }, AST_Statement); -var AST_Var = DEFNODE("Var", null, { - $documentation: "A `var` statement" -}, AST_Definitions); - -var AST_Const = DEFNODE("Const", null, { - $documentation: "A `const` statement" -}, AST_Definitions); - -var AST_VarDef = DEFNODE("VarDef", "name value", { - $documentation: "A variable declaration; only appears in a AST_Definitions node", +var AST_DefaultValue = DEFNODE("DefaultValue", "name value", { + $documentation: "A default value declaration", $propdoc: { - name: "[AST_SymbolVar|AST_SymbolConst] name of the variable", - value: "[AST_Node?] initializer, or null of there's no initializer" + name: "[AST_Destructured|AST_SymbolDeclaration] name of the variable", + value: "[AST_Node] value to assign if variable is `undefined`", + }, + _equals: function(node) { + return this.name.equals(node.name) + && this.value.equals(node.value); }, - _walk: function(visitor) { - return visitor._visit(this, function(){ - this.name._walk(visitor); - if (this.value) this.value._walk(visitor); + walk: function(visitor) { + var node = this; + visitor.visit(node, function() { + node.name.walk(visitor); + node.value.walk(visitor); }); - } + }, + _validate: function() { + must_be_expression(this, "value"); + }, }); -/* -----[ OTHER ]----- */ +function must_be_expressions(node, prop, allow_spread, allow_hole) { + node[prop].forEach(function(node) { + validate_expression(node, prop, true, allow_spread, allow_hole); + }); +} -var AST_Call = DEFNODE("Call", "expression args", { +var AST_Call = DEFNODE("Call", "args expression optional pure terminal", { $documentation: "A function call expression", $propdoc: { + args: "[AST_Node*] array of arguments", expression: "[AST_Node] expression to invoke as function", - args: "[AST_Node*] array of arguments" + optional: "[boolean] whether the expression is optional chaining", + pure: "[boolean/S] marker for side-effect-free call expression", + terminal: "[boolean] whether the chain has ended", }, - _walk: function(visitor) { - return visitor._visit(this, function(){ - this.expression._walk(visitor); - this.args.forEach(function(arg){ - arg._walk(visitor); + _equals: function(node) { + return !this.optional == !node.optional + && this.expression.equals(node.expression) + && all_equals(this.args, node.args); + }, + walk: function(visitor) { + var node = this; + visitor.visit(node, function() { + node.expression.walk(visitor); + node.args.forEach(function(arg) { + arg.walk(visitor); }); }); - } + }, + _validate: function() { + must_be_expression(this, "expression"); + must_be_expressions(this, "args", true); + }, }); var AST_New = DEFNODE("New", null, { - $documentation: "An object instantiation. Derives from a function call since it has exactly the same properties" + $documentation: "An object instantiation. Derives from a function call since it has exactly the same properties", + _validate: function() { + if (this.optional) throw new Error("optional must be false"); + if (this.terminal) throw new Error("terminal must be false"); + }, }, AST_Call); -var AST_Seq = DEFNODE("Seq", "car cdr", { - $documentation: "A sequence expression (two comma-separated expressions)", - $propdoc: { - car: "[AST_Node] first element in sequence", - cdr: "[AST_Node] second element in sequence" - }, - $cons: function(x, y) { - var seq = new AST_Seq(x); - seq.car = x; - seq.cdr = y; - return seq; - }, - $from_array: function(array) { - if (array.length == 0) return null; - if (array.length == 1) return array[0].clone(); - var list = null; - for (var i = array.length; --i >= 0;) { - list = AST_Seq.cons(array[i], list); - } - var p = list; - while (p) { - if (p.cdr && !p.cdr.cdr) { - p.cdr = p.cdr.car; - break; - } - p = p.cdr; - } - return list; - }, - to_array: function() { - var p = this, a = []; - while (p) { - a.push(p.car); - if (p.cdr && !(p.cdr instanceof AST_Seq)) { - a.push(p.cdr); - break; - } - p = p.cdr; - } - return a; +var AST_Sequence = DEFNODE("Sequence", "expressions", { + $documentation: "A sequence expression (comma-separated expressions)", + $propdoc: { + expressions: "[AST_Node*] array of expressions (at least two)", }, - add: function(node) { - var p = this; - while (p) { - if (!(p.cdr instanceof AST_Seq)) { - var cell = AST_Seq.cons(p.cdr, node); - return p.cdr = cell; - } - p = p.cdr; - } + _equals: function(node) { + return all_equals(this.expressions, node.expressions); }, - _walk: function(visitor) { - return visitor._visit(this, function(){ - this.car._walk(visitor); - if (this.cdr) this.cdr._walk(visitor); + walk: function(visitor) { + var node = this; + visitor.visit(node, function() { + node.expressions.forEach(function(expr) { + expr.walk(visitor); + }); }); - } + }, + _validate: function() { + if (this.expressions.length < 2) throw new Error("expressions must contain multiple elements"); + must_be_expressions(this, "expressions"); + }, }); -var AST_PropAccess = DEFNODE("PropAccess", "expression property", { +function root_expr(prop) { + while (prop instanceof AST_PropAccess) prop = prop.expression; + return prop; +} + +var AST_PropAccess = DEFNODE("PropAccess", "expression optional property terminal", { $documentation: "Base class for property access expressions, i.e. `a.foo` or `a[\"foo\"]`", $propdoc: { expression: "[AST_Node] the “container” expression", - property: "[AST_Node|string] the property to access. For AST_Dot this is always a plain string, while for AST_Sub it's an arbitrary AST_Node" - } + optional: "[boolean] whether the expression is optional chaining", + property: "[AST_Node|string] the property to access. For AST_Dot this is always a plain string, while for AST_Sub it's an arbitrary AST_Node", + terminal: "[boolean] whether the chain has ended", + }, + _equals: function(node) { + return !this.optional == !node.optional + && prop_equals(this.property, node.property) + && this.expression.equals(node.expression); + }, + get_property: function() { + var p = this.property; + if (p instanceof AST_Constant) return p.value; + if (p instanceof AST_UnaryPrefix && p.operator == "void" && p.expression instanceof AST_Constant) return; + return p; + }, + _validate: function() { + if (this.TYPE == "PropAccess") throw new Error("should not instantiate AST_PropAccess"); + must_be_expression(this, "expression"); + }, }); -var AST_Dot = DEFNODE("Dot", null, { +var AST_Dot = DEFNODE("Dot", "quoted", { $documentation: "A dotted property access expression", - _walk: function(visitor) { - return visitor._visit(this, function(){ - this.expression._walk(visitor); + $propdoc: { + quoted: "[boolean] whether property is transformed from a quoted string", + }, + walk: function(visitor) { + var node = this; + visitor.visit(node, function() { + node.expression.walk(visitor); }); - } + }, + _validate: function() { + if (typeof this.property != "string") throw new Error("property must be string"); + }, }, AST_PropAccess); var AST_Sub = DEFNODE("Sub", null, { $documentation: "Index-style property access, i.e. `a[\"foo\"]`", - _walk: function(visitor) { - return visitor._visit(this, function(){ - this.expression._walk(visitor); - this.property._walk(visitor); + walk: function(visitor) { + var node = this; + visitor.visit(node, function() { + node.expression.walk(visitor); + node.property.walk(visitor); }); - } + }, + _validate: function() { + must_be_expression(this, "property"); + }, }, AST_PropAccess); +var AST_Spread = DEFNODE("Spread", "expression", { + $documentation: "Spread expression in array/object literals or function calls", + $propdoc: { + expression: "[AST_Node] expression to be expanded", + }, + _equals: function(node) { + return this.expression.equals(node.expression); + }, + walk: function(visitor) { + var node = this; + visitor.visit(node, function() { + node.expression.walk(visitor); + }); + }, + _validate: function() { + must_be_expression(this, "expression"); + }, +}); + var AST_Unary = DEFNODE("Unary", "operator expression", { $documentation: "Base class for unary expressions", $propdoc: { operator: "[string] the operator", - expression: "[AST_Node] expression that this unary operator applies to" + expression: "[AST_Node] expression that this unary operator applies to", }, - _walk: function(visitor) { - return visitor._visit(this, function(){ - this.expression._walk(visitor); + _equals: function(node) { + return this.operator == node.operator + && this.expression.equals(node.expression); + }, + walk: function(visitor) { + var node = this; + visitor.visit(node, function() { + node.expression.walk(visitor); }); - } + }, + _validate: function() { + if (this.TYPE == "Unary") throw new Error("should not instantiate AST_Unary"); + if (typeof this.operator != "string") throw new Error("operator must be string"); + must_be_expression(this, "expression"); + }, }); var AST_UnaryPrefix = DEFNODE("UnaryPrefix", null, { @@ -688,19 +1648,30 @@ var AST_UnaryPostfix = DEFNODE("UnaryPostfix", null, { $documentation: "Unary postfix expression, i.e. `i++`" }, AST_Unary); -var AST_Binary = DEFNODE("Binary", "left operator right", { +var AST_Binary = DEFNODE("Binary", "operator left right", { $documentation: "Binary expression, i.e. `a + b`", $propdoc: { left: "[AST_Node] left-hand side expression", operator: "[string] the operator", right: "[AST_Node] right-hand side expression" }, - _walk: function(visitor) { - return visitor._visit(this, function(){ - this.left._walk(visitor); - this.right._walk(visitor); + _equals: function(node) { + return this.operator == node.operator + && this.left.equals(node.left) + && this.right.equals(node.right); + }, + walk: function(visitor) { + var node = this; + visitor.visit(node, function() { + node.left.walk(visitor); + node.right.walk(visitor); }); - } + }, + _validate: function() { + if (!(this instanceof AST_Assign)) must_be_expression(this, "left"); + if (typeof this.operator != "string") throw new Error("operator must be string"); + must_be_expression(this, "right"); + }, }); var AST_Conditional = DEFNODE("Conditional", "condition consequent alternative", { @@ -710,19 +1681,91 @@ var AST_Conditional = DEFNODE("Conditional", "condition consequent alternative", consequent: "[AST_Node]", alternative: "[AST_Node]" }, - _walk: function(visitor) { - return visitor._visit(this, function(){ - this.condition._walk(visitor); - this.consequent._walk(visitor); - this.alternative._walk(visitor); + _equals: function(node) { + return this.condition.equals(node.condition) + && this.consequent.equals(node.consequent) + && this.alternative.equals(node.alternative); + }, + walk: function(visitor) { + var node = this; + visitor.visit(node, function() { + node.condition.walk(visitor); + node.consequent.walk(visitor); + node.alternative.walk(visitor); }); - } + }, + _validate: function() { + must_be_expression(this, "condition"); + must_be_expression(this, "consequent"); + must_be_expression(this, "alternative"); + }, }); var AST_Assign = DEFNODE("Assign", null, { $documentation: "An assignment expression — `a = b + 5`", + _validate: function() { + if (this.operator.indexOf("=") < 0) throw new Error('operator must contain "="'); + if (this.left instanceof AST_Destructured) { + if (this.operator != "=") throw new Error("invalid destructuring operator: " + this.operator); + validate_destructured(this.left, function(node) { + if (!(node instanceof AST_PropAccess || node instanceof AST_SymbolRef)) { + throw new Error("left must be assignable: " + node.TYPE); + } + }); + } else if (!(this.left instanceof AST_Infinity + || this.left instanceof AST_NaN + || this.left instanceof AST_PropAccess && !this.left.optional + || this.left instanceof AST_SymbolRef + || this.left instanceof AST_Undefined)) { + throw new Error("left must be assignable"); + } + }, }, AST_Binary); +var AST_Await = DEFNODE("Await", "expression", { + $documentation: "An await expression", + $propdoc: { + expression: "[AST_Node] expression with Promise to resolve on", + }, + _equals: function(node) { + return this.expression.equals(node.expression); + }, + walk: function(visitor) { + var node = this; + visitor.visit(node, function() { + node.expression.walk(visitor); + }); + }, + _validate: function() { + must_be_expression(this, "expression"); + }, +}); + +var AST_Yield = DEFNODE("Yield", "expression nested", { + $documentation: "A yield expression", + $propdoc: { + expression: "[AST_Node?] return value for iterator, or null if undefined", + nested: "[boolean] whether to iterate over expression as generator", + }, + _equals: function(node) { + return !this.nested == !node.nested + && prop_equals(this.expression, node.expression); + }, + walk: function(visitor) { + var node = this; + visitor.visit(node, function() { + if (node.expression) node.expression.walk(visitor); + }); + }, + _validate: function() { + if (this.expression != null) { + must_be_expression(this, "expression"); + } else if (this.nested) { + throw new Error("yield* must contain expression"); + } + }, +}); + /* -----[ LITERALS ]----- */ var AST_Array = DEFNODE("Array", "elements", { @@ -730,83 +1773,246 @@ var AST_Array = DEFNODE("Array", "elements", { $propdoc: { elements: "[AST_Node*] array of elements" }, - _walk: function(visitor) { - return visitor._visit(this, function(){ - this.elements.forEach(function(el){ - el._walk(visitor); + _equals: function(node) { + return all_equals(this.elements, node.elements); + }, + walk: function(visitor) { + var node = this; + visitor.visit(node, function() { + node.elements.forEach(function(element) { + element.walk(visitor); }); }); + }, + _validate: function() { + must_be_expressions(this, "elements", true, true); + }, +}); + +var AST_Destructured = DEFNODE("Destructured", "rest", { + $documentation: "Base class for destructured literal", + $propdoc: { + rest: "[(AST_Destructured|AST_SymbolDeclaration|AST_SymbolRef)?] rest parameter, or null if absent", + }, + _validate: function() { + if (this.TYPE == "Destructured") throw new Error("should not instantiate AST_Destructured"); + }, +}); + +function validate_destructured(node, check, allow_default) { + if (node instanceof AST_DefaultValue && allow_default) return validate_destructured(node.name, check); + if (node instanceof AST_Destructured) { + if (node.rest != null) validate_destructured(node.rest, check); + if (node instanceof AST_DestructuredArray) return node.elements.forEach(function(node) { + if (!(node instanceof AST_Hole)) validate_destructured(node, check, true); + }); + if (node instanceof AST_DestructuredObject) return node.properties.forEach(function(prop) { + validate_destructured(prop.value, check, true); + }); } + check(node); +} + +var AST_DestructuredArray = DEFNODE("DestructuredArray", "elements", { + $documentation: "A destructured array literal", + $propdoc: { + elements: "[(AST_DefaultValue|AST_Destructured|AST_SymbolDeclaration|AST_SymbolRef)*] array of elements", + }, + _equals: function(node) { + return prop_equals(this.rest, node.rest) + && all_equals(this.elements, node.elements); + }, + walk: function(visitor) { + var node = this; + visitor.visit(node, function() { + node.elements.forEach(function(element) { + element.walk(visitor); + }); + if (node.rest) node.rest.walk(visitor); + }); + }, +}, AST_Destructured); + +var AST_DestructuredKeyVal = DEFNODE("DestructuredKeyVal", "key value", { + $documentation: "A key: value destructured property", + $propdoc: { + key: "[string|AST_Node] property name. For computed property this is an AST_Node.", + value: "[AST_DefaultValue|AST_Destructured|AST_SymbolDeclaration|AST_SymbolRef] property value", + }, + _equals: function(node) { + return prop_equals(this.key, node.key) + && this.value.equals(node.value); + }, + walk: function(visitor) { + var node = this; + visitor.visit(node, function() { + if (node.key instanceof AST_Node) node.key.walk(visitor); + node.value.walk(visitor); + }); + }, + _validate: function() { + if (typeof this.key != "string") { + if (!(this.key instanceof AST_Node)) throw new Error("key must be string or AST_Node"); + must_be_expression(this, "key"); + } + if (!(this.value instanceof AST_Node)) throw new Error("value must be AST_Node"); + }, }); +var AST_DestructuredObject = DEFNODE("DestructuredObject", "properties", { + $documentation: "A destructured object literal", + $propdoc: { + properties: "[AST_DestructuredKeyVal*] array of properties", + }, + _equals: function(node) { + return prop_equals(this.rest, node.rest) + && all_equals(this.properties, node.properties); + }, + walk: function(visitor) { + var node = this; + visitor.visit(node, function() { + node.properties.forEach(function(prop) { + prop.walk(visitor); + }); + if (node.rest) node.rest.walk(visitor); + }); + }, + _validate: function() { + this.properties.forEach(function(node) { + if (!(node instanceof AST_DestructuredKeyVal)) throw new Error("properties must be AST_DestructuredKeyVal[]"); + }); + }, +}, AST_Destructured); + var AST_Object = DEFNODE("Object", "properties", { $documentation: "An object literal", $propdoc: { - properties: "[AST_ObjectProperty*] array of properties" + properties: "[(AST_ObjectProperty|AST_Spread)*] array of properties" }, - _walk: function(visitor) { - return visitor._visit(this, function(){ - this.properties.forEach(function(prop){ - prop._walk(visitor); + _equals: function(node) { + return all_equals(this.properties, node.properties); + }, + walk: function(visitor) { + var node = this; + visitor.visit(node, function() { + node.properties.forEach(function(prop) { + prop.walk(visitor); }); }); - } + }, + _validate: function() { + this.properties.forEach(function(node) { + if (!(node instanceof AST_ObjectProperty || node instanceof AST_Spread)) { + throw new Error("properties must contain AST_ObjectProperty and/or AST_Spread only"); + } + }); + }, }); var AST_ObjectProperty = DEFNODE("ObjectProperty", "key value", { $documentation: "Base class for literal object properties", $propdoc: { - key: "[string] the property name converted to a string for ObjectKeyVal. For setters and getters this is an arbitrary AST_Node.", - value: "[AST_Node] property value. For setters and getters this is an AST_Function." + key: "[string|AST_Node] property name. For computed property this is an AST_Node.", + value: "[AST_Node] property value. For getters and setters this is an AST_Accessor.", + }, + _equals: function(node) { + return prop_equals(this.key, node.key) + && this.value.equals(node.value); }, - _walk: function(visitor) { - return visitor._visit(this, function(){ - this.value._walk(visitor); + walk: function(visitor) { + var node = this; + visitor.visit(node, function() { + if (node.key instanceof AST_Node) node.key.walk(visitor); + node.value.walk(visitor); }); - } + }, + _validate: function() { + if (this.TYPE == "ObjectProperty") throw new Error("should not instantiate AST_ObjectProperty"); + if (typeof this.key != "string") { + if (!(this.key instanceof AST_Node)) throw new Error("key must be string or AST_Node"); + must_be_expression(this, "key"); + } + if (!(this.value instanceof AST_Node)) throw new Error("value must be AST_Node"); + }, }); var AST_ObjectKeyVal = DEFNODE("ObjectKeyVal", null, { $documentation: "A key: value object property", + _validate: function() { + must_be_expression(this, "value"); + }, }, AST_ObjectProperty); +var AST_ObjectMethod = DEFNODE("ObjectMethod", null, { + $documentation: "A key(){} object property", + _validate: function() { + if (!(this.value instanceof AST_LambdaExpression)) throw new Error("value must be AST_LambdaExpression"); + if (is_arrow(this.value)) throw new Error("value cannot be AST_Arrow or AST_AsyncArrow"); + if (this.value.name != null) throw new Error("name of object method's lambda must be null"); + }, +}, AST_ObjectKeyVal); + var AST_ObjectSetter = DEFNODE("ObjectSetter", null, { $documentation: "An object setter property", + _validate: function() { + if (!(this.value instanceof AST_Accessor)) throw new Error("value must be AST_Accessor"); + }, }, AST_ObjectProperty); var AST_ObjectGetter = DEFNODE("ObjectGetter", null, { $documentation: "An object getter property", + _validate: function() { + if (!(this.value instanceof AST_Accessor)) throw new Error("value must be AST_Accessor"); + }, }, AST_ObjectProperty); var AST_Symbol = DEFNODE("Symbol", "scope name thedef", { + $documentation: "Base class for all symbols", $propdoc: { name: "[string] name of this symbol", scope: "[AST_Scope/S] the current scope (not necessarily the definition scope)", thedef: "[SymbolDef/S] the definition of this symbol" }, - $documentation: "Base class for all symbols", + _equals: function(node) { + return this.thedef ? this.thedef === node.thedef : this.name == node.name; + }, + _validate: function() { + if (this.TYPE == "Symbol") throw new Error("should not instantiate AST_Symbol"); + if (typeof this.name != "string") throw new Error("name must be string"); + }, }); -var AST_SymbolAccessor = DEFNODE("SymbolAccessor", null, { - $documentation: "The name of a property accessor (setter/getter function)" +var AST_SymbolDeclaration = DEFNODE("SymbolDeclaration", "init", { + $documentation: "A declaration symbol (symbol in var, function name or argument, symbol in catch)", }, AST_Symbol); -var AST_SymbolDeclaration = DEFNODE("SymbolDeclaration", "init", { - $documentation: "A declaration symbol (symbol in var/const, function name or argument, symbol in catch)", +var AST_SymbolConst = DEFNODE("SymbolConst", null, { + $documentation: "Symbol defining a constant", +}, AST_SymbolDeclaration); + +var AST_SymbolImport = DEFNODE("SymbolImport", "key", { + $documentation: "Symbol defined by an `import` statement", $propdoc: { - init: "[AST_Node*/S] array of initializers for this declaration." - } -}, AST_Symbol); + key: "[AST_String] the original `export` name", + }, + _equals: function(node) { + return this.name == node.name + && this.key.equals(node.key); + }, + _validate: function() { + if (!(this.key instanceof AST_String)) throw new Error("key must be AST_String"); + }, +}, AST_SymbolConst); -var AST_SymbolVar = DEFNODE("SymbolVar", null, { - $documentation: "Symbol defining a variable", +var AST_SymbolLet = DEFNODE("SymbolLet", null, { + $documentation: "Symbol defining a lexical-scoped variable", }, AST_SymbolDeclaration); -var AST_SymbolConst = DEFNODE("SymbolConst", null, { - $documentation: "A constant declaration" +var AST_SymbolVar = DEFNODE("SymbolVar", null, { + $documentation: "Symbol defining a variable", }, AST_SymbolDeclaration); -var AST_SymbolFunarg = DEFNODE("SymbolFunarg", null, { +var AST_SymbolFunarg = DEFNODE("SymbolFunarg", "unused", { $documentation: "Symbol naming a function argument", }, AST_SymbolVar); @@ -818,6 +2024,14 @@ var AST_SymbolLambda = DEFNODE("SymbolLambda", null, { $documentation: "Symbol naming a function expression", }, AST_SymbolDeclaration); +var AST_SymbolDefClass = DEFNODE("SymbolDefClass", null, { + $documentation: "Symbol defining a class", +}, AST_SymbolConst); + +var AST_SymbolClass = DEFNODE("SymbolClass", null, { + $documentation: "Symbol naming a class expression", +}, AST_SymbolConst); + var AST_SymbolCatch = DEFNODE("SymbolCatch", null, { $documentation: "Symbol naming the exception in catch", }, AST_SymbolDeclaration); @@ -830,161 +2044,313 @@ var AST_Label = DEFNODE("Label", "references", { initialize: function() { this.references = []; this.thedef = this; - } + }, }, AST_Symbol); -var AST_SymbolRef = DEFNODE("SymbolRef", null, { +var AST_SymbolRef = DEFNODE("SymbolRef", "fixed in_arg redef", { $documentation: "Reference to some symbol (not definition/declaration)", }, AST_Symbol); +var AST_SymbolExport = DEFNODE("SymbolExport", "alias", { + $documentation: "Reference in an `export` statement", + $propdoc: { + alias: "[AST_String] the `export` alias", + }, + _equals: function(node) { + return this.name == node.name + && this.alias.equals(node.alias); + }, + _validate: function() { + if (!(this.alias instanceof AST_String)) throw new Error("alias must be AST_String"); + }, +}, AST_SymbolRef); + var AST_LabelRef = DEFNODE("LabelRef", null, { $documentation: "Reference to a label symbol", }, AST_Symbol); +var AST_ObjectIdentity = DEFNODE("ObjectIdentity", null, { + $documentation: "Base class for `super` & `this`", + _equals: return_true, + _validate: function() { + if (this.TYPE == "ObjectIdentity") throw new Error("should not instantiate AST_ObjectIdentity"); + }, +}, AST_Symbol); + +var AST_Super = DEFNODE("Super", null, { + $documentation: "The `super` symbol", + _validate: function() { + if (this.name !== "super") throw new Error('name must be "super"'); + }, +}, AST_ObjectIdentity); + var AST_This = DEFNODE("This", null, { $documentation: "The `this` symbol", -}, AST_Symbol); + _validate: function() { + if (this.TYPE == "This" && this.name !== "this") throw new Error('name must be "this"'); + }, +}, AST_ObjectIdentity); + +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); + }, +}, AST_This); + +var AST_Template = DEFNODE("Template", "expressions strings tag", { + $documentation: "A template literal, i.e. tag`str1${expr1}...strN${exprN}strN+1`", + $propdoc: { + expressions: "[AST_Node*] the placeholder expressions", + strings: "[string*] the raw text segments", + tag: "[AST_Node?] tag function, or null if absent", + }, + _equals: function(node) { + return prop_equals(this.tag, node.tag) + && list_equals(this.strings, node.strings) + && all_equals(this.expressions, node.expressions); + }, + walk: function(visitor) { + var node = this; + visitor.visit(node, function() { + if (node.tag) node.tag.walk(visitor); + node.expressions.forEach(function(expr) { + expr.walk(visitor); + }); + }); + }, + _validate: function() { + if (this.expressions.length + 1 != this.strings.length) { + throw new Error("malformed template with " + this.expressions.length + " placeholder(s) but " + this.strings.length + " text segment(s)"); + } + must_be_expressions(this, "expressions"); + this.strings.forEach(function(string) { + if (typeof string != "string") throw new Error("strings must contain string"); + }); + if (this.tag != null) must_be_expression(this, "tag"); + }, +}); var AST_Constant = DEFNODE("Constant", null, { $documentation: "Base class for all constants", - getValue: function() { - return this.value; - } + _equals: function(node) { + return this.value === node.value; + }, + _validate: function() { + if (this.TYPE == "Constant") throw new Error("should not instantiate AST_Constant"); + }, }); -var AST_String = DEFNODE("String", "value", { +var AST_String = DEFNODE("String", "quote value", { $documentation: "A string literal", $propdoc: { - value: "[string] the contents of this string" - } + quote: "[string?] the original quote character", + value: "[string] the contents of this string", + }, + _validate: function() { + if (this.quote != null) { + if (typeof this.quote != "string") throw new Error("quote must be string"); + if (!/^["']$/.test(this.quote)) throw new Error("invalid quote: " + this.quote); + } + if (typeof this.value != "string") throw new Error("value must be string"); + }, }, AST_Constant); var AST_Number = DEFNODE("Number", "value", { $documentation: "A number literal", $propdoc: { - value: "[number] the numeric value" - } + value: "[number] the numeric value", + }, + _validate: function() { + if (typeof this.value != "number") throw new Error("value must be number"); + if (!isFinite(this.value)) throw new Error("value must be finite"); + if (this.value < 0) throw new Error("value cannot be negative"); + }, +}, AST_Constant); + +var AST_BigInt = DEFNODE("BigInt", "value", { + $documentation: "A BigInt literal", + $propdoc: { + value: "[string] the numeric representation", + }, + _validate: function() { + if (typeof this.value != "string") throw new Error("value must be string"); + if (this.value[0] == "-") throw new Error("value cannot be negative"); + }, }, AST_Constant); var AST_RegExp = DEFNODE("RegExp", "value", { $documentation: "A regexp literal", $propdoc: { value: "[RegExp] the actual regexp" - } + }, + _equals: function(node) { + return "" + this.value == "" + node.value; + }, + _validate: function() { + if (!(this.value instanceof RegExp)) throw new Error("value must be RegExp"); + }, }, AST_Constant); var AST_Atom = DEFNODE("Atom", null, { $documentation: "Base class for atoms", + _equals: return_true, + _validate: function() { + if (this.TYPE == "Atom") throw new Error("should not instantiate AST_Atom"); + }, }, AST_Constant); var AST_Null = DEFNODE("Null", null, { $documentation: "The `null` atom", - value: null + value: null, }, AST_Atom); var AST_NaN = DEFNODE("NaN", null, { $documentation: "The impossible value", - value: 0/0 + value: 0/0, }, AST_Atom); var AST_Undefined = DEFNODE("Undefined", null, { $documentation: "The `undefined` value", - value: (function(){}()) + value: function(){}(), }, AST_Atom); var AST_Hole = DEFNODE("Hole", null, { $documentation: "A hole in an array", - value: (function(){}()) + value: function(){}(), }, AST_Atom); var AST_Infinity = DEFNODE("Infinity", null, { $documentation: "The `Infinity` value", - value: 1/0 + value: 1/0, }, AST_Atom); var AST_Boolean = DEFNODE("Boolean", null, { $documentation: "Base class for booleans", + _validate: function() { + if (this.TYPE == "Boolean") throw new Error("should not instantiate AST_Boolean"); + }, }, AST_Atom); var AST_False = DEFNODE("False", null, { $documentation: "The `false` atom", - value: false + value: false, }, AST_Boolean); var AST_True = DEFNODE("True", null, { $documentation: "The `true` atom", - value: true + value: true, }, AST_Boolean); /* -----[ TreeWalker ]----- */ function TreeWalker(callback) { - this.visit = callback; + this.callback = callback; + this.directives = Object.create(null); this.stack = []; -}; +} TreeWalker.prototype = { - _visit: function(node, descend) { - this.stack.push(node); - var ret = this.visit(node, descend ? function(){ - descend.call(node); - } : noop); - if (!ret && descend) { - descend.call(node); - } - this.stack.pop(); - return ret; + visit: function(node, descend) { + this.push(node); + var done = this.callback(node, descend || noop); + if (!done && descend) descend(); + this.pop(); }, parent: function(n) { return this.stack[this.stack.length - 2 - (n || 0)]; }, - push: function (node) { + push: function(node) { + var value; + if (node instanceof AST_Class) { + this.directives = Object.create(this.directives); + value = "use strict"; + } else if (node instanceof AST_Directive) { + value = node.value; + } else if (node instanceof AST_Lambda) { + this.directives = Object.create(this.directives); + } + if (value && !this.directives[value]) this.directives[value] = node; this.stack.push(node); }, pop: function() { - return this.stack.pop(); + var node = this.stack.pop(); + if (node instanceof AST_Class || node instanceof AST_Lambda) { + this.directives = Object.getPrototypeOf(this.directives); + } }, self: function() { return this.stack[this.stack.length - 1]; }, find_parent: function(type) { var stack = this.stack; - for (var i = stack.length; --i >= 0;) { + for (var i = stack.length - 1; --i >= 0;) { var x = stack[i]; if (x instanceof type) return x; } }, has_directive: function(type) { - return this.find_parent(AST_Scope).has_directive(type); - }, - in_boolean_context: function() { - var stack = this.stack; - var i = stack.length, self = stack[--i]; - while (i > 0) { - var p = stack[--i]; - if ((p instanceof AST_If && p.condition === self) || - (p instanceof AST_Conditional && p.condition === self) || - (p instanceof AST_DWLoop && p.condition === self) || - (p instanceof AST_For && p.condition === self) || - (p instanceof AST_UnaryPrefix && p.operator == "!" && p.expression === self)) - { - return true; + var dir = this.directives[type]; + if (dir) return dir; + var node = this.stack[this.stack.length - 1]; + if (node instanceof AST_Scope) { + for (var i = 0; i < node.body.length; ++i) { + var st = node.body[i]; + if (!(st instanceof AST_Directive)) break; + if (st.value == type) return st; } - if (!(p instanceof AST_Binary && (p.operator == "&&" || p.operator == "||"))) - return false; - self = p; } }, - loopcontrol_target: function(label) { + loopcontrol_target: function(node) { var stack = this.stack; - if (label) for (var i = stack.length; --i >= 0;) { + if (node.label) for (var i = stack.length; --i >= 0;) { var x = stack[i]; - if (x instanceof AST_LabeledStatement && x.label.name == label.name) { + if (x instanceof AST_LabeledStatement && x.label.name == node.label.name) return x.body; - } } else for (var i = stack.length; --i >= 0;) { var x = stack[i]; - if (x instanceof AST_Switch || x instanceof AST_IterationStatement) + if (x instanceof AST_IterationStatement + || node instanceof AST_Break && x instanceof AST_Switch) return x; } + }, + in_boolean_context: function() { + for (var drop = true, level = 0, parent, self = this.self(); parent = this.parent(level++); self = parent) { + if (parent instanceof AST_Binary) switch (parent.operator) { + case "&&": + case "||": + if (parent.left === self) drop = false; + continue; + default: + return false; + } + if (parent instanceof AST_Conditional) { + if (parent.condition === self) return true; + continue; + } + if (parent instanceof AST_DWLoop) return parent.condition === self; + if (parent instanceof AST_For) return parent.condition === self; + if (parent instanceof AST_If) return parent.condition === self; + if (parent instanceof AST_Return) { + if (parent.in_bool) return true; + while (parent = this.parent(level++)) { + if (parent instanceof AST_Lambda) { + if (parent.name) return false; + parent = this.parent(level++); + if (parent.TYPE != "Call") return false; + break; + } + } + } + if (parent instanceof AST_Sequence) { + if (parent.tail_node() === self) continue; + return drop ? "d" : true; + } + if (parent instanceof AST_SimpleStatement) return drop ? "d" : true; + if (parent instanceof AST_UnaryPrefix) return parent.operator == "!"; + return false; + } } }; diff --git a/lib/compress.js b/lib/compress.js index ae20d485f97..98661c765f7 100644 --- a/lib/compress.js +++ b/lib/compress.js @@ -1,7 +1,7 @@ /*********************************************************************** A JavaScript tokenizer / parser / beautifier / compressor. - https://github.com/mishoo/UglifyJS2 + https://github.com/mishoo/UglifyJS -------------------------------- (C) --------------------------------- @@ -48,2428 +48,14340 @@ function Compressor(options, false_by_default) { return new Compressor(options, false_by_default); TreeTransformer.call(this, this.before, this.after); this.options = defaults(options, { - sequences : !false_by_default, - properties : !false_by_default, - dead_code : !false_by_default, - drop_debugger : !false_by_default, - unsafe : false, - unsafe_comps : false, - conditionals : !false_by_default, - comparisons : !false_by_default, - evaluate : !false_by_default, - booleans : !false_by_default, - loops : !false_by_default, - unused : !false_by_default, - hoist_funs : !false_by_default, - keep_fargs : false, - keep_fnames : false, - hoist_vars : false, - if_return : !false_by_default, - join_vars : !false_by_default, - cascade : !false_by_default, - side_effects : !false_by_default, - pure_getters : false, - pure_funcs : null, - negate_iife : !false_by_default, - screw_ie8 : false, - drop_console : false, - angular : false, - - warnings : true, - global_defs : {} + annotations : !false_by_default, + arguments : !false_by_default, + arrows : !false_by_default, + assignments : !false_by_default, + awaits : !false_by_default, + booleans : !false_by_default, + collapse_vars : !false_by_default, + comparisons : !false_by_default, + conditionals : !false_by_default, + dead_code : !false_by_default, + default_values : !false_by_default, + directives : !false_by_default, + drop_console : false, + drop_debugger : !false_by_default, + evaluate : !false_by_default, + expression : false, + functions : !false_by_default, + global_defs : false, + hoist_exports : !false_by_default, + hoist_funs : false, + hoist_props : !false_by_default, + hoist_vars : false, + ie : false, + if_return : !false_by_default, + imports : !false_by_default, + inline : !false_by_default, + join_vars : !false_by_default, + keep_fargs : false_by_default, + keep_fnames : false, + keep_infinity : false, + loops : !false_by_default, + merge_vars : !false_by_default, + module : false, + negate_iife : !false_by_default, + objects : !false_by_default, + optional_chains : !false_by_default, + passes : 1, + properties : !false_by_default, + pure_funcs : null, + pure_getters : !false_by_default && "strict", + reduce_funcs : !false_by_default, + reduce_vars : !false_by_default, + rests : !false_by_default, + sequences : !false_by_default, + side_effects : !false_by_default, + spreads : !false_by_default, + strings : !false_by_default, + switches : !false_by_default, + templates : !false_by_default, + top_retain : null, + toplevel : !!(options && !options["expression"] && options["top_retain"]), + typeofs : !false_by_default, + unsafe : false, + unsafe_comps : false, + unsafe_Function : false, + unsafe_math : false, + unsafe_proto : false, + unsafe_regexp : false, + unsafe_undefined: false, + unused : !false_by_default, + varify : !false_by_default, + webkit : false, + yields : !false_by_default, }, true); -}; + var evaluate = this.options["evaluate"]; + this.eval_threshold = /eager/.test(evaluate) ? 1 / 0 : +evaluate; + var global_defs = this.options["global_defs"]; + if (typeof global_defs == "object") for (var key in global_defs) { + if (/^@/.test(key) && HOP(global_defs, key)) { + global_defs[key.slice(1)] = parse(global_defs[key], { expression: true }); + } + } + if (this.options["inline"] === true) this.options["inline"] = 4; + this.drop_fargs = this.options["keep_fargs"] ? return_false : function(lambda, parent) { + if (lambda.length_read) return false; + var name = lambda.name; + if (!name) return parent && parent.TYPE == "Call" && parent.expression === lambda; + if (name.fixed_value() !== lambda) return false; + var def = name.definition(); + if (def.direct_access) return false; + var escaped = def.escaped; + return escaped && escaped.depth != 1; + }; + if (this.options["module"]) this.directives["use strict"] = true; + var pure_funcs = this.options["pure_funcs"]; + if (typeof pure_funcs == "function") { + this.pure_funcs = pure_funcs; + } else if (typeof pure_funcs == "string") { + this.pure_funcs = function(node) { + var expr; + if (node instanceof AST_Call) { + expr = node.expression; + } else if (node instanceof AST_Template) { + expr = node.tag; + } + return !(expr && pure_funcs === expr.print_to_string()); + }; + } else if (Array.isArray(pure_funcs)) { + this.pure_funcs = function(node) { + var expr; + if (node instanceof AST_Call) { + expr = node.expression; + } else if (node instanceof AST_Template) { + expr = node.tag; + } + return !(expr && member(expr.print_to_string(), pure_funcs)); + }; + } else { + this.pure_funcs = return_true; + } + var sequences = this.options["sequences"]; + this.sequences_limit = sequences == 1 ? 800 : sequences | 0; + var top_retain = this.options["top_retain"]; + if (top_retain instanceof RegExp) { + this.top_retain = function(def) { + return top_retain.test(def.name); + }; + } else if (typeof top_retain == "function") { + this.top_retain = top_retain; + } else if (top_retain) { + if (typeof top_retain == "string") { + top_retain = top_retain.split(/,/); + } + this.top_retain = function(def) { + return member(def.name, top_retain); + }; + } + var toplevel = this.options["toplevel"]; + this.toplevel = typeof toplevel == "string" ? { + funcs: /funcs/.test(toplevel), + vars: /vars/.test(toplevel) + } : { + funcs: toplevel, + vars: toplevel + }; +} -Compressor.prototype = new TreeTransformer; -merge(Compressor.prototype, { - option: function(key) { return this.options[key] }, - warn: function() { - if (this.options.warnings) - AST_Node.warn.apply(AST_Node, arguments); - }, - before: function(node, descend, in_list) { - if (node._squeezed) return node; - var was_scope = false; - if (node instanceof AST_Scope) { - node = node.hoist_declarations(this); - was_scope = true; - } - descend(node, this); - node = node.optimize(this); - if (was_scope && node instanceof AST_Scope) { - node.drop_unused(this); - descend(node, this); - } - node._squeezed = true; - return node; +Compressor.prototype = new TreeTransformer(function(node, descend, in_list) { + if (node._squeezed) return node; + var is_scope = node instanceof AST_Scope; + if (is_scope) { + if (this.option("arrows") && is_arrow(node) && node.value) { + node.body = [ node.first_statement() ]; + node.value = null; + } + node.hoist_properties(this); + node.hoist_declarations(this); + node.process_returns(this); } + // Before https://github.com/mishoo/UglifyJS/pull/1602 AST_Node.optimize() + // would call AST_Node.transform() if a different instance of AST_Node is + // produced after OPT(). + // This corrupts TreeWalker.stack, which cause AST look-ups to malfunction. + // Migrate and defer all children's AST_Node.transform() to below, which + // will now happen after this parent AST_Node has been properly substituted + // thus gives a consistent AST snapshot. + descend(node, this); + // Existing code relies on how AST_Node.optimize() worked, and omitting the + // following replacement call would result in degraded efficiency of both + // output and performance. + descend(node, this); + var opt = node.optimize(this); + if (is_scope && opt === node && !this.has_directive("use asm") && !opt.pinned()) { + opt.drop_unused(this); + if (opt.merge_variables(this)) opt.drop_unused(this); + descend(opt, this); + } + if (opt === node) opt._squeezed = true; + return opt; }); +Compressor.prototype.option = function(key) { + return this.options[key]; +}; +Compressor.prototype.exposed = function(def) { + if (def.exported) return true; + if (def.undeclared) return true; + if (!(def.global || def.scope.resolve() instanceof AST_Toplevel)) return false; + var toplevel = this.toplevel; + return !all(def.orig, function(sym) { + return toplevel[sym instanceof AST_SymbolDefun ? "funcs" : "vars"]; + }); +}; +Compressor.prototype.compress = function(node) { + node = node.resolve_defines(this); + node.hoist_exports(this); + if (this.option("expression")) node.process_expression(true); + var merge_vars = this.options.merge_vars; + var passes = +this.options.passes || 1; + var min_count = 1 / 0; + var stopping = false; + var mangle = { ie: this.option("ie") }; + for (var pass = 0; pass < passes; pass++) { + node.figure_out_scope(mangle); + if (pass > 0 || this.option("reduce_vars")) + node.reset_opt_flags(this); + this.options.merge_vars = merge_vars && (stopping || pass == passes - 1); + node = node.transform(this); + if (passes > 1) { + var count = 0; + node.walk(new TreeWalker(function() { + count++; + })); + AST_Node.info("pass {pass}: last_count: {min_count}, count: {count}", { + pass: pass, + min_count: min_count, + count: count, + }); + if (count < min_count) { + min_count = count; + stopping = false; + } else if (stopping) { + break; + } else { + stopping = true; + } + } + } + if (this.option("expression")) node.process_expression(false); + return node; +}; -(function(){ +(function(OPT) { + OPT(AST_Node, function(self, compressor) { + return self; + }); - function OPT(node, optimizer) { - node.DEFMETHOD("optimize", function(compressor){ - var self = this; - if (self._optimized) return self; - var opt = optimizer(self, compressor); - opt._optimized = true; - if (opt === self) return opt; - return opt.transform(compressor); - }); - }; + AST_Toplevel.DEFMETHOD("hoist_exports", function(compressor) { + if (!compressor.option("hoist_exports")) return; + var body = this.body, props = []; + for (var i = 0; i < body.length; i++) { + var stat = body[i]; + if (stat instanceof AST_ExportDeclaration) { + body[i] = stat = stat.body; + if (stat instanceof AST_Definitions) { + stat.definitions.forEach(function(defn) { + defn.name.match_symbol(export_symbol, true); + }); + } else { + export_symbol(stat.name); + } + } else if (stat instanceof AST_ExportReferences) { + body.splice(i--, 1); + [].push.apply(props, stat.properties); + } + } + if (props.length) body.push(make_node(AST_ExportReferences, this, { properties: props })); - OPT(AST_Node, function(self, compressor){ - return self; + function export_symbol(sym) { + if (!(sym instanceof AST_SymbolDeclaration)) return; + var node = make_node(AST_SymbolExport, sym); + node.alias = make_node(AST_String, node, { value: node.name }); + props.push(node); + } }); - AST_Node.DEFMETHOD("equivalent_to", function(node){ - // XXX: this is a rather expensive way to test two node's equivalence: - return this.print_to_string() == node.print_to_string(); + AST_Scope.DEFMETHOD("process_expression", function(insert, transform) { + var self = this; + var tt = new TreeTransformer(function(node) { + if (insert) { + if (node instanceof AST_Directive) node = make_node(AST_SimpleStatement, node, { + body: make_node(AST_String, node), + }); + if (node instanceof AST_SimpleStatement) { + return transform ? transform(node) : make_node(AST_Return, node, { value: node.body }); + } + } else if (node instanceof AST_Return) { + if (transform) return transform(node); + var value = node.value; + if (value instanceof AST_String) return make_node(AST_Directive, value); + return make_node(AST_SimpleStatement, node, { + body: value || make_node(AST_UnaryPrefix, node, { + operator: "void", + expression: make_node(AST_Number, node, { value: 0 }), + }), + }); + } + if (node instanceof AST_Block) { + if (node instanceof AST_Lambda) { + if (node !== self) return node; + } else if (insert === "awaits" && node instanceof AST_Try) { + if (node.bfinally) return node; + } + for (var index = node.body.length; --index >= 0;) { + var stat = node.body[index]; + if (!is_declaration(stat, true)) { + node.body[index] = stat.transform(tt); + break; + } + } + } else if (node instanceof AST_If) { + node.body = node.body.transform(tt); + if (node.alternative) node.alternative = node.alternative.transform(tt); + } else if (node instanceof AST_With) { + node.body = node.body.transform(tt); + } + return node; + }); + self.transform(tt); + }); + AST_Toplevel.DEFMETHOD("unwrap_expression", function() { + var self = this; + switch (self.body.length) { + case 0: + return make_node(AST_UnaryPrefix, self, { + operator: "void", + expression: make_node(AST_Number, self, { value: 0 }), + }); + case 1: + var stat = self.body[0]; + if (stat instanceof AST_Directive) return make_node(AST_String, stat); + if (stat instanceof AST_SimpleStatement) return stat.body; + default: + return make_node(AST_Call, self, { + expression: make_node(AST_Function, self, { + argnames: [], + body: self.body, + }).init_vars(self, self), + args: [], + }); + } + }); + AST_Node.DEFMETHOD("wrap_expression", function() { + var self = this; + if (!is_statement(self)) self = make_node(AST_SimpleStatement, self, { body: self }); + if (!(self instanceof AST_Toplevel)) self = make_node(AST_Toplevel, self, { body: [ self ] }); + return self; }); - function make_node(ctor, orig, props) { - if (!props) props = {}; - if (orig) { - if (!props.start) props.start = orig.start; - if (!props.end) props.end = orig.end; + function read_property(obj, node) { + var key = node.get_property(); + if (key instanceof AST_Node) return; + var value; + if (obj instanceof AST_Array) { + var elements = obj.elements; + if (key == "length") return make_node_from_constant(elements.length, obj); + if (typeof key == "number" && key in elements) value = elements[key]; + } else if (obj instanceof AST_Lambda) { + if (key == "length") { + obj.length_read = true; + return make_node_from_constant(obj.argnames.length, obj); + } + } else if (obj instanceof AST_Object) { + key = "" + key; + var props = obj.properties; + for (var i = props.length; --i >= 0;) { + var prop = props[i]; + if (!can_hoist_property(prop)) return; + if (!value && props[i].key === key) value = props[i].value; + } } - return new ctor(props); - }; + return value instanceof AST_SymbolRef && value.fixed_value() || value; + } - function make_node_from_constant(compressor, val, orig) { - // XXX: WIP. - // if (val instanceof AST_Node) return val.transform(new TreeTransformer(null, function(node){ - // if (node instanceof AST_SymbolRef) { - // var scope = compressor.find_parent(AST_Scope); - // var def = scope.find_variable(node); - // node.thedef = def; - // return node; - // } - // })).transform(compressor); - - if (val instanceof AST_Node) return val.transform(compressor); - switch (typeof val) { - case "string": - return make_node(AST_String, orig, { - value: val - }).optimize(compressor); - case "number": - return make_node(isNaN(val) ? AST_NaN : AST_Number, orig, { - value: val - }).optimize(compressor); - case "boolean": - return make_node(val ? AST_True : AST_False, orig).optimize(compressor); - case "undefined": - return make_node(AST_Undefined, orig).optimize(compressor); + function is_read_only_fn(value, name) { + if (value instanceof AST_Boolean) return native_fns.Boolean[name]; + if (value instanceof AST_Number) return native_fns.Number[name]; + if (value instanceof AST_String) return native_fns.String[name]; + if (name == "valueOf") return false; + if (value instanceof AST_Array) return native_fns.Array[name]; + if (value instanceof AST_Lambda) return native_fns.Function[name]; + if (value instanceof AST_Object) return native_fns.Object[name]; + if (value instanceof AST_RegExp) return native_fns.RegExp[name] && !value.value.global; + } + + function is_modified(compressor, tw, node, value, level, immutable, recursive) { + var parent = tw.parent(level); + if (compressor.option("unsafe") && parent instanceof AST_Dot && is_read_only_fn(value, parent.property)) { + return; + } + var lhs = is_lhs(node, parent); + if (lhs) return lhs; + if (level == 0 && value && value.is_constant()) return; + if (parent instanceof AST_Array) return is_modified(compressor, tw, parent, parent, level + 1); + if (parent instanceof AST_Assign) switch (parent.operator) { + case "=": + return is_modified(compressor, tw, parent, value, level + 1, immutable, recursive); + case "&&=": + case "||=": + case "??=": + return is_modified(compressor, tw, parent, parent, level + 1); default: - if (val === null) { - return make_node(AST_Null, orig, { value: null }).optimize(compressor); + return; + } + if (parent instanceof AST_Binary) { + if (!lazy_op[parent.operator]) return; + return is_modified(compressor, tw, parent, parent, level + 1); + } + if (parent instanceof AST_Call) { + return !immutable + && parent.expression === node + && !parent.is_expr_pure(compressor) + && (!(value instanceof AST_LambdaExpression) || !(parent instanceof AST_New) && value.contains_this()); + } + if (parent instanceof AST_Conditional) { + if (parent.condition === node) return; + return is_modified(compressor, tw, parent, parent, level + 1); + } + if (parent instanceof AST_ForEnumeration) return parent.init === node; + if (parent instanceof AST_ObjectKeyVal) { + if (parent.value !== node) return; + var obj = tw.parent(level + 1); + return is_modified(compressor, tw, obj, obj, level + 2); + } + if (parent instanceof AST_PropAccess) { + if (parent.expression !== node) return; + var prop = read_property(value, parent); + return (!immutable || recursive) && is_modified(compressor, tw, parent, prop, level + 1); + } + if (parent instanceof AST_Sequence) { + if (parent.tail_node() !== node) return; + return is_modified(compressor, tw, parent, value, level + 1, immutable, recursive); + } + } + + function is_lambda(node) { + return node instanceof AST_Class || node instanceof AST_Lambda; + } + + function safe_for_extends(node) { + return node instanceof AST_Class || node instanceof AST_Defun || node instanceof AST_Function; + } + + function is_arguments(def) { + return def.name == "arguments" && def.scope.uses_arguments; + } + + function cross_scope(def, sym) { + do { + if (def === sym) return false; + if (sym instanceof AST_Scope) return true; + } while (sym = sym.parent_scope); + } + + function can_drop_symbol(ref, compressor, keep_lambda) { + var def = ref.redef || ref.definition(); + if (ref.in_arg && is_funarg(def)) return false; + return all(def.orig, function(sym) { + if (sym instanceof AST_SymbolConst || sym instanceof AST_SymbolLet) { + if (sym instanceof AST_SymbolImport) return true; + return compressor && safe_from_tdz(compressor, sym); } - if (val instanceof RegExp) { - return make_node(AST_RegExp, orig, { value: val }).optimize(compressor); + return !(keep_lambda && sym instanceof AST_SymbolLambda); + }); + } + + function has_escaped(d, scope, node, parent) { + if (parent instanceof AST_Assign) return parent.operator == "=" && parent.right === node; + if (parent instanceof AST_Call) return parent.expression !== node || parent instanceof AST_New; + if (parent instanceof AST_ClassField) return parent.value === node && !parent.static; + if (parent instanceof AST_Exit) return parent.value === node && scope.resolve() !== d.scope.resolve(); + if (parent instanceof AST_VarDef) return parent.value === node; + } + + function make_ref(ref, fixed) { + var node = make_node(AST_SymbolRef, ref); + node.fixed = fixed || make_node(AST_Undefined, ref); + return node; + } + + function replace_ref(resolve, fixed) { + return function(node) { + var ref = resolve(node); + var node = make_ref(ref, fixed); + var def = ref.definition(); + def.references.push(node); + def.replaced++; + return node; + }; + } + + var RE_POSITIVE_INTEGER = /^(0|[1-9][0-9]*)$/; + (function(def) { + def(AST_Node, noop); + + function reset_def(tw, compressor, def) { + def.assignments = 0; + def.bool_return = 0; + def.cross_loop = false; + def.direct_access = false; + def.drop_return = 0; + def.escaped = []; + def.first_decl = null; + def.fixed = !def.const_redefs + && !def.scope.pinned() + && !compressor.exposed(def) + && !(def.init instanceof AST_LambdaExpression && def.init !== def.scope) + && def.init; + def.reassigned = 0; + def.recursive_refs = 0; + def.references = []; + def.single_use = undefined; + } + + function reset_block_variables(tw, compressor, scope) { + scope.variables.each(function(def) { + reset_def(tw, compressor, def); + }); + } + + function reset_variables(tw, compressor, scope) { + scope.fn_defs = []; + scope.variables.each(function(def) { + reset_def(tw, compressor, def); + var init = def.init; + if (init instanceof AST_LambdaDefinition) { + scope.fn_defs.push(init); + init.safe_ids = null; + } + if (def.fixed === null) { + def.safe_ids = tw.safe_ids; + mark(tw, def); + } else if (def.fixed) { + tw.loop_ids[def.id] = tw.in_loop; + mark(tw, def); + } + }); + scope.may_call_this = function() { + scope.may_call_this = scope.contains_this() ? return_true : return_false; + }; + if (scope.uses_arguments) scope.each_argname(function(node) { + node.definition().last_ref = false; + }); + if (compressor.option("ie")) scope.variables.each(function(def) { + var d = def.orig[0].definition(); + if (d !== def) d.fixed = false; + }); + } + + function safe_to_visit(tw, fn) { + var marker = fn.safe_ids; + return marker === undefined || marker === tw.safe_ids; + } + + function walk_fn_def(tw, fn) { + var was_scanning = tw.fn_scanning; + tw.fn_scanning = fn; + fn.walk(tw); + tw.fn_scanning = was_scanning; + } + + function revisit_fn_def(tw, fn) { + fn.enclosed.forEach(function(d) { + if (fn.variables.get(d.name) === d) return; + if (safe_to_read(tw, d)) return; + d.single_use = false; + var fixed = d.fixed; + if (typeof fixed == "function") fixed = fixed(); + if (fixed instanceof AST_Lambda && fixed.safe_ids !== undefined) return; + d.fixed = false; + }); + } + + function mark_fn_def(tw, def, fn) { + var marker = fn.safe_ids; + if (marker === undefined) return; + if (marker === false) return; + if (fn.parent_scope.resolve().may_call_this === return_true) { + if (member(fn, tw.fn_visited)) revisit_fn_def(tw, fn); + } else if (marker) { + var visited = member(fn, tw.fn_visited); + if (marker === tw.safe_ids) { + if (!visited) walk_fn_def(tw, fn); + } else if (visited) { + revisit_fn_def(tw, fn); + } else { + fn.safe_ids = false; + } + } else if (tw.fn_scanning && tw.fn_scanning !== def.scope.resolve()) { + fn.safe_ids = false; + } else { + fn.safe_ids = tw.safe_ids; + walk_fn_def(tw, fn); } - throw new Error(string_template("Can't handle constant of type: {type}", { - type: typeof val - })); } - }; - function as_statement_array(thing) { - if (thing === null) return []; - if (thing instanceof AST_BlockStatement) return thing.body; - if (thing instanceof AST_EmptyStatement) return []; - if (thing instanceof AST_Statement) return [ thing ]; - throw new Error("Can't convert thing to statement array"); - }; + function pop_scope(tw, scope) { + pop(tw); + var fn_defs = scope.fn_defs; + fn_defs.forEach(function(fn) { + fn.safe_ids = tw.safe_ids; + walk_fn_def(tw, fn); + }); + fn_defs.forEach(function(fn) { + fn.safe_ids = undefined; + }); + scope.fn_defs = undefined; + scope.may_call_this = undefined; + } - function is_empty(thing) { - if (thing === null) return true; - if (thing instanceof AST_EmptyStatement) return true; - if (thing instanceof AST_BlockStatement) return thing.body.length == 0; - return false; - }; + function push(tw, sequential) { + var safe_ids = Object.create(tw.safe_ids); + if (!sequential) safe_ids.seq = {}; + tw.safe_ids = safe_ids; + } - function loop_body(x) { - if (x instanceof AST_Switch) return x; - if (x instanceof AST_For || x instanceof AST_ForIn || x instanceof AST_DWLoop) { - return (x.body instanceof AST_BlockStatement ? x.body : x); + function pop(tw) { + tw.safe_ids = Object.getPrototypeOf(tw.safe_ids); } - return x; - }; - function tighten_body(statements, compressor) { - var CHANGED; - do { - CHANGED = false; - if (compressor.option("angular")) { - statements = process_for_angular(statements); + function mark(tw, def) { + tw.safe_ids[def.id] = {}; + } + + function push_ref(def, ref) { + def.references.push(ref); + if (def.last_ref !== false) def.last_ref = ref; + } + + function safe_to_read(tw, def) { + if (def.single_use == "m") return false; + var safe = tw.safe_ids[def.id]; + if (safe) { + var in_order = HOP(tw.safe_ids, def.id); + if (!in_order) { + var seq = tw.safe_ids.seq; + if (!safe.read) { + safe.read = seq; + } else if (safe.read !== seq) { + safe.read = true; + } + } + if (def.fixed == null) { + if (is_arguments(def)) return false; + if (def.global && def.name == "arguments") return false; + tw.loop_ids[def.id] = null; + def.fixed = make_node(AST_Undefined, def.orig[0]); + if (in_order) def.safe_ids = undefined; + return true; + } + return !safe.assign || safe.assign === tw.safe_ids; } - statements = eliminate_spurious_blocks(statements); - if (compressor.option("dead_code")) { - statements = eliminate_dead_code(statements, compressor); + return def.fixed instanceof AST_LambdaDefinition; + } + + function safe_to_assign(tw, def, declare) { + if (!declare) { + if (is_funarg(def) && def.scope.uses_arguments && !tw.has_directive("use strict")) return false; + if (!all(def.orig, function(sym) { + return !(sym instanceof AST_SymbolConst); + })) return false; } - if (compressor.option("if_return")) { - statements = handle_if_return(statements, compressor); + if (def.fixed === undefined) return declare || all(def.orig, function(sym) { + return !(sym instanceof AST_SymbolLet); + }); + if (def.fixed === false || def.fixed === 0) return false; + var safe = tw.safe_ids[def.id]; + if (def.safe_ids) { + def.safe_ids[def.id] = false; + def.safe_ids = undefined; + return def.fixed === null || HOP(tw.safe_ids, def.id) && !safe.read; } - if (compressor.option("sequences")) { - statements = sequencesize(statements, compressor); + if (!HOP(tw.safe_ids, def.id)) { + if (!safe) return false; + if (safe.read || tw.in_loop) { + var scope = tw.find_parent(AST_BlockScope); + if (scope instanceof AST_Class) return false; + if (def.scope.resolve() !== scope.resolve()) return false; + } + safe.assign = safe.assign && safe.assign !== tw.safe_ids ? true : tw.safe_ids; } - if (compressor.option("join_vars")) { - statements = join_consecutive_vars(statements, compressor); + if (def.fixed != null && safe.read) { + if (safe.read !== tw.safe_ids.seq) return false; + if (tw.loop_ids[def.id] !== tw.in_loop) return false; } - } while (CHANGED); - - if (compressor.option("negate_iife")) { - negate_iifes(statements, compressor); + return safe_to_read(tw, def) && all(def.orig, function(sym) { + return !(sym instanceof AST_SymbolLambda); + }); } - return statements; + function ref_once(compressor, def) { + return compressor.option("unused") + && !def.scope.pinned() + && def.single_use !== false + && def.references.length - def.recursive_refs == 1 + && !(is_funarg(def) && def.scope.uses_arguments); + } - function process_for_angular(statements) { - function has_inject(comment) { - return /@ngInject/.test(comment.value); - } - function make_arguments_names_list(func) { - return func.argnames.map(function(sym){ - return make_node(AST_String, sym, { value: sym.name }); - }); + function is_immutable(value) { + if (!value) return false; + if (value instanceof AST_Assign) { + var op = value.operator; + return op == "=" ? is_immutable(value.right) : !lazy_op[op.slice(0, -1)]; } - function make_array(orig, elements) { - return make_node(AST_Array, orig, { elements: elements }); - } - function make_injector(func, name) { - return make_node(AST_SimpleStatement, func, { - body: make_node(AST_Assign, func, { - operator: "=", - left: make_node(AST_Dot, name, { - expression: make_node(AST_SymbolRef, name, name), - property: "$inject" - }), - right: make_array(func, make_arguments_names_list(func)) - }) - }); - } - function check_expression(body) { - if (body && body.args) { - // if this is a function call check all of arguments passed - body.args.forEach(function(argument, index, array) { - var comments = argument.start.comments_before; - // if the argument is function preceded by @ngInject - if (argument instanceof AST_Lambda && comments.length && has_inject(comments[0])) { - // replace the function with an array of names of its parameters and function at the end - array[index] = make_array(argument, make_arguments_names_list(argument).concat(argument)); - } - }); - // if this is chained call check previous one recursively - if (body.expression && body.expression.expression) { - check_expression(body.expression.expression); - } - } + if (value instanceof AST_Sequence) return is_immutable(value.tail_node()); + return value.is_constant() || is_lambda(value) || value instanceof AST_ObjectIdentity; + } + + function value_in_use(node, parent) { + if (parent instanceof AST_Array) return true; + if (parent instanceof AST_Binary) return lazy_op[parent.operator]; + if (parent instanceof AST_Conditional) return parent.condition !== node; + if (parent instanceof AST_Sequence) return parent.tail_node() === node; + if (parent instanceof AST_Spread) return true; + } + + function mark_escaped(tw, d, scope, node, value, level, depth) { + var parent = tw.parent(level); + if (value && value.is_constant()) return; + if (has_escaped(d, scope, node, parent)) { + d.escaped.push(parent); + if (depth > 1 && !(value && value.is_constant_expression(scope))) depth = 1; + if (!d.escaped.depth || d.escaped.depth > depth) d.escaped.depth = depth; + if (d.scope.resolve() !== scope.resolve()) d.escaped.cross_scope = true; + if (d.fixed) d.fixed.escaped = d.escaped; + return; + } else if (value_in_use(node, parent)) { + mark_escaped(tw, d, scope, parent, parent, level + 1, depth); + } else if (parent instanceof AST_ObjectKeyVal && parent.value === node) { + var obj = tw.parent(level + 1); + mark_escaped(tw, d, scope, obj, obj, level + 2, depth); + } else if (parent instanceof AST_PropAccess && parent.expression === node) { + value = read_property(value, parent); + mark_escaped(tw, d, scope, parent, value, level + 1, depth + 1); + if (value) return; } - return statements.reduce(function(a, stat){ - a.push(stat); + if (level > 0) return; + if (parent instanceof AST_Call && parent.expression === node) return; + if (parent instanceof AST_Sequence && parent.tail_node() !== node) return; + if (parent instanceof AST_SimpleStatement) return; + if (parent instanceof AST_Unary && !unary_side_effects[parent.operator]) return; + d.direct_access = true; + if (d.fixed) d.fixed.direct_access = true; + } - if (stat.body && stat.body.args) { - check_expression(stat.body); - } else { - var token = stat.start; - var comments = token.comments_before; - if (comments && comments.length > 0) { - var last = comments.pop(); - if (has_inject(last)) { - // case 1: defun - if (stat instanceof AST_Defun) { - a.push(make_injector(stat, stat.name)); - } - else if (stat instanceof AST_Definitions) { - stat.definitions.forEach(function(def) { - if (def.value && def.value instanceof AST_Lambda) { - a.push(make_injector(def.value, def.name)); - } - }); - } - else { - compressor.warn("Unknown statement marked with @ngInject [{file}:{line},{col}]", token); - } - } - } - } + function mark_assignment_to_arguments(node) { + if (!(node instanceof AST_Sub)) return; + var expr = node.expression; + if (!(expr instanceof AST_SymbolRef)) return; + var def = expr.definition(); + if (!is_arguments(def)) return; + var key = node.property; + if (key.is_constant()) key = key.value; + if (!(key instanceof AST_Node) && !RE_POSITIVE_INTEGER.test(key)) return; + def.reassigned++; + (key instanceof AST_Node ? def.scope.argnames : [ def.scope.argnames[key] ]).forEach(function(argname) { + if (argname instanceof AST_SymbolFunarg) argname.definition().fixed = false; + }); + } - return a; - }, []); + function make_fixed(save, fn) { + var prev_save, prev_value; + return function() { + var current = save(); + if (prev_save !== current) { + prev_save = current; + prev_value = fn(current); + } + return prev_value; + }; } - function eliminate_spurious_blocks(statements) { - var seen_dirs = []; - return statements.reduce(function(a, stat){ - if (stat instanceof AST_BlockStatement) { - CHANGED = true; - a.push.apply(a, eliminate_spurious_blocks(stat.body)); - } else if (stat instanceof AST_EmptyStatement) { - CHANGED = true; - } else if (stat instanceof AST_Directive) { - if (seen_dirs.indexOf(stat.value) < 0) { - a.push(stat); - seen_dirs.push(stat.value); - } else { - CHANGED = true; - } - } else { - a.push(stat); + function make_fixed_default(compressor, node, save) { + var prev_save, prev_seq; + return function() { + if (prev_seq === node) return node; + var current = save(); + var ev = fuzzy_eval(compressor, current, true); + if (ev instanceof AST_Node) { + prev_seq = node; + } else if (prev_save !== current) { + prev_save = current; + prev_seq = ev === undefined ? make_sequence(node, [ current, node.value ]) : current; } - return a; - }, []); - }; + return prev_seq; + }; + } - function handle_if_return(statements, compressor) { - var self = compressor.self(); - var in_lambda = self instanceof AST_Lambda; - var ret = []; - loop: for (var i = statements.length; --i >= 0;) { - var stat = statements[i]; - switch (true) { - case (in_lambda && stat instanceof AST_Return && !stat.value && ret.length == 0): - CHANGED = true; - // note, ret.length is probably always zero - // because we drop unreachable code before this - // step. nevertheless, it's good to check. - continue loop; - case stat instanceof AST_If: - if (stat.body instanceof AST_Return) { - //--- - // pretty silly case, but: - // if (foo()) return; return; ==> foo(); return; - if (((in_lambda && ret.length == 0) - || (ret[0] instanceof AST_Return && !ret[0].value)) - && !stat.body.value && !stat.alternative) { - CHANGED = true; - var cond = make_node(AST_SimpleStatement, stat.condition, { - body: stat.condition - }); - ret.unshift(cond); - continue loop; - } - //--- - // if (foo()) return x; return y; ==> return foo() ? x : y; - if (ret[0] instanceof AST_Return && stat.body.value && ret[0].value && !stat.alternative) { - CHANGED = true; - stat = stat.clone(); - stat.alternative = ret[0]; - ret[0] = stat.transform(compressor); - continue loop; - } - //--- - // if (foo()) return x; [ return ; ] ==> return foo() ? x : undefined; - if ((ret.length == 0 || ret[0] instanceof AST_Return) && stat.body.value && !stat.alternative && in_lambda) { - CHANGED = true; - stat = stat.clone(); - stat.alternative = ret[0] || make_node(AST_Return, stat, { - value: make_node(AST_Undefined, stat) - }); - ret[0] = stat.transform(compressor); - continue loop; - } - //--- - // if (foo()) return; [ else x... ]; y... ==> if (!foo()) { x...; y... } - if (!stat.body.value && in_lambda) { - CHANGED = true; - stat = stat.clone(); - stat.condition = stat.condition.negate(compressor); - stat.body = make_node(AST_BlockStatement, stat, { - body: as_statement_array(stat.alternative).concat(ret) + function scan_declaration(tw, compressor, lhs, fixed, visit) { + var scanner = new TreeWalker(function(node) { + if (node instanceof AST_DefaultValue) { + reset_flags(node); + push(tw, true); + node.value.walk(tw); + pop(tw); + var save = fixed; + if (save) fixed = make_fixed_default(compressor, node, save); + node.name.walk(scanner); + fixed = save; + return true; + } + if (node instanceof AST_DestructuredArray) { + reset_flags(node); + var save = fixed; + node.elements.forEach(function(node, index) { + if (node instanceof AST_Hole) return reset_flags(node); + if (save) fixed = make_fixed(save, function(value) { + return make_node(AST_Sub, node, { + expression: value, + property: make_node(AST_Number, node, { value: index }), }); - stat.alternative = null; - ret = [ stat.transform(compressor) ]; - continue loop; - } - //--- - if (ret.length == 1 && in_lambda && ret[0] instanceof AST_SimpleStatement - && (!stat.alternative || stat.alternative instanceof AST_SimpleStatement)) { - CHANGED = true; - ret.push(make_node(AST_Return, ret[0], { - value: make_node(AST_Undefined, ret[0]) - }).transform(compressor)); - ret = as_statement_array(stat.alternative).concat(ret); - ret.unshift(stat); - continue loop; - } - } - - var ab = aborts(stat.body); - var lct = ab instanceof AST_LoopControl ? compressor.loopcontrol_target(ab.label) : null; - if (ab && ((ab instanceof AST_Return && !ab.value && in_lambda) - || (ab instanceof AST_Continue && self === loop_body(lct)) - || (ab instanceof AST_Break && lct instanceof AST_BlockStatement && self === lct))) { - if (ab.label) { - remove(ab.label.thedef.references, ab); - } - CHANGED = true; - var body = as_statement_array(stat.body).slice(0, -1); - stat = stat.clone(); - stat.condition = stat.condition.negate(compressor); - stat.body = make_node(AST_BlockStatement, stat, { - body: as_statement_array(stat.alternative).concat(ret) }); - stat.alternative = make_node(AST_BlockStatement, stat, { - body: body + node.walk(scanner); + }); + if (node.rest) { + var fixed_node; + if (save) fixed = compressor.option("rests") && make_fixed(save, function(value) { + if (!(value instanceof AST_Array)) return node; + for (var i = 0, len = node.elements.length; i < len; i++) { + if (value.elements[i] instanceof AST_Spread) return node; + } + if (!fixed_node) fixed_node = make_node(AST_Array, node, {}); + fixed_node.elements = value.elements.slice(len); + return fixed_node; }); - ret = [ stat.transform(compressor) ]; - continue loop; + node.rest.walk(scanner); } - - var ab = aborts(stat.alternative); - var lct = ab instanceof AST_LoopControl ? compressor.loopcontrol_target(ab.label) : null; - if (ab && ((ab instanceof AST_Return && !ab.value && in_lambda) - || (ab instanceof AST_Continue && self === loop_body(lct)) - || (ab instanceof AST_Break && lct instanceof AST_BlockStatement && self === lct))) { - if (ab.label) { - remove(ab.label.thedef.references, ab); + fixed = save; + return true; + } + if (node instanceof AST_DestructuredObject) { + reset_flags(node); + var save = fixed; + node.properties.forEach(function(node) { + reset_flags(node); + if (node.key instanceof AST_Node) { + push(tw); + node.key.walk(tw); + pop(tw); } - CHANGED = true; - stat = stat.clone(); - stat.body = make_node(AST_BlockStatement, stat.body, { - body: as_statement_array(stat.body).concat(ret) - }); - stat.alternative = make_node(AST_BlockStatement, stat.alternative, { - body: as_statement_array(stat.alternative).slice(0, -1) + if (save) fixed = make_fixed(save, function(value) { + var key = node.key; + var type = AST_Sub; + if (typeof key == "string") { + if (is_identifier_string(key)) { + type = AST_Dot; + } else { + key = make_node_from_constant(key, node); + } + } + return make_node(type, node, { + expression: value, + property: key, + }); }); - ret = [ stat.transform(compressor) ]; - continue loop; + node.value.walk(scanner); + }); + if (node.rest) { + fixed = false; + node.rest.walk(scanner); } - - ret.unshift(stat); - break; - default: - ret.unshift(stat); - break; + fixed = save; + return true; } - } - return ret; - }; - - function eliminate_dead_code(statements, compressor) { - var has_quit = false; - var orig = statements.length; - var self = compressor.self(); - statements = statements.reduce(function(a, stat){ - if (has_quit) { - extract_declarations_from_unreachable_code(compressor, stat, a); - } else { - if (stat instanceof AST_LoopControl) { - var lct = compressor.loopcontrol_target(stat.label); - if ((stat instanceof AST_Break - && lct instanceof AST_BlockStatement - && loop_body(lct) === self) || (stat instanceof AST_Continue - && loop_body(lct) === self)) { - if (stat.label) { - remove(stat.label.thedef.references, stat); - } - } else { - a.push(stat); - } - } else { - a.push(stat); + visit(node, fixed, function() { + var save_len = tw.stack.length; + for (var i = 0, len = scanner.stack.length - 1; i < len; i++) { + tw.stack.push(scanner.stack[i]); } - if (aborts(stat)) has_quit = true; - } - return a; - }, []); - CHANGED = statements.length != orig; - return statements; - }; + node.walk(tw); + tw.stack.length = save_len; + }); + return true; + }); + lhs.walk(scanner); + } - function sequencesize(statements, compressor) { - if (statements.length < 2) return statements; - var seq = [], ret = []; - function push_seq() { - seq = AST_Seq.from_array(seq); - if (seq) ret.push(make_node(AST_SimpleStatement, seq, { - body: seq - })); - seq = []; - }; - statements.forEach(function(stat){ - if (stat instanceof AST_SimpleStatement) seq.push(stat.body); - else push_seq(), ret.push(stat); + function reduce_iife(tw, descend, compressor) { + var fn = this; + fn.inlined = false; + var iife = tw.parent(); + var sequential = !is_async(fn) && !is_generator(fn); + var hit = !sequential; + var aborts = false; + fn.walk(new TreeWalker(function(node) { + if (hit) return aborts = true; + if (node instanceof AST_Return) return hit = true; + if (node instanceof AST_Scope && node !== fn) return true; + })); + if (aborts) push(tw, sequential); + reset_variables(tw, compressor, fn); + // Virtually turn IIFE parameters into variable definitions: + // (function(a,b) {...})(c,d) ---> (function() {var a=c,b=d; ...})() + // So existing transformation rules can work on them. + var safe = !fn.uses_arguments || tw.has_directive("use strict"); + fn.argnames.forEach(function(argname, i) { + var value = iife.args[i]; + scan_declaration(tw, compressor, argname, function() { + var j = fn.argnames.indexOf(argname); + var arg = j < 0 ? value : iife.args[j]; + if (arg instanceof AST_Sequence && arg.expressions.length < 2) arg = arg.expressions[0]; + return arg || make_node(AST_Undefined, iife); + }, visit); }); - push_seq(); - ret = sequencesize_2(ret, compressor); - CHANGED = ret.length != statements.length; - return ret; - }; + var rest = fn.rest, fixed_node; + if (rest) scan_declaration(tw, compressor, rest, compressor.option("rests") && function() { + if (fn.rest !== rest) return rest; + if (!fixed_node) fixed_node = make_node(AST_Array, fn, {}); + fixed_node.elements = iife.args.slice(fn.argnames.length); + return fixed_node; + }, visit); + walk_lambda(fn, tw); + var safe_ids = tw.safe_ids; + pop_scope(tw, fn); + if (!aborts) tw.safe_ids = safe_ids; + return true; - function sequencesize_2(statements, compressor) { - function cons_seq(right) { - ret.pop(); - var left = prev.body; - if (left instanceof AST_Seq) { - left.add(right); + function visit(node, fixed) { + var d = node.definition(); + if (!d.first_decl && d.references.length == 0) d.first_decl = node; + if (fixed && safe && d.fixed === undefined) { + mark(tw, d); + tw.loop_ids[d.id] = tw.in_loop; + d.fixed = fixed; + d.fixed.assigns = [ node ]; } else { - left = AST_Seq.cons(left, right); - } - return left.transform(compressor); - }; - var ret = [], prev = null; - statements.forEach(function(stat){ - if (prev) { - if (stat instanceof AST_For) { - var opera = {}; - try { - prev.body.walk(new TreeWalker(function(node){ - if (node instanceof AST_Binary && node.operator == "in") - throw opera; - })); - if (stat.init && !(stat.init instanceof AST_Definitions)) { - stat.init = cons_seq(stat.init); - } - else if (!stat.init) { - stat.init = prev.body; - ret.pop(); - } - } catch(ex) { - if (ex !== opera) throw ex; - } - } - else if (stat instanceof AST_If) { - stat.condition = cons_seq(stat.condition); - } - else if (stat instanceof AST_With) { - stat.expression = cons_seq(stat.expression); - } - else if (stat instanceof AST_Exit && stat.value) { - stat.value = cons_seq(stat.value); - } - else if (stat instanceof AST_Exit) { - stat.value = cons_seq(make_node(AST_Undefined, stat)); - } - else if (stat instanceof AST_Switch) { - stat.expression = cons_seq(stat.expression); - } + d.fixed = false; } - ret.push(stat); - prev = stat instanceof AST_SimpleStatement ? stat : null; - }); - return ret; - }; + } + } - function join_consecutive_vars(statements, compressor) { - var prev = null; - return statements.reduce(function(a, stat){ - if (stat instanceof AST_Definitions && prev && prev.TYPE == stat.TYPE) { - prev.definitions = prev.definitions.concat(stat.definitions); - CHANGED = true; - } - else if (stat instanceof AST_For - && prev instanceof AST_Definitions - && (!stat.init || stat.init.TYPE == prev.TYPE)) { - CHANGED = true; - a.pop(); - if (stat.init) { - stat.init.definitions = prev.definitions.concat(stat.init.definitions); - } else { - stat.init = prev; - } - a.push(stat); - prev = stat; + def(AST_Assign, function(tw, descend, compressor) { + var node = this; + var left = node.left; + var right = node.right; + var ld = left instanceof AST_SymbolRef && left.definition(); + var scan = ld || left instanceof AST_Destructured; + switch (node.operator) { + case "=": + if (left.equals(right) && !left.has_side_effects(compressor)) { + right.walk(tw); + walk_prop(left); + node.redundant = true; + return true; } - else { - prev = stat; - a.push(stat); + if (ld && right instanceof AST_LambdaExpression) { + walk_assign(); + right.parent_scope.resolve().fn_defs.push(right); + right.safe_ids = null; + if (!ld.fixed || !node.write_only) mark_fn_def(tw, ld, right); + return true; } - return a; - }, []); - }; + if (scan) { + right.walk(tw); + walk_assign(); + return true; + } + mark_assignment_to_arguments(left); + return; + case "&&=": + case "||=": + case "??=": + var lazy = true; + default: + if (!scan) { + mark_assignment_to_arguments(left); + return walk_lazy(); + } + ld.assignments++; + var fixed = ld.fixed; + if (is_modified(compressor, tw, node, node, 0)) { + ld.fixed = false; + return walk_lazy(); + } + var safe = safe_to_read(tw, ld); + if (lazy) push(tw, true); + right.walk(tw); + if (lazy) pop(tw); + if (safe && !left.in_arg && safe_to_assign(tw, ld)) { + push_ref(ld, left); + mark(tw, ld); + if (ld.single_use) ld.single_use = false; + left.fixed = ld.fixed = function() { + return make_node(AST_Binary, node, { + operator: node.operator.slice(0, -1), + left: make_ref(left, fixed), + right: node.right, + }); + }; + left.fixed.assigns = !fixed || !fixed.assigns ? [ ld.orig[0] ] : fixed.assigns.slice(); + left.fixed.assigns.push(node); + left.fixed.to_binary = replace_ref(function(node) { + return node.left; + }, fixed); + } else { + left.walk(tw); + ld.fixed = false; + } + return true; + } - function negate_iifes(statements, compressor) { - statements.forEach(function(stat){ - if (stat instanceof AST_SimpleStatement) { - stat.body = (function transform(thing) { - return thing.transform(new TreeTransformer(function(node){ - if (node instanceof AST_Call && node.expression instanceof AST_Function) { - return make_node(AST_UnaryPrefix, node, { - operator: "!", - expression: node - }); - } - else if (node instanceof AST_Call) { - node.expression = transform(node.expression); - } - else if (node instanceof AST_Seq) { - node.car = transform(node.car); - } - else if (node instanceof AST_Conditional) { - var expr = transform(node.condition); - if (expr !== node.condition) { - // it has been negated, reverse - node.condition = expr; - var tmp = node.consequent; - node.consequent = node.alternative; - node.alternative = tmp; - } - } - return node; - })); - })(stat.body); + function walk_prop(lhs) { + reset_flags(lhs); + if (lhs instanceof AST_Dot) { + walk_prop(lhs.expression); + } else if (lhs instanceof AST_Sub) { + walk_prop(lhs.expression); + lhs.property.walk(tw); + } else if (lhs instanceof AST_SymbolRef) { + var d = lhs.definition(); + push_ref(d, lhs); + if (d.fixed) { + lhs.fixed = d.fixed; + if (lhs.fixed.assigns) { + lhs.fixed.assigns.push(node); + } else { + lhs.fixed.assigns = [ node ]; + } + } + } else { + lhs.walk(tw); } - }); - }; + } - }; + function walk_assign() { + var recursive = ld && recursive_ref(tw, ld); + var modified = is_modified(compressor, tw, node, right, 0, is_immutable(right), recursive); + scan_declaration(tw, compressor, left, function() { + return node.right; + }, function(sym, fixed, walk) { + if (!(sym instanceof AST_SymbolRef)) { + mark_assignment_to_arguments(sym); + walk(); + return; + } + var d = sym.definition(); + d.assignments++; + if (!fixed || sym.in_arg || !safe_to_assign(tw, d)) { + walk(); + d.fixed = false; + } else { + push_ref(d, sym); + mark(tw, d); + if (left instanceof AST_Destructured + || d.orig.length == 1 && d.orig[0] instanceof AST_SymbolDefun) { + d.single_use = false; + } + tw.loop_ids[d.id] = tw.in_loop; + d.fixed = modified ? 0 : fixed; + sym.fixed = fixed; + sym.fixed.assigns = [ node ]; + mark_escaped(tw, d, sym.scope, node, right, 0, 1); + } + }); + } - function extract_declarations_from_unreachable_code(compressor, stat, target) { - compressor.warn("Dropping unreachable code [{file}:{line},{col}]", stat.start); - stat.walk(new TreeWalker(function(node){ - if (node instanceof AST_Definitions) { - compressor.warn("Declarations in unreachable code! [{file}:{line},{col}]", node.start); - node.remove_initializers(); - target.push(node); + function walk_lazy() { + if (!lazy) return; + left.walk(tw); + push(tw, true); + right.walk(tw); + pop(tw); return true; } - if (node instanceof AST_Defun) { - target.push(node); + }); + def(AST_Binary, function(tw) { + if (!lazy_op[this.operator]) return; + this.left.walk(tw); + push(tw, true); + this.right.walk(tw); + pop(tw); + return true; + }); + def(AST_BlockScope, function(tw, descend, compressor) { + reset_block_variables(tw, compressor, this); + }); + def(AST_Call, function(tw, descend) { + var node = this; + var exp = node.expression; + if (exp instanceof AST_LambdaExpression) { + var iife = is_iife_single(node); + node.args.forEach(function(arg) { + arg.walk(tw); + if (arg instanceof AST_Spread) iife = false; + }); + if (iife) exp.reduce_vars = reduce_iife; + exp.walk(tw); + if (iife) delete exp.reduce_vars; return true; } - if (node instanceof AST_Scope) { - return true; + if (node.TYPE == "Call") switch (tw.in_boolean_context()) { + case "d": + var drop = true; + case true: + mark_refs(exp, drop); } - })); - }; - - /* -----[ boolean/negation helpers ]----- */ - - // methods to determine whether an expression has a boolean result type - (function (def){ - var unary_bool = [ "!", "delete" ]; - var binary_bool = [ "in", "instanceof", "==", "!=", "===", "!==", "<", "<=", ">=", ">" ]; - def(AST_Node, function(){ return false }); - def(AST_UnaryPrefix, function(){ - return member(this.operator, unary_bool); - }); - def(AST_Binary, function(){ - return member(this.operator, binary_bool) || - ( (this.operator == "&&" || this.operator == "||") && - this.left.is_boolean() && this.right.is_boolean() ); - }); - def(AST_Conditional, function(){ - return this.consequent.is_boolean() && this.alternative.is_boolean(); - }); - def(AST_Assign, function(){ - return this.operator == "=" && this.right.is_boolean(); - }); - def(AST_Seq, function(){ - return this.cdr.is_boolean(); - }); - def(AST_True, function(){ return true }); - def(AST_False, function(){ return true }); - })(function(node, func){ - node.DEFMETHOD("is_boolean", func); - }); + exp.walk(tw); + var optional = node.optional; + if (optional) push(tw, true); + node.args.forEach(function(arg) { + arg.walk(tw); + }); + if (optional) pop(tw); + var fixed = exp instanceof AST_SymbolRef && exp.fixed_value(); + if (fixed instanceof AST_Lambda) { + mark_fn_def(tw, exp.definition(), fixed); + } else { + tw.find_parent(AST_Scope).may_call_this(); + } + return true; - // methods to determine if an expression has a string result type - (function (def){ - def(AST_Node, function(){ return false }); - def(AST_String, function(){ return true }); - def(AST_UnaryPrefix, function(){ - return this.operator == "typeof"; + function mark_refs(node, drop) { + if (node instanceof AST_Assign) { + if (node.operator != "=") return; + mark_refs(node.left, drop); + mark_refs(node.right, drop); + } else if (node instanceof AST_Binary) { + if (!lazy_op[node.operator]) return; + mark_refs(node.left, drop); + mark_refs(node.right, drop); + } else if (node instanceof AST_Conditional) { + mark_refs(node.consequent, drop); + mark_refs(node.alternative, drop); + } else if (node instanceof AST_SymbolRef) { + var def = node.definition(); + def.bool_return++; + if (drop) def.drop_return++; + } + } }); - def(AST_Binary, function(compressor){ - return this.operator == "+" && - (this.left.is_string(compressor) || this.right.is_string(compressor)); + def(AST_Class, function(tw, descend, compressor) { + var node = this; + reset_block_variables(tw, compressor, node); + if (node.extends) node.extends.walk(tw); + var props = node.properties.filter(function(prop) { + reset_flags(prop); + if (prop.key instanceof AST_Node) { + tw.push(prop); + prop.key.walk(tw); + tw.pop(); + } + return prop.value; + }); + if (node.name) { + var d = node.name.definition(); + var parent = tw.parent(); + if (parent instanceof AST_ExportDeclaration || parent instanceof AST_ExportDefault) d.single_use = false; + if (safe_to_assign(tw, d, true)) { + mark(tw, d); + tw.loop_ids[d.id] = tw.in_loop; + d.fixed = function() { + return node; + }; + d.fixed.assigns = [ node ]; + if (!is_safe_lexical(d)) d.single_use = false; + } else { + d.fixed = false; + } + } + props.forEach(function(prop) { + tw.push(prop); + if (!prop.static || is_static_field_or_init(prop) && prop.value.contains_this()) { + push(tw); + prop.value.walk(tw); + pop(tw); + } else { + prop.value.walk(tw); + } + tw.pop(); + }); + return true; }); - def(AST_Assign, function(compressor){ - return (this.operator == "=" || this.operator == "+=") && this.right.is_string(compressor); + def(AST_ClassInitBlock, function(tw, descend, compressor) { + var node = this; + push(tw, true); + reset_variables(tw, compressor, node); + descend(); + pop_scope(tw, node); + return true; }); - def(AST_Seq, function(compressor){ - return this.cdr.is_string(compressor); + def(AST_Conditional, function(tw) { + this.condition.walk(tw); + push(tw, true); + this.consequent.walk(tw); + pop(tw); + push(tw, true); + this.alternative.walk(tw); + pop(tw); + return true; }); - def(AST_Conditional, function(compressor){ - return this.consequent.is_string(compressor) && this.alternative.is_string(compressor); + def(AST_DefaultValue, function(tw) { + push(tw, true); + this.value.walk(tw); + pop(tw); + this.name.walk(tw); + return true; }); - def(AST_Call, function(compressor){ - return compressor.option("unsafe") - && this.expression instanceof AST_SymbolRef - && this.expression.name == "String" - && this.expression.undeclared(); + def(AST_Do, function(tw) { + var save_loop = tw.in_loop; + tw.in_loop = this; + push(tw); + this.body.walk(tw); + if (has_loop_control(this, tw.parent())) { + pop(tw); + push(tw); + } + this.condition.walk(tw); + pop(tw); + tw.in_loop = save_loop; + return true; }); - })(function(node, func){ - node.DEFMETHOD("is_string", func); - }); - - function best_of(ast1, ast2) { - return ast1.print_to_string().length > - ast2.print_to_string().length - ? ast2 : ast1; - }; - - // methods to evaluate a constant expression - (function (def){ - // The evaluate method returns an array with one or two - // elements. If the node has been successfully reduced to a - // constant, then the second element tells us the value; - // otherwise the second element is missing. The first element - // of the array is always an AST_Node descendant; if - // evaluation was successful it's a node that represents the - // constant; otherwise it's the original or a replacement node. - AST_Node.DEFMETHOD("evaluate", function(compressor){ - if (!compressor.option("evaluate")) return [ this ]; - try { - var val = this._eval(compressor); - return [ best_of(make_node_from_constant(compressor, val, this), this), val ]; - } catch(ex) { - if (ex !== def) throw ex; - return [ this ]; + def(AST_For, function(tw, descend, compressor) { + var node = this; + reset_block_variables(tw, compressor, node); + if (node.init) node.init.walk(tw); + var save_loop = tw.in_loop; + tw.in_loop = node; + push(tw); + if (node.condition) node.condition.walk(tw); + node.body.walk(tw); + if (node.step) { + if (has_loop_control(node, tw.parent())) { + pop(tw); + push(tw); + } + node.step.walk(tw); } + pop(tw); + tw.in_loop = save_loop; + return true; }); - def(AST_Statement, function(){ - throw new Error(string_template("Cannot evaluate a statement [{file}:{line},{col}]", this.start)); + def(AST_ForEnumeration, function(tw, descend, compressor) { + var node = this; + reset_block_variables(tw, compressor, node); + node.object.walk(tw); + var save_loop = tw.in_loop; + tw.in_loop = node; + push(tw); + var init = node.init; + if (init instanceof AST_Definitions) { + init.definitions[0].name.mark_symbol(function(node) { + if (node instanceof AST_SymbolDeclaration) { + var def = node.definition(); + def.assignments++; + def.fixed = false; + } + }, tw); + } else if (init instanceof AST_Destructured || init instanceof AST_SymbolRef) { + init.mark_symbol(function(node) { + if (node instanceof AST_SymbolRef) { + var def = node.definition(); + push_ref(def, node); + def.assignments++; + if (!node.is_immutable()) def.fixed = false; + } + }, tw); + } else { + init.walk(tw); + } + node.body.walk(tw); + pop(tw); + tw.in_loop = save_loop; + return true; }); - def(AST_Function, function(){ - // XXX: AST_Function inherits from AST_Scope, which itself - // inherits from AST_Statement; however, an AST_Function - // isn't really a statement. This could byte in other - // places too. :-( Wish JS had multiple inheritance. - throw def; + def(AST_If, function(tw) { + this.condition.walk(tw); + push(tw, true); + this.body.walk(tw); + pop(tw); + if (this.alternative) { + push(tw, true); + this.alternative.walk(tw); + pop(tw); + } + return true; }); - function ev(node, compressor) { - if (!compressor) throw new Error("Compressor must be passed"); - - return node._eval(compressor); - }; - def(AST_Node, function(){ - throw def; // not constant + def(AST_LabeledStatement, function(tw) { + push(tw, true); + this.body.walk(tw); + pop(tw); + return true; }); - def(AST_Constant, function(){ - return this.getValue(); + def(AST_Lambda, function(tw, descend, compressor) { + var fn = this; + if (!safe_to_visit(tw, fn)) return true; + if (!push_uniq(tw.fn_visited, fn)) return true; + fn.inlined = false; + push(tw); + reset_variables(tw, compressor, fn); + descend(); + pop_scope(tw, fn); + if (fn.name) mark_escaped(tw, fn.name.definition(), fn, fn.name, fn, 0, 1); + return true; }); - def(AST_UnaryPrefix, function(compressor){ - var e = this.expression; - switch (this.operator) { - case "!": return !ev(e, compressor); - case "typeof": - // Function would be evaluated to an array and so typeof would - // incorrectly return 'object'. Hence making is a special case. - if (e instanceof AST_Function) return typeof function(){}; - - e = ev(e, compressor); - - // typeof returns "object" or "function" on different platforms - // so cannot evaluate reliably - if (e instanceof RegExp) throw def; - - return typeof e; - case "void": return void ev(e, compressor); - case "~": return ~ev(e, compressor); - case "-": - e = ev(e, compressor); - if (e === 0) throw def; - return -e; - case "+": return +ev(e, compressor); - } - throw def; + def(AST_LambdaDefinition, function(tw, descend, compressor) { + var fn = this; + var def = fn.name.definition(); + var parent = tw.parent(); + if (parent instanceof AST_ExportDeclaration || parent instanceof AST_ExportDefault) def.single_use = false; + if (!safe_to_visit(tw, fn)) return true; + if (!push_uniq(tw.fn_visited, fn)) return true; + fn.inlined = false; + push(tw); + reset_variables(tw, compressor, fn); + descend(); + pop_scope(tw, fn); + return true; }); - def(AST_Binary, function(c){ - var left = this.left, right = this.right; - switch (this.operator) { - case "&&" : return ev(left, c) && ev(right, c); - case "||" : return ev(left, c) || ev(right, c); - case "|" : return ev(left, c) | ev(right, c); - case "&" : return ev(left, c) & ev(right, c); - case "^" : return ev(left, c) ^ ev(right, c); - case "+" : return ev(left, c) + ev(right, c); - case "*" : return ev(left, c) * ev(right, c); - case "/" : return ev(left, c) / ev(right, c); - case "%" : return ev(left, c) % ev(right, c); - case "-" : return ev(left, c) - ev(right, c); - case "<<" : return ev(left, c) << ev(right, c); - case ">>" : return ev(left, c) >> ev(right, c); - case ">>>" : return ev(left, c) >>> ev(right, c); - case "==" : return ev(left, c) == ev(right, c); - case "===" : return ev(left, c) === ev(right, c); - case "!=" : return ev(left, c) != ev(right, c); - case "!==" : return ev(left, c) !== ev(right, c); - case "<" : return ev(left, c) < ev(right, c); - case "<=" : return ev(left, c) <= ev(right, c); - case ">" : return ev(left, c) > ev(right, c); - case ">=" : return ev(left, c) >= ev(right, c); - case "in" : return ev(left, c) in ev(right, c); - case "instanceof" : return ev(left, c) instanceof ev(right, c); - } - throw def; - }); - def(AST_Conditional, function(compressor){ - return ev(this.condition, compressor) - ? ev(this.consequent, compressor) - : ev(this.alternative, compressor); - }); - def(AST_SymbolRef, function(compressor){ - var d = this.definition(); - if (d && d.constant && d.init) return ev(d.init, compressor); - throw def; + def(AST_Sub, function(tw) { + if (!this.optional) return; + this.expression.walk(tw); + push(tw, true); + this.property.walk(tw); + pop(tw); + return true; }); - def(AST_Dot, function(compressor){ - if (compressor.option("unsafe") && this.property == "length") { - var str = ev(this.expression, compressor); - if (typeof str == "string") - return str.length; - } - throw def; + def(AST_Switch, function(tw, descend, compressor) { + var node = this; + reset_block_variables(tw, compressor, node); + node.expression.walk(tw); + var first = true; + node.body.forEach(function(branch) { + if (branch instanceof AST_Default) return; + branch.expression.walk(tw); + if (first) { + first = false; + push(tw, true); + } + }) + if (!first) pop(tw); + walk_body(node, tw); + return true; }); - })(function(node, func){ - node.DEFMETHOD("_eval", func); - }); - - // method to negate an expression - (function(def){ - function basic_negation(exp) { - return make_node(AST_UnaryPrefix, exp, { - operator: "!", - expression: exp - }); - }; - def(AST_Node, function(){ - return basic_negation(this); + def(AST_SwitchBranch, function(tw) { + push(tw, true); + walk_body(this, tw); + pop(tw); + return true; }); - def(AST_Statement, function(){ - throw new Error("Cannot negate a statement"); + def(AST_SymbolCatch, function() { + var d = this.definition(); + if (!d.first_decl && d.references.length == 0) d.first_decl = this; + d.fixed = false; }); - def(AST_Function, function(){ - return basic_negation(this); + def(AST_SymbolDeclaration, function() { + var d = this.definition(); + if (!d.first_decl && d.references.length == 0) d.first_decl = this; }); - def(AST_UnaryPrefix, function(){ - if (this.operator == "!") - return this.expression; - return basic_negation(this); + def(AST_SymbolImport, function() { + var d = this.definition(); + d.first_decl = this; + d.fixed = false; }); - def(AST_Seq, function(compressor){ - var self = this.clone(); - self.cdr = self.cdr.negate(compressor); - return self; + def(AST_SymbolRef, function(tw, descend, compressor) { + var ref = this; + var d = ref.definition(); + var fixed = d.fixed || d.last_ref && d.last_ref.fixed; + push_ref(d, ref); + if (d.references.length == 1 && !d.fixed && d.orig[0] instanceof AST_SymbolDefun) { + tw.loop_ids[d.id] = tw.in_loop; + } + var recursive = recursive_ref(tw, d); + if (recursive) recursive.enclosed.forEach(function(def) { + if (d === def) return; + if (def.scope.resolve() === recursive) return; + var assigns = def.fixed && def.fixed.assigns; + if (!assigns) return; + if (assigns[assigns.length - 1] instanceof AST_VarDef) return; + var safe = tw.safe_ids[def.id]; + if (!safe) return; + safe.assign = true; + }); + if (d.single_use == "m" && d.fixed) { + d.fixed = 0; + d.single_use = false; + } + switch (d.fixed) { + case 0: + if (!safe_to_read(tw, d)) d.fixed = false; + case false: + var redef = d.redefined(); + if (redef && cross_scope(d.scope, ref.scope)) redef.single_use = false; + break; + case undefined: + d.fixed = false; + break; + default: + if (!safe_to_read(tw, d)) { + d.fixed = false; + break; + } + if (ref.in_arg && d.orig[0] instanceof AST_SymbolLambda) ref.fixed = d.scope; + var value = ref.fixed_value(); + if (recursive) { + d.recursive_refs++; + } else if (value && ref_once(compressor, d)) { + d.in_loop = tw.loop_ids[d.id] !== tw.in_loop; + d.single_use = is_lambda(value) + && !value.pinned() + && (!d.in_loop || tw.parent() instanceof AST_Call) + || !d.in_loop + && d.scope === ref.scope.resolve() + && value.is_constant_expression(); + } else { + d.single_use = false; + } + if (is_modified(compressor, tw, ref, value, 0, is_immutable(value), recursive)) { + if (d.single_use) { + d.single_use = "m"; + } else { + d.fixed = 0; + } + } + if (d.fixed && tw.loop_ids[d.id] !== tw.in_loop) d.cross_loop = true; + mark_escaped(tw, d, ref.scope, ref, value, 0, 1); + break; + } + if (!ref.fixed) ref.fixed = d.fixed === 0 ? fixed : d.fixed; + if (!value && fixed) value = fixed instanceof AST_Node ? fixed : fixed(); + if (!(value instanceof AST_Lambda)) return; + if (d.fixed) { + var parent = tw.parent(); + if (parent instanceof AST_Call && parent.expression === ref) return; + } + mark_fn_def(tw, d, value); }); - def(AST_Conditional, function(compressor){ - var self = this.clone(); - self.consequent = self.consequent.negate(compressor); - self.alternative = self.alternative.negate(compressor); - return best_of(basic_negation(this), self); + def(AST_Template, function(tw, descend) { + var node = this; + var tag = node.tag; + if (!tag) return; + if (tag instanceof AST_LambdaExpression) { + node.expressions.forEach(function(exp) { + exp.walk(tw); + }); + tag.walk(tw); + return true; + } + tag.walk(tw); + node.expressions.forEach(function(exp) { + exp.walk(tw); + }); + var fixed = tag instanceof AST_SymbolRef && tag.fixed_value(); + if (fixed instanceof AST_Lambda) { + mark_fn_def(tw, tag.definition(), fixed); + } else { + tw.find_parent(AST_Scope).may_call_this(); + } + return true; }); - def(AST_Binary, function(compressor){ - var self = this.clone(), op = this.operator; - if (compressor.option("unsafe_comps")) { - switch (op) { - case "<=" : self.operator = ">" ; return self; - case "<" : self.operator = ">=" ; return self; - case ">=" : self.operator = "<" ; return self; - case ">" : self.operator = "<=" ; return self; + def(AST_Toplevel, function(tw, descend, compressor) { + var node = this; + node.globals.each(function(def) { + reset_def(tw, compressor, def); + }); + push(tw, true); + reset_variables(tw, compressor, node); + descend(); + pop_scope(tw, node); + return true; + }); + def(AST_Try, function(tw, descend, compressor) { + var node = this; + reset_block_variables(tw, compressor, node); + push(tw, true); + walk_body(node, tw); + pop(tw); + if (node.bcatch) { + push(tw, true); + node.bcatch.walk(tw); + pop(tw); + } + if (node.bfinally) node.bfinally.walk(tw); + return true; + }); + def(AST_Unary, function(tw, descend) { + var node = this; + if (!UNARY_POSTFIX[node.operator]) return; + var exp = node.expression; + if (!(exp instanceof AST_SymbolRef)) { + mark_assignment_to_arguments(exp); + return; + } + var d = exp.definition(); + d.assignments++; + var fixed = d.fixed; + if (safe_to_read(tw, d) && !exp.in_arg && safe_to_assign(tw, d)) { + push_ref(d, exp); + mark(tw, d); + if (d.single_use) d.single_use = false; + d.fixed = function() { + return make_node(AST_Binary, node, { + operator: node.operator.slice(0, -1), + left: make_node(AST_UnaryPrefix, node, { + operator: "+", + expression: make_ref(exp, fixed), + }), + right: make_node(AST_Number, node, { value: 1 }), + }); + }; + d.fixed.assigns = fixed && fixed.assigns ? fixed.assigns.slice() : []; + d.fixed.assigns.push(node); + if (node instanceof AST_UnaryPrefix) { + exp.fixed = d.fixed; + } else { + exp.fixed = function() { + return make_node(AST_UnaryPrefix, node, { + operator: "+", + expression: make_ref(exp, fixed), + }); + }; + exp.fixed.assigns = fixed && fixed.assigns; + exp.fixed.to_prefix = replace_ref(function(node) { + return node.expression; + }, d.fixed); } + } else { + exp.walk(tw); + d.fixed = false; } - switch (op) { - case "==" : self.operator = "!="; return self; - case "!=" : self.operator = "=="; return self; - case "===": self.operator = "!=="; return self; - case "!==": self.operator = "==="; return self; - case "&&": - self.operator = "||"; - self.left = self.left.negate(compressor); - self.right = self.right.negate(compressor); - return best_of(basic_negation(this), self); - case "||": - self.operator = "&&"; - self.left = self.left.negate(compressor); - self.right = self.right.negate(compressor); - return best_of(basic_negation(this), self); + return true; + }); + def(AST_VarDef, function(tw, descend, compressor) { + var node = this; + var value = node.value; + if (value instanceof AST_LambdaExpression && node.name instanceof AST_SymbolDeclaration) { + walk_defn(); + value.parent_scope.resolve().fn_defs.push(value); + value.safe_ids = null; + var ld = node.name.definition(); + if (!ld.fixed) mark_fn_def(tw, ld, value); + } else if (value) { + value.walk(tw); + walk_defn(); + } else if (tw.parent() instanceof AST_Let) { + walk_defn(); + } else { + node.name.walk(tw); + } + return true; + + function walk_defn() { + scan_declaration(tw, compressor, node.name, function() { + return node.value || make_node(AST_Undefined, node); + }, function(name, fixed) { + var d = name.definition(); + if (!d.first_decl && d.references.length == 0) d.first_decl = name; + if (fixed && safe_to_assign(tw, d, true)) { + mark(tw, d); + tw.loop_ids[d.id] = tw.in_loop; + d.fixed = fixed; + d.fixed.assigns = [ node ]; + if (name instanceof AST_SymbolConst && d.redefined() + || !(can_drop_symbol(name) || is_safe_lexical(d))) { + d.single_use = false; + } + } else { + d.fixed = false; + } + }); } - return basic_negation(this); }); - })(function(node, func){ - node.DEFMETHOD("negate", function(compressor){ - return func.call(this, compressor); + def(AST_While, function(tw, descend) { + var save_loop = tw.in_loop; + tw.in_loop = this; + push(tw); + descend(); + pop(tw); + tw.in_loop = save_loop; + return true; }); + })(function(node, func) { + node.DEFMETHOD("reduce_vars", func); }); - // determine if expression has side effects - (function(def){ - def(AST_Node, function(compressor){ return true }); + function reset_flags(node) { + node._squeezed = false; + node._optimized = false; + if (node instanceof AST_BlockScope) node._var_names = undefined; + if (node instanceof AST_SymbolRef) node.fixed = undefined; + } + + AST_Toplevel.DEFMETHOD("reset_opt_flags", function(compressor) { + var tw = new TreeWalker(compressor.option("reduce_vars") ? function(node, descend) { + reset_flags(node); + return node.reduce_vars(tw, descend, compressor); + } : reset_flags); + // Flow control for visiting lambda definitions + tw.fn_scanning = null; + tw.fn_visited = []; + // Record the loop body in which `AST_SymbolDeclaration` is first encountered + tw.in_loop = null; + tw.loop_ids = Object.create(null); + // Stack of look-up tables to keep track of whether a `SymbolDef` has been + // properly assigned before use: + // - `push()` & `pop()` when visiting conditional branches + // - backup & restore via `save_ids` when visiting out-of-order sections + tw.safe_ids = Object.create(null); + tw.safe_ids.seq = {}; + this.walk(tw); + }); - def(AST_EmptyStatement, function(compressor){ return false }); - def(AST_Constant, function(compressor){ return false }); - def(AST_This, function(compressor){ return false }); + AST_Symbol.DEFMETHOD("fixed_value", function(ref_only) { + var def = this.definition(); + var fixed = def.fixed; + if (fixed) { + if (this.fixed) fixed = this.fixed; + return (fixed instanceof AST_Node ? fixed : fixed()).tail_node(); + } + fixed = fixed === 0 && this.fixed; + if (!fixed) return fixed; + var value = (fixed instanceof AST_Node ? fixed : fixed()).tail_node(); + if (ref_only && def.escaped.depth != 1 && is_object(value, true)) return value; + if (value.is_constant()) return value; + }); - def(AST_Call, function(compressor){ - var pure = compressor.option("pure_funcs"); - if (!pure) return true; - return pure.indexOf(this.expression.print_to_string()) < 0; - }); + AST_SymbolRef.DEFMETHOD("is_immutable", function() { + var def = this.redef || this.definition(); + if (!(def.orig[0] instanceof AST_SymbolLambda)) return false; + if (def.orig.length == 1) return true; + if (!this.in_arg) return false; + return !(def.orig[1] instanceof AST_SymbolFunarg); + }); - def(AST_Block, function(compressor){ - for (var i = this.body.length; --i >= 0;) { - if (this.body[i].has_side_effects(compressor)) - return true; + AST_Node.DEFMETHOD("convert_symbol", noop); + function convert_destructured(type, process) { + return this.transform(new TreeTransformer(function(node, descend) { + if (node instanceof AST_DefaultValue) { + node = node.clone(); + node.name = node.name.transform(this); + return node; } - return false; - }); + if (node instanceof AST_Destructured) { + node = node.clone(); + descend(node, this); + return node; + } + if (node instanceof AST_DestructuredKeyVal) { + node = node.clone(); + node.value = node.value.transform(this); + return node; + } + return node.convert_symbol(type, process); + })); + } + AST_DefaultValue.DEFMETHOD("convert_symbol", convert_destructured); + AST_Destructured.DEFMETHOD("convert_symbol", convert_destructured); + function convert_symbol(type, process) { + var node = make_node(type, this); + return process(node, this) || node; + } + AST_SymbolDeclaration.DEFMETHOD("convert_symbol", convert_symbol); + AST_SymbolRef.DEFMETHOD("convert_symbol", convert_symbol); - def(AST_SimpleStatement, function(compressor){ - return this.body.has_side_effects(compressor); - }); - def(AST_Defun, function(compressor){ return true }); - def(AST_Function, function(compressor){ return false }); - def(AST_Binary, function(compressor){ - return this.left.has_side_effects(compressor) - || this.right.has_side_effects(compressor); + function process_to_assign(ref) { + var def = ref.definition(); + def.assignments++; + def.references.push(ref); + } + + function mark_destructured(process, tw) { + var marker = new TreeWalker(function(node) { + if (node instanceof AST_DefaultValue) { + node.value.walk(tw); + node.name.walk(marker); + return true; + } + if (node instanceof AST_DestructuredKeyVal) { + if (node.key instanceof AST_Node) node.key.walk(tw); + node.value.walk(marker); + return true; + } + return process(node); }); - def(AST_Assign, function(compressor){ return true }); - def(AST_Conditional, function(compressor){ - return this.condition.has_side_effects(compressor) - || this.consequent.has_side_effects(compressor) - || this.alternative.has_side_effects(compressor); + this.walk(marker); + } + AST_DefaultValue.DEFMETHOD("mark_symbol", mark_destructured); + AST_Destructured.DEFMETHOD("mark_symbol", mark_destructured); + function mark_symbol(process) { + return process(this); + } + AST_SymbolDeclaration.DEFMETHOD("mark_symbol", mark_symbol); + AST_SymbolRef.DEFMETHOD("mark_symbol", mark_symbol); + + AST_Node.DEFMETHOD("match_symbol", function(predicate) { + return predicate(this); + }); + function match_destructured(predicate, ignore_side_effects) { + var found = false; + var tw = new TreeWalker(function(node) { + if (found) return true; + if (node instanceof AST_DefaultValue) { + if (!ignore_side_effects) return found = true; + node.name.walk(tw); + return true; + } + if (node instanceof AST_DestructuredKeyVal) { + if (!ignore_side_effects && node.key instanceof AST_Node) return found = true; + node.value.walk(tw); + return true; + } + if (predicate(node)) return found = true; }); - def(AST_Unary, function(compressor){ - return this.operator == "delete" - || this.operator == "++" - || this.operator == "--" - || this.expression.has_side_effects(compressor); + this.walk(tw); + return found; + } + AST_DefaultValue.DEFMETHOD("match_symbol", match_destructured); + AST_Destructured.DEFMETHOD("match_symbol", match_destructured); + + function in_async_generator(scope) { + return scope instanceof AST_AsyncGeneratorDefun || scope instanceof AST_AsyncGeneratorFunction; + } + + function find_scope(compressor) { + var level = 0, node = compressor.self(); + do { + if (node.variables) return node; + } while (node = compressor.parent(level++)); + } + + function find_try(compressor, level, node, scope, may_throw, sync) { + for (var parent; parent = compressor.parent(level++); node = parent) { + if (parent === scope) return false; + if (sync && parent instanceof AST_Lambda) { + if (parent.name || is_async(parent) || is_generator(parent)) return true; + } else if (parent instanceof AST_Try) { + if (parent.bfinally && parent.bfinally !== node) return true; + if (may_throw && parent.bcatch && parent.bcatch !== node) return true; + } + } + return false; + } + + var identifier_atom = makePredicate("Infinity NaN undefined"); + function is_lhs_read_only(lhs, compressor) { + if (lhs instanceof AST_Assign) { + if (lhs.operator != "=") return true; + if (lhs.right.tail_node().is_constant()) return true; + return is_lhs_read_only(lhs.left, compressor); + } + if (lhs instanceof AST_Atom) return true; + if (lhs instanceof AST_ObjectIdentity) return true; + if (lhs instanceof AST_PropAccess) { + if (lhs.property === "__proto__") return true; + lhs = lhs.expression; + if (lhs instanceof AST_SymbolRef) { + if (lhs.is_immutable()) return false; + lhs = lhs.fixed_value(); + } + if (!lhs) return true; + if (lhs.tail_node().is_constant()) return true; + return is_lhs_read_only(lhs, compressor); + } + if (lhs instanceof AST_SymbolRef) { + if (lhs.is_immutable()) return true; + var def = lhs.definition(); + return compressor.exposed(def) && identifier_atom[def.name]; + } + return false; + } + + function make_node(ctor, orig, props) { + if (props) { + props.start = orig.start; + props.end = orig.end; + } else { + props = orig; + } + return new ctor(props); + } + + function make_sequence(orig, expressions) { + if (expressions.length == 1) return expressions[0]; + return make_node(AST_Sequence, orig, { expressions: expressions.reduce(merge_sequence, []) }); + } + + function make_node_from_constant(val, orig) { + switch (typeof val) { + case "string": + return make_node(AST_String, orig, { value: val }); + case "number": + if (isNaN(val)) return make_node(AST_NaN, orig); + if (isFinite(val)) { + return 1 / val < 0 ? make_node(AST_UnaryPrefix, orig, { + operator: "-", + expression: make_node(AST_Number, orig, { value: -val }), + }) : make_node(AST_Number, orig, { value: val }); + } + return val < 0 ? make_node(AST_UnaryPrefix, orig, { + operator: "-", + expression: make_node(AST_Infinity, orig), + }) : make_node(AST_Infinity, orig); + case "boolean": + return make_node(val ? AST_True : AST_False, orig); + case "undefined": + return make_node(AST_Undefined, orig); + default: + if (val === null) { + return make_node(AST_Null, orig); + } + if (val instanceof RegExp) { + return make_node(AST_RegExp, orig, { value: val }); + } + throw new Error(string_template("Can't handle constant of type: {type}", { type: typeof val })); + } + } + + function needs_unbinding(val) { + return val instanceof AST_PropAccess + || is_undeclared_ref(val) && val.name == "eval"; + } + + // we shouldn't compress (1,func)(something) to + // func(something) because that changes the meaning of + // the func (becomes lexical instead of global). + function maintain_this_binding(parent, orig, val) { + var wrap = false; + if (parent.TYPE == "Call") { + wrap = parent.expression === orig && needs_unbinding(val); + } else if (parent instanceof AST_Template) { + wrap = parent.tag === orig && needs_unbinding(val); + } else if (parent instanceof AST_UnaryPrefix) { + wrap = parent.operator == "delete" + || parent.operator == "typeof" && is_undeclared_ref(val); + } + return wrap ? make_sequence(orig, [ make_node(AST_Number, orig, { value: 0 }), val ]) : val; + } + + function merge_expression(base, target) { + var fixed_by_id = new Dictionary(); + base.walk(new TreeWalker(function(node) { + if (!(node instanceof AST_SymbolRef)) return; + var def = node.definition(); + var fixed = node.fixed; + if (!fixed || !fixed_by_id.has(def.id)) { + fixed_by_id.set(def.id, fixed); + } else if (fixed_by_id.get(def.id) !== fixed) { + fixed_by_id.set(def.id, false); + } + })); + if (fixed_by_id.size() > 0) target.walk(new TreeWalker(function(node) { + if (!(node instanceof AST_SymbolRef)) return; + var def = node.definition(); + var fixed = node.fixed; + if (!fixed || !fixed_by_id.has(def.id)) return; + if (fixed_by_id.get(def.id) !== fixed) node.fixed = false; + })); + return target; + } + + function merge_sequence(array, node) { + if (node instanceof AST_Sequence) { + [].push.apply(array, node.expressions); + } else { + array.push(node); + } + return array; + } + + function is_lexical_definition(stat) { + return stat instanceof AST_Const || stat instanceof AST_DefClass || stat instanceof AST_Let; + } + + function safe_to_trim(stat) { + if (stat instanceof AST_LambdaDefinition) { + var def = stat.name.definition(); + var scope = stat.name.scope; + return def.scope === scope || all(def.references, function(ref) { + var s = ref.scope; + do { + if (s === scope) return true; + } while (s = s.parent_scope); + }); + } + return !is_lexical_definition(stat); + } + + function as_statement_array(thing) { + if (thing === null) return []; + if (thing instanceof AST_BlockStatement) return all(thing.body, safe_to_trim) ? thing.body : [ thing ]; + if (thing instanceof AST_EmptyStatement) return []; + if (is_statement(thing)) return [ thing ]; + throw new Error("Can't convert thing to statement array"); + } + + function is_empty(thing) { + if (thing === null) return true; + if (thing instanceof AST_EmptyStatement) return true; + if (thing instanceof AST_BlockStatement) return thing.body.length == 0; + return false; + } + + function has_declarations_only(block) { + return all(block.body, function(stat) { + return is_empty(stat) + || stat instanceof AST_Defun + || stat instanceof AST_Var && declarations_only(stat); }); - def(AST_SymbolRef, function(compressor){ - return this.global() && this.undeclared(); + } + + function loop_body(x) { + if (x instanceof AST_IterationStatement) { + return x.body instanceof AST_BlockStatement ? x.body : x; + } + return x; + } + + function is_iife_call(node) { + if (node.TYPE != "Call") return false; + do { + node = node.expression; + } while (node instanceof AST_PropAccess); + 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; + } + + var global_names = makePredicate("Array Boolean clearInterval clearTimeout console Date decodeURI decodeURIComponent encodeURI encodeURIComponent Error escape eval EvalError Function isFinite isNaN JSON Map Math Number parseFloat parseInt RangeError ReferenceError RegExp Object Set setInterval setTimeout String SyntaxError TypeError unescape URIError WeakMap WeakSet"); + AST_SymbolRef.DEFMETHOD("is_declared", function(compressor) { + return this.defined + || !this.definition().undeclared + || compressor.option("unsafe") && global_names[this.name]; + }); + + function is_static_field_or_init(prop) { + return prop.static && prop.value && (prop instanceof AST_ClassField || prop instanceof AST_ClassInit); + } + + function declarations_only(node) { + return all(node.definitions, function(var_def) { + return !var_def.value; }); - def(AST_Object, function(compressor){ - for (var i = this.properties.length; --i >= 0;) - if (this.properties[i].has_side_effects(compressor)) - return true; - return false; + } + + function is_declaration(stat, lexical) { + if (stat instanceof AST_DefClass) return lexical && !stat.extends && all(stat.properties, function(prop) { + if (prop.key instanceof AST_Node) return false; + return !is_static_field_or_init(prop); }); - def(AST_ObjectProperty, function(compressor){ - return this.value.has_side_effects(compressor); + if (stat instanceof AST_Definitions) return (lexical || stat instanceof AST_Var) && declarations_only(stat); + if (stat instanceof AST_ExportDeclaration) return is_declaration(stat.body, lexical); + if (stat instanceof AST_ExportDefault) return is_declaration(stat.body, lexical); + return stat instanceof AST_LambdaDefinition; + } + + function is_last_statement(body, stat) { + var index = body.lastIndexOf(stat); + if (index < 0) return false; + while (++index < body.length) { + if (!is_declaration(body[index], true)) return false; + } + return true; + } + + // Certain combination of unused name + side effect leads to invalid AST: + // https://github.com/mishoo/UglifyJS/issues/44 + // https://github.com/mishoo/UglifyJS/issues/1838 + // https://github.com/mishoo/UglifyJS/issues/3371 + // We fix it at this stage by moving the `var` outside the `for`. + function patch_for_init(node, in_list) { + var block; + if (node.init instanceof AST_BlockStatement) { + block = node.init; + node.init = block.body.pop(); + block.body.push(node); + } + if (node.init instanceof AST_Defun) { + if (!block) block = make_node(AST_BlockStatement, node, { body: [ node ] }); + block.body.splice(-1, 0, node.init); + node.init = null; + } else if (node.init instanceof AST_SimpleStatement) { + node.init = node.init.body; + } else if (is_empty(node.init)) { + node.init = null; + } + if (!block) return; + return in_list ? List.splice(block.body) : block; + } + + function tighten_body(statements, compressor) { + var in_lambda = last_of(compressor, function(node) { + return node instanceof AST_Lambda; }); - def(AST_Array, function(compressor){ - for (var i = this.elements.length; --i >= 0;) - if (this.elements[i].has_side_effects(compressor)) + var block_scope, iife_in_try, in_iife_single, in_loop, in_try, scope; + find_loop_scope_try(); + var changed, last_changed, max_iter = 10; + do { + last_changed = changed; + changed = 0; + if (eliminate_spurious_blocks(statements)) changed = 1; + if (!changed && last_changed == 1) break; + if (compressor.option("dead_code")) { + if (eliminate_dead_code(statements, compressor)) changed = 2; + if (!changed && last_changed == 2) break; + } + if (compressor.option("if_return")) { + if (handle_if_return(statements, compressor)) changed = 3; + if (!changed && last_changed == 3) break; + } + if (compressor.option("awaits") && compressor.option("side_effects")) { + if (trim_awaits(statements, compressor)) changed = 4; + if (!changed && last_changed == 4) break; + } + if (compressor.option("inline") >= 4) { + if (inline_iife(statements, compressor)) changed = 5; + if (!changed && last_changed == 5) break; + } + if (compressor.sequences_limit > 0) { + if (sequencesize(statements, compressor)) changed = 6; + if (!changed && last_changed == 6) break; + if (sequencesize_2(statements, compressor)) changed = 7; + if (!changed && last_changed == 7) break; + } + if (compressor.option("join_vars")) { + if (join_consecutive_vars(statements)) changed = 8; + if (!changed && last_changed == 8) break; + } + if (compressor.option("collapse_vars")) { + if (collapse(statements, compressor)) changed = 9; + } + } while (changed && max_iter-- > 0); + return statements; + + function last_of(compressor, predicate) { + var block = compressor.self(), level = 0, stat; + do { + if (block instanceof AST_Catch) { + block = compressor.parent(level++); + } else if (block instanceof AST_LabeledStatement) { + block = block.body; + } else if (block instanceof AST_SwitchBranch) { + var branches = compressor.parent(level); + if (branches.body[branches.body.length - 1] === block || has_break(block.body)) { + level++; + block = branches; + } + } + do { + stat = block; + if (predicate(stat)) return stat; + block = compressor.parent(level++); + } while (block instanceof AST_If); + } while (stat + && (block instanceof AST_BlockStatement + || block instanceof AST_Catch + || block instanceof AST_Scope + || block instanceof AST_SwitchBranch + || block instanceof AST_Try) + && is_last_statement(block.body, stat)); + + function has_break(stats) { + for (var i = stats.length; --i >= 0;) { + if (stats[i] instanceof AST_Break) return true; + } + return false; + } + } + + function find_loop_scope_try() { + var node = compressor.self(), level = 0; + do { + if (!block_scope && node.variables) block_scope = node; + if (node instanceof AST_Catch) { + if (compressor.parent(level).bfinally) { + if (!in_try) in_try = {}; + in_try.bfinally = true; + } + level++; + } else if (node instanceof AST_Finally) { + level++; + } else if (node instanceof AST_IterationStatement) { + in_loop = true; + } else if (node instanceof AST_Scope) { + scope = node; + break; + } else if (node instanceof AST_Try) { + if (!in_try) in_try = {}; + if (node.bcatch) in_try.bcatch = true; + if (node.bfinally) in_try.bfinally = true; + } + } while (node = compressor.parent(level++)); + } + + // Search from right to left for assignment-like expressions: + // - `var a = x;` + // - `a = x;` + // - `++a` + // For each candidate, scan from left to right for first usage, then try + // to fold assignment into the site for compression. + // Will not attempt to collapse assignments into or past code blocks + // which are not sequentially executed, e.g. loops and conditionals. + function collapse(statements, compressor) { + if (scope.pinned()) return; + var args; + var assignments = new Dictionary(); + var candidates = []; + var changed = false; + var declare_only = new Dictionary(); + var force_single; + var stat_index = statements.length; + var scanner = new TreeTransformer(function(node, descend) { + if (abort) return node; + // Skip nodes before `candidate` as quickly as possible + if (!hit) { + if (node !== hit_stack[hit_index]) return node; + hit_index++; + if (hit_index < hit_stack.length) return handle_custom_scan_order(node, scanner); + hit = true; + stop_after = (value_def ? find_stop_value : find_stop)(node, 0); + if (stop_after === node) abort = true; + return node; + } + var parent = scanner.parent(); + // Stop only if candidate is found within conditional branches + if (!stop_if_hit && in_conditional(node, parent)) { + stop_if_hit = parent; + } + // Cascade compound assignments + if (compound && scan_lhs && can_replace && !stop_if_hit + && node instanceof AST_Assign && node.operator != "=" && node.left.equals(lhs)) { + replaced++; + changed = true; + AST_Node.info("Cascading {this} [{start}]", node); + can_replace = false; + lvalues = get_lvalues(lhs); + node.right.transform(scanner); + clear_write_only(candidate); + var folded; + if (abort) { + folded = candidate; + } else { + abort = true; + folded = make_node(AST_Binary, candidate, { + operator: compound, + left: lhs.fixed && lhs.definition().fixed ? lhs.fixed.to_binary(candidate) : lhs, + right: rvalue, + }); + } + return make_node(AST_Assign, node, { + operator: "=", + left: node.left, + right: make_node(AST_Binary, node, { + operator: node.operator.slice(0, -1), + left: folded, + right: node.right, + }), + }); + } + // Stop immediately if these node types are encountered + if (should_stop(node, parent)) { + abort = true; + return node; + } + // Skip transient nodes caused by single-use variable replacement + if (node.single_use) return node; + // Replace variable with assignment when found + var hit_rhs; + if (!(node instanceof AST_SymbolDeclaration) + && (scan_lhs && lhs.equals(node) + || scan_rhs && (hit_rhs = scan_rhs(node, this)))) { + if (!can_replace || stop_if_hit && (hit_rhs || !lhs_local || !replace_all)) { + if (!hit_rhs && !value_def) abort = true; + return node; + } + if (is_lhs(node, parent)) { + if (value_def && !hit_rhs) assign_used = true; + return node; + } + if (!hit_rhs && verify_ref && node.fixed !== lhs.fixed) { + abort = true; + return node; + } + if (value_def) { + if (stop_if_hit && assign_pos == 0) assign_pos = remaining - replaced; + if (!hit_rhs) replaced++; + return node; + } + replaced++; + changed = abort = true; + AST_Node.info("Collapsing {this} [{start}]", node); + if (candidate.TYPE == "Binary") { + update_symbols(candidate, node); + return make_node(AST_Assign, candidate, { + operator: "=", + left: candidate.right.left, + right: candidate.operator == "&&" ? make_node(AST_Conditional, candidate, { + condition: candidate.left, + consequent: candidate.right.right, + alternative: node, + }) : make_node(AST_Conditional, candidate, { + condition: candidate.left, + consequent: node, + alternative: candidate.right.right, + }), + }); + } + if (candidate instanceof AST_UnaryPostfix) return make_node(AST_UnaryPrefix, candidate, { + operator: candidate.operator, + expression: lhs.fixed && lhs.definition().fixed ? lhs.fixed.to_prefix(candidate) : lhs, + }); + if (candidate instanceof AST_UnaryPrefix) { + clear_write_only(candidate); + return candidate; + } + update_symbols(rvalue, node); + if (candidate instanceof AST_VarDef) { + var def = candidate.name.definition(); + if (def.references.length - def.replaced == 1 && !compressor.exposed(def)) { + def.replaced++; + return maintain_this_binding(parent, node, rvalue); + } + return make_node(AST_Assign, candidate, { + operator: "=", + left: node, + right: rvalue, + }); + } + clear_write_only(rvalue); + var assign = candidate.clone(); + assign.right = rvalue; + return assign; + } + // Stop signals related to AST_SymbolRef + if (should_stop_ref(node, parent)) { + abort = true; + return node; + } + // These node types have child nodes that execute sequentially, + // but are otherwise not safe to scan into or beyond them. + if (is_last_node(node, parent) || may_throw(node)) { + stop_after = node; + if (node instanceof AST_Scope) abort = true; + } + // Scan but don't replace inside getter/setter + if (node instanceof AST_Accessor) { + var replace = can_replace; + can_replace = false; + descend(node, scanner); + can_replace = replace; + return signal_abort(node); + } + // Scan but don't replace inside destructuring expression + if (node instanceof AST_Destructured) { + var replace = can_replace; + can_replace = false; + descend(node, scanner); + can_replace = replace; + return signal_abort(node); + } + // Scan but don't replace inside default value + if (node instanceof AST_DefaultValue) { + node.name = node.name.transform(scanner); + var replace = can_replace; + can_replace = false; + node.value = node.value.transform(scanner); + can_replace = replace; + return signal_abort(node); + } + // Scan but don't replace inside block scope with colliding variable + if (node instanceof AST_BlockScope + && !(node instanceof AST_Scope) + && !(node.variables && node.variables.all(function(def) { + return !enclosed.has(def.name) && !lvalues.has(def.name); + }))) { + var replace = can_replace; + can_replace = false; + if (!handle_custom_scan_order(node, scanner)) descend(node, scanner); + can_replace = replace; + return signal_abort(node); + } + if (handle_custom_scan_order(node, scanner)) return signal_abort(node); + }, signal_abort); + var multi_replacer = new TreeTransformer(function(node) { + if (abort) return node; + // Skip nodes before `candidate` as quickly as possible + if (!hit) { + if (node !== hit_stack[hit_index]) return node; + hit_index++; + switch (hit_stack.length - hit_index) { + case 0: + hit = true; + if (assign_used) return node; + if (node !== candidate) return node; + if (node instanceof AST_VarDef) return node; + def.replaced++; + var parent = multi_replacer.parent(); + if (parent instanceof AST_Sequence && parent.tail_node() !== node) { + value_def.replaced++; + if (rvalue === rhs_value) return List.skip; + return make_sequence(rhs_value, rhs_value.expressions.slice(0, -1)); + } + return rvalue; + case 1: + if (!assign_used && node.body === candidate) { + hit = true; + def.replaced++; + value_def.replaced++; + return null; + } + default: + return handle_custom_scan_order(node, multi_replacer); + } + } + // Replace variable when found + if (node instanceof AST_SymbolRef && node.definition() === def) { + if (is_lhs(node, multi_replacer.parent())) return node; + if (!--replaced) abort = true; + AST_Node.info("Replacing {this} [{start}]", node); + var ref = rvalue.clone(); + ref.scope = node.scope; + ref.reference(); + if (replaced == assign_pos) { + abort = true; + return make_node(AST_Assign, candidate, { + operator: "=", + left: node, + right: ref, + }); + } + def.replaced++; + return ref; + } + // Skip (non-executed) functions and (leading) default case in switch statements + if (node instanceof AST_Default || node instanceof AST_Scope) return node; + }, function(node) { + return patch_sequence(node, multi_replacer); + }); + while (--stat_index >= 0) { + // Treat parameters as collapsible in IIFE, i.e. + // function(a, b){ ... }(x()); + // would be translated into equivalent assignments: + // var a = x(), b = undefined; + if (stat_index == 0 && compressor.option("unused")) extract_args(); + // Find collapsible assignments + var hit_stack = []; + extract_candidates(statements[stat_index]); + while (candidates.length > 0) { + hit_stack = candidates.pop(); + var hit_index = 0; + var candidate = hit_stack[hit_stack.length - 1]; + var assign_pos = -1; + var assign_used = false; + var verify_ref = false; + var remaining; + var value_def = null; + var stop_after = null; + var stop_if_hit = null; + var lhs = get_lhs(candidate); + var side_effects = lhs && lhs.has_side_effects(compressor); + var scan_lhs = lhs && (!side_effects || lhs instanceof AST_SymbolRef) + && !is_lhs_read_only(lhs, compressor); + var scan_rhs = foldable(candidate); + if (!scan_lhs && !scan_rhs) continue; + var compound = candidate instanceof AST_Assign && candidate.operator.slice(0, -1); + var funarg = candidate.name instanceof AST_SymbolFunarg; + var may_throw = return_false; + if (candidate.may_throw(compressor)) { + if (funarg && is_async(scope)) continue; + may_throw = in_try ? function(node) { + return node.has_side_effects(compressor); + } : side_effects_external; + } + var read_toplevel = false; + var modify_toplevel = false; + // Locate symbols which may execute code outside of scanning range + var enclosed = new Dictionary(); + var well_defined = true; + var lvalues = get_lvalues(candidate); + var lhs_local = is_lhs_local(lhs); + var rhs_value = get_rvalue(candidate); + var rvalue = rhs_value; + if (!side_effects) { + if (!compound && rvalue instanceof AST_Sequence) rvalue = rvalue.tail_node(); + side_effects = value_has_side_effects(); + } + var check_destructured = in_try || !lhs_local ? function(node) { + return node instanceof AST_Destructured; + } : return_false; + var replace_all = replace_all_symbols(candidate); + var hit = funarg; + var abort = false; + var replaced = 0; + var can_replace = !args || !hit; + if (!can_replace) { + for (var j = candidate.arg_index + 1; !abort && j < args.length; j++) { + if (args[j]) args[j].transform(scanner); + } + can_replace = true; + } + for (var i = stat_index; !abort && i < statements.length; i++) { + statements[i].transform(scanner); + } + if (value_def) { + if (!replaced || remaining > replaced + assign_used) { + candidates.push(hit_stack); + force_single = true; + continue; + } + if (replaced == assign_pos) assign_used = true; + var def = lhs.definition(); + abort = false; + hit_index = 0; + hit = funarg; + for (var i = stat_index; !abort && i < statements.length; i++) { + if (!statements[i].transform(multi_replacer)) statements.splice(i--, 1); + } + replaced = candidate instanceof AST_VarDef + && candidate === hit_stack[hit_stack.length - 1] + && def.references.length == def.replaced + && !compressor.exposed(def); + value_def.last_ref = false; + value_def.single_use = false; + changed = true; + } + if (replaced) remove_candidate(candidate); + } + } + return changed; + + function signal_abort(node) { + if (abort) return node; + if (stop_after === node) abort = true; + if (stop_if_hit === node) stop_if_hit = null; + return node; + } + + function handle_custom_scan_order(node, tt) { + if (!(node instanceof AST_BlockScope)) return; + // Skip (non-executed) functions + if (node instanceof AST_Scope) return node; + // Scan computed keys, static fields & initializers in class + if (node instanceof AST_Class) { + var replace = can_replace; + can_replace = false; + if (node.name) node.name.transform(tt); + if (!abort && node.extends) node.extends.transform(tt); + var fields = [], stats = []; + for (var i = 0; !abort && i < node.properties.length; i++) { + var prop = node.properties[i]; + if (prop.key instanceof AST_Node) prop.key = prop.key.transform(tt); + if (!prop.static) continue; + if (prop instanceof AST_ClassField) { + if (prop.value) fields.push(prop); + } else if (prop instanceof AST_ClassInit) { + [].push.apply(stats, prop.value.body); + } + } + for (var i = 0; !abort && i < stats.length; i++) { + stats[i].transform(tt); + } + for (var i = 0; !abort && i < fields.length; i++) { + fields[i].value.transform(tt); + } + can_replace = replace; + return node; + } + // Scan object only in a for-in/of statement + if (node instanceof AST_ForEnumeration) { + node.object = node.object.transform(tt); + abort = true; + return node; + } + // Scan first case expression only in a switch statement + if (node instanceof AST_Switch) { + node.expression = node.expression.transform(tt); + for (var i = 0; !abort && i < node.body.length; i++) { + var branch = node.body[i]; + if (branch instanceof AST_Case) { + if (!hit) { + if (branch !== hit_stack[hit_index]) continue; + hit_index++; + } + branch.expression = branch.expression.transform(tt); + if (!replace_all || verify_ref) break; + scan_rhs = false; + } + } + abort = true; + return node; + } + } + + function is_direct_assignment(node, parent) { + if (parent instanceof AST_Assign) return parent.operator == "=" && parent.left === node; + if (parent instanceof AST_DefaultValue) return parent.name === node; + if (parent instanceof AST_DestructuredArray) return true; + if (parent instanceof AST_DestructuredKeyVal) return parent.value === node; + } + + function should_stop(node, parent) { + if (node === rvalue) return true; + if (parent instanceof AST_For) { + if (node !== parent.init) return true; + } + if (node instanceof AST_Assign) { + return node.operator != "=" && lhs.equals(node.left); + } + if (node instanceof AST_Call) { + if (!(lhs instanceof AST_PropAccess)) return false; + if (!lhs.equals(node.expression)) return false; + return !(rvalue instanceof AST_LambdaExpression && !rvalue.contains_this()); + } + if (node instanceof AST_Class) return !compressor.has_directive("use strict"); + if (node instanceof AST_Debugger) return true; + if (node instanceof AST_Defun) return funarg && lhs.name === node.name.name; + if (node instanceof AST_DestructuredKeyVal) return node.key instanceof AST_Node; + if (node instanceof AST_DWLoop) return true; + if (node instanceof AST_LoopControl) return true; + if (node instanceof AST_Try) return true; + if (node instanceof AST_With) return true; + return false; + } + + function should_stop_ref(node, parent) { + if (!(node instanceof AST_SymbolRef)) return false; + if (node.is_declared(compressor)) { + if (node.fixed_value()) return false; + if (can_drop_symbol(node)) { + return !(parent instanceof AST_PropAccess && parent.expression === node) + && is_arguments(node.definition()); + } + } else if (is_direct_assignment(node, parent)) { + return false; + } + if (!replace_all) return true; + scan_rhs = false; + return false; + } + + function in_conditional(node, parent) { + if (parent instanceof AST_Assign) return parent.left !== node && lazy_op[parent.operator.slice(0, -1)]; + if (parent instanceof AST_Binary) return parent.left !== node && lazy_op[parent.operator]; + if (parent instanceof AST_Call) return parent.optional && parent.expression !== node; + if (parent instanceof AST_Case) return parent.expression !== node; + if (parent instanceof AST_Conditional) return parent.condition !== node; + if (parent instanceof AST_If) return parent.condition !== node; + if (parent instanceof AST_Sub) return parent.optional && parent.expression !== node; + } + + function is_last_node(node, parent) { + if (node instanceof AST_Await) return true; + if (node.TYPE == "Binary") return !can_drop_op(node, compressor); + if (node instanceof AST_Call) { + var def, fn = node.expression; + if (fn instanceof AST_SymbolRef) { + def = fn.definition(); + fn = fn.fixed_value(); + } + if (!(fn instanceof AST_Lambda)) return !node.is_expr_pure(compressor); + if (def && recursive_ref(compressor, def, fn)) return true; + if (fn.collapse_scanning) return false; + fn.collapse_scanning = true; + var replace = can_replace; + can_replace = false; + var after = stop_after; + var if_hit = stop_if_hit; + for (var i = 0; !abort && i < fn.argnames.length; i++) { + if (arg_may_throw(reject, fn.argnames[i], node.args[i])) abort = true; + } + if (!abort) { + if (fn.rest && arg_may_throw(reject, fn.rest, make_node(AST_Array, node, { + elements: node.args.slice(i), + }))) { + abort = true; + } else if (is_arrow(fn) && fn.value) { + fn.value.transform(scanner); + } else for (var i = 0; !abort && i < fn.body.length; i++) { + var stat = fn.body[i]; + if (stat instanceof AST_Return) { + if (stat.value) stat.value.transform(scanner); + break; + } + stat.transform(scanner); + } + } + stop_if_hit = if_hit; + stop_after = after; + can_replace = replace; + fn.collapse_scanning = false; + if (!abort) return false; + abort = false; return true; - return false; - }); - def(AST_Dot, function(compressor){ - if (!compressor.option("pure_getters")) return true; - return this.expression.has_side_effects(compressor); - }); - def(AST_Sub, function(compressor){ - if (!compressor.option("pure_getters")) return true; - return this.expression.has_side_effects(compressor) - || this.property.has_side_effects(compressor); - }); - def(AST_PropAccess, function(compressor){ - return !compressor.option("pure_getters"); - }); - def(AST_Seq, function(compressor){ - return this.car.has_side_effects(compressor) - || this.cdr.has_side_effects(compressor); - }); - })(function(node, func){ - node.DEFMETHOD("has_side_effects", func); - }); + } + if (node instanceof AST_Class) { + if (!in_try) return false; + var base = node.extends; + if (!base) return false; + if (base instanceof AST_SymbolRef) base = base.fixed_value(); + return !safe_for_extends(base); + } + if (node instanceof AST_Exit) { + if (in_try) { + if (in_try.bfinally) return true; + if (in_try.bcatch && node instanceof AST_Throw) return true; + } + return side_effects || lhs instanceof AST_PropAccess || may_modify(lhs); + } + if (node instanceof AST_Function) { + return compressor.option("ie") && node.name && lvalues.has(node.name.name); + } + if (node instanceof AST_ObjectIdentity) return symbol_in_lvalues(node, parent); + if (node instanceof AST_PropAccess) { + if (side_effects) return true; + var exp = node.expression; + if (exp instanceof AST_SymbolRef && is_arguments(exp.definition())) return true; + if (compressor.option("unsafe")) { + if (is_undeclared_ref(exp) && global_names[exp.name]) return false; + if (is_static_fn(exp)) return false; + } + if (!well_defined) return true; + if (value_def) return false; + if (!in_try && lhs_local) return false; + if (node.optional) return false; + return exp.may_throw_on_access(compressor); + } + if (node instanceof AST_Spread) return true; + if (node instanceof AST_SymbolRef) { + if (symbol_in_lvalues(node, parent)) return !is_direct_assignment(node, parent); + if (side_effects && may_modify(node)) return true; + var def = node.definition(); + return (in_try || def.scope.resolve() !== scope) && !can_drop_symbol(node); + } + if (node instanceof AST_Template) return !node.is_expr_pure(compressor); + if (node instanceof AST_VarDef) { + if (check_destructured(node.name)) return true; + return (node.value || parent instanceof AST_Let) && node.name.match_symbol(function(node) { + return node instanceof AST_SymbolDeclaration + && (lvalues.has(node.name) || side_effects && may_modify(node)); + }, true); + } + if (node instanceof AST_Yield) return true; + var sym = is_lhs(node.left, node); + if (!sym) return false; + if (sym instanceof AST_PropAccess) return true; + if (check_destructured(sym)) return true; + return sym.match_symbol(function(node) { + return node instanceof AST_SymbolRef + && (lvalues.has(node.name) || read_toplevel && compressor.exposed(node.definition())); + }, true); + + function reject(node) { + node.transform(scanner); + return abort; + } + } + + function arg_may_throw(reject, node, value) { + if (node instanceof AST_DefaultValue) { + return reject(node.value) + || arg_may_throw(reject, node.name, node.value) + || !is_undefined(value) && arg_may_throw(reject, node.name, value); + } + if (!value) return !(node instanceof AST_Symbol); + if (node instanceof AST_Destructured) { + if (node.rest && arg_may_throw(reject, node.rest)) return true; + if (node instanceof AST_DestructuredArray) { + if (value instanceof AST_Array) return !all(node.elements, function(element, index) { + return !arg_may_throw(reject, element, value[index]); + }); + if (!value.is_string(compressor)) return true; + return !all(node.elements, function(element) { + return !arg_may_throw(reject, element); + }); + } + if (node instanceof AST_DestructuredObject) { + if (value.may_throw_on_access(compressor)) return true; + return !all(node.properties, function(prop) { + if (prop.key instanceof AST_Node && reject(prop.key)) return false; + return !arg_may_throw(reject, prop.value); + }); + } + } + } + + function extract_args() { + if (in_iife_single === false) return; + var iife = compressor.parent(), fn = compressor.self(); + if (in_iife_single === undefined) { + if (!(fn instanceof AST_LambdaExpression) + || is_generator(fn) + || fn.uses_arguments + || fn.pinned() + || !(iife instanceof AST_Call) + || iife.expression !== fn + || !all(iife.args, function(arg) { + return !(arg instanceof AST_Spread); + })) { + in_iife_single = false; + return; + } + if (!is_iife_single(iife)) return; + in_iife_single = true; + } + var fn_strict = fn.in_strict_mode(compressor) + && !fn.parent_scope.resolve(true).in_strict_mode(compressor); + var has_await; + if (is_async(fn)) { + has_await = function(node) { + return node instanceof AST_Symbol && node.name == "await"; + }; + iife_in_try = true; + } else { + has_await = function(node) { + return node instanceof AST_Await && !tw.find_parent(AST_Scope); + }; + if (iife_in_try === undefined) iife_in_try = find_try(compressor, 1, iife, null, true, true); + } + var arg_scope = null; + var tw = new TreeWalker(function(node, descend) { + if (!arg) return true; + if (has_await(node) || node instanceof AST_Yield) { + arg = null; + return true; + } + if (node instanceof AST_ObjectIdentity) { + if (fn_strict || !arg_scope) arg = null; + return true; + } + if (node instanceof AST_SymbolRef) { + var def; + if (node.in_arg && !is_safe_lexical(node.definition()) + || (def = fn.variables.get(node.name)) && def !== node.definition()) { + arg = null; + } + return true; + } + if (node instanceof AST_Scope && !is_arrow(node)) { + var save_scope = arg_scope; + arg_scope = node; + descend(); + arg_scope = save_scope; + return true; + } + }); + args = iife.args.slice(); + var len = args.length; + var names = new Dictionary(); + for (var i = fn.argnames.length; --i >= 0;) { + var sym = fn.argnames[i]; + var arg = args[i]; + var value = null; + if (sym instanceof AST_DefaultValue) { + value = sym.value; + sym = sym.name; + args[len + i] = value; + } + if (sym instanceof AST_Destructured) { + if (iife_in_try && arg_may_throw(function(node) { + return node.has_side_effects(compressor); + }, sym, arg)) { + candidates.length = 0; + break; + } + args[len + i] = fn.argnames[i]; + continue; + } + if (names.has(sym.name)) continue; + names.set(sym.name, true); + if (value) arg = is_undefined(arg) ? value : null; + if (!arg && !value) { + arg = make_node(AST_Undefined, sym).transform(compressor); + } else if (arg instanceof AST_Lambda && arg.pinned()) { + arg = null; + } else if (arg) { + arg.walk(tw); + } + if (!arg) continue; + var candidate = make_node(AST_VarDef, sym, { + name: sym, + value: arg, + }); + candidate.name_index = i; + candidate.arg_index = value ? len + i : i; + candidates.unshift([ candidate ]); + } + if (fn.rest) args.push(fn.rest); + } + + function extract_candidates(expr, unused) { + hit_stack.push(expr); + if (expr instanceof AST_Array) { + expr.elements.forEach(function(node) { + extract_candidates(node, unused); + }); + } else if (expr instanceof AST_Assign) { + var lhs = expr.left; + if (!(lhs instanceof AST_Destructured)) candidates.push(hit_stack.slice()); + extract_candidates(lhs); + extract_candidates(expr.right); + if (lhs instanceof AST_SymbolRef && expr.operator == "=") { + assignments.set(lhs.name, (assignments.get(lhs.name) || 0) + 1); + } + } else if (expr instanceof AST_Await) { + extract_candidates(expr.expression, unused); + } else if (expr instanceof AST_Binary) { + var lazy = lazy_op[expr.operator]; + if (unused + && lazy + && expr.operator != "??" + && expr.right instanceof AST_Assign + && expr.right.operator == "=" + && !(expr.right.left instanceof AST_Destructured)) { + candidates.push(hit_stack.slice()); + } + extract_candidates(expr.left, !lazy && unused); + extract_candidates(expr.right, unused); + } else if (expr instanceof AST_Call) { + extract_candidates(expr.expression); + expr.args.forEach(extract_candidates); + } else if (expr instanceof AST_Case) { + extract_candidates(expr.expression); + } else if (expr instanceof AST_Conditional) { + extract_candidates(expr.condition); + extract_candidates(expr.consequent, unused); + extract_candidates(expr.alternative, unused); + } else if (expr instanceof AST_Definitions) { + expr.definitions.forEach(extract_candidates); + } else if (expr instanceof AST_Dot) { + extract_candidates(expr.expression); + } else if (expr instanceof AST_DWLoop) { + extract_candidates(expr.condition); + if (!(expr.body instanceof AST_Block)) { + extract_candidates(expr.body); + } + } else if (expr instanceof AST_Exit) { + if (expr.value) extract_candidates(expr.value); + } else if (expr instanceof AST_For) { + if (expr.init) extract_candidates(expr.init, true); + if (expr.condition) extract_candidates(expr.condition); + if (expr.step) extract_candidates(expr.step, true); + if (!(expr.body instanceof AST_Block)) { + extract_candidates(expr.body); + } + } else if (expr instanceof AST_ForEnumeration) { + extract_candidates(expr.object); + if (!(expr.body instanceof AST_Block)) { + extract_candidates(expr.body); + } + } else if (expr instanceof AST_If) { + extract_candidates(expr.condition); + if (!(expr.body instanceof AST_Block)) { + extract_candidates(expr.body); + } + if (expr.alternative && !(expr.alternative instanceof AST_Block)) { + extract_candidates(expr.alternative); + } + } else if (expr instanceof AST_Object) { + expr.properties.forEach(function(prop) { + hit_stack.push(prop); + if (prop.key instanceof AST_Node) extract_candidates(prop.key); + if (prop instanceof AST_ObjectKeyVal) extract_candidates(prop.value, unused); + hit_stack.pop(); + }); + } else if (expr instanceof AST_Sequence) { + var end = expr.expressions.length - (unused ? 0 : 1); + expr.expressions.forEach(function(node, index) { + extract_candidates(node, index < end); + }); + } else if (expr instanceof AST_SimpleStatement) { + extract_candidates(expr.body, true); + } else if (expr instanceof AST_Spread) { + extract_candidates(expr.expression); + } else if (expr instanceof AST_Sub) { + extract_candidates(expr.expression); + extract_candidates(expr.property); + } else if (expr instanceof AST_Switch) { + extract_candidates(expr.expression); + expr.body.forEach(extract_candidates); + } else if (expr instanceof AST_Unary) { + if (UNARY_POSTFIX[expr.operator]) { + candidates.push(hit_stack.slice()); + } else { + extract_candidates(expr.expression); + } + } else if (expr instanceof AST_VarDef) { + if (expr.name instanceof AST_SymbolVar) { + if (expr.value) { + var def = expr.name.definition(); + if (def.references.length > def.replaced) { + candidates.push(hit_stack.slice()); + } + } else { + declare_only.set(expr.name.name, (declare_only.get(expr.name.name) || 0) + 1); + } + } + if (expr.value) extract_candidates(expr.value); + } else if (expr instanceof AST_Yield) { + if (expr.expression) extract_candidates(expr.expression); + } + hit_stack.pop(); + } + + function find_stop(node, level) { + var parent = scanner.parent(level); + if (parent instanceof AST_Array) return node; + if (parent instanceof AST_Assign) return node; + if (parent instanceof AST_Await) return node; + if (parent instanceof AST_Binary) return node; + if (parent instanceof AST_Call) return node; + if (parent instanceof AST_Case) return node; + if (parent instanceof AST_Conditional) return node; + if (parent instanceof AST_Definitions) return find_stop_unused(parent, level + 1); + if (parent instanceof AST_Exit) return node; + if (parent instanceof AST_If) return node; + if (parent instanceof AST_IterationStatement) return node; + if (parent instanceof AST_ObjectProperty) return node; + if (parent instanceof AST_PropAccess) return node; + if (parent instanceof AST_Sequence) { + return (parent.tail_node() === node ? find_stop : find_stop_unused)(parent, level + 1); + } + if (parent instanceof AST_SimpleStatement) return find_stop_unused(parent, level + 1); + if (parent instanceof AST_Spread) return node; + if (parent instanceof AST_Switch) return node; + if (parent instanceof AST_Unary) return node; + if (parent instanceof AST_VarDef) return node; + if (parent instanceof AST_Yield) return node; + return null; + } + + function find_stop_logical(parent, op, level) { + var node; + do { + node = parent; + parent = scanner.parent(++level); + } while (parent instanceof AST_Assign && parent.operator.slice(0, -1) == op + || parent instanceof AST_Binary && parent.operator == op); + return node; + } + + function find_stop_expr(expr, cont, node, parent, level) { + var replace = can_replace; + can_replace = false; + var after = stop_after; + var if_hit = stop_if_hit; + var stack = scanner.stack; + scanner.stack = [ parent ]; + expr.transform(scanner); + scanner.stack = stack; + stop_if_hit = if_hit; + stop_after = after; + can_replace = replace; + if (abort) { + abort = false; + return node; + } + return cont(parent, level + 1); + } + + function find_stop_value(node, level) { + var parent = scanner.parent(level); + if (parent instanceof AST_Array) return find_stop_value(parent, level + 1); + if (parent instanceof AST_Assign) { + if (may_throw(parent)) return node; + if (parent.left.match_symbol(function(ref) { + return ref instanceof AST_SymbolRef && (lhs.name == ref.name || value_def.name == ref.name); + })) return node; + var op; + if (parent.left === node || !lazy_op[op = parent.operator.slice(0, -1)]) { + return find_stop_value(parent, level + 1); + } + return find_stop_logical(parent, op, level); + } + if (parent instanceof AST_Await) return find_stop_value(parent, level + 1); + if (parent instanceof AST_Binary) { + var op; + if (parent.left === node || !lazy_op[op = parent.operator]) { + return find_stop_value(parent, level + 1); + } + return find_stop_logical(parent, op, level); + } + if (parent instanceof AST_Call) return parent; + if (parent instanceof AST_Case) { + if (parent.expression !== node) return node; + return find_stop_value(parent, level + 1); + } + if (parent instanceof AST_Conditional) { + if (parent.condition !== node) return node; + return find_stop_value(parent, level + 1); + } + if (parent instanceof AST_Definitions) return find_stop_unused(parent, level + 1); + if (parent instanceof AST_Do) return node; + if (parent instanceof AST_Exit) return find_stop_unused(parent, level + 1); + if (parent instanceof AST_For) { + if (parent.init !== node && parent.condition !== node) return node; + return find_stop_value(parent, level + 1); + } + if (parent instanceof AST_ForEnumeration) { + if (parent.init !== node) return node; + return find_stop_value(parent, level + 1); + } + if (parent instanceof AST_If) { + if (parent.condition !== node) return node; + return find_stop_value(parent, level + 1); + } + if (parent instanceof AST_ObjectProperty) { + var obj = scanner.parent(level + 1); + return all(obj.properties, function(prop) { + return prop instanceof AST_ObjectKeyVal; + }) ? find_stop_value(obj, level + 2) : obj; + } + if (parent instanceof AST_PropAccess) { + var exp = parent.expression; + return exp === node ? find_stop_value(parent, level + 1) : node; + } + if (parent instanceof AST_Sequence) { + return (parent.tail_node() === node ? find_stop_value : find_stop_unused)(parent, level + 1); + } + if (parent instanceof AST_SimpleStatement) return find_stop_unused(parent, level + 1); + if (parent instanceof AST_Spread) return find_stop_value(parent, level + 1); + if (parent instanceof AST_Switch) { + if (parent.expression !== node) return node; + return find_stop_value(parent, level + 1); + } + if (parent instanceof AST_Unary) { + if (parent.operator == "delete") return node; + return find_stop_value(parent, level + 1); + } + if (parent instanceof AST_VarDef) return parent.name.match_symbol(function(sym) { + return sym instanceof AST_SymbolDeclaration && (lhs.name == sym.name || value_def.name == sym.name); + }) ? node : find_stop_value(parent, level + 1); + if (parent instanceof AST_While) { + if (parent.condition !== node) return node; + return find_stop_value(parent, level + 1); + } + if (parent instanceof AST_Yield) return find_stop_value(parent, level + 1); + return null; + } + + function find_stop_unused(node, level) { + var parent = scanner.parent(level); + if (is_last_node(node, parent)) return node; + if (in_conditional(node, parent)) return node; + if (parent instanceof AST_Array) return find_stop_unused(parent, level + 1); + if (parent instanceof AST_Assign) return check_assignment(parent.left); + if (parent instanceof AST_Await) return node; + if (parent instanceof AST_Binary) return find_stop_unused(parent, level + 1); + if (parent instanceof AST_Call) return find_stop_unused(parent, level + 1); + if (parent instanceof AST_Case) return find_stop_unused(parent, level + 1); + if (parent instanceof AST_Conditional) return find_stop_unused(parent, level + 1); + if (parent instanceof AST_Definitions) return find_stop_unused(parent, level + 1); + if (parent instanceof AST_Exit) return find_stop_unused(parent, level + 1); + if (parent instanceof AST_If) return find_stop_unused(parent, level + 1); + if (parent instanceof AST_IterationStatement) return node; + if (parent instanceof AST_ObjectProperty) { + var obj = scanner.parent(level + 1); + return all(obj.properties, function(prop) { + return prop instanceof AST_ObjectKeyVal; + }) ? find_stop_unused(obj, level + 2) : obj; + } + if (parent instanceof AST_PropAccess) { + var exp = parent.expression; + if (exp === node) return find_stop_unused(parent, level + 1); + return find_stop_expr(exp, find_stop_unused, node, parent, level); + } + if (parent instanceof AST_Sequence) return find_stop_unused(parent, level + 1); + if (parent instanceof AST_SimpleStatement) return find_stop_unused(parent, level + 1); + if (parent instanceof AST_Spread) return node; + if (parent instanceof AST_Switch) return find_stop_unused(parent, level + 1); + if (parent instanceof AST_Unary) return find_stop_unused(parent, level + 1); + if (parent instanceof AST_VarDef) return check_assignment(parent.name); + if (parent instanceof AST_Yield) return node; + return null; + + function check_assignment(lhs) { + if (may_throw(parent)) return node; + if (lhs !== node && lhs instanceof AST_Destructured) { + return find_stop_expr(lhs, find_stop_unused, node, parent, level); + } + return find_stop_unused(parent, level + 1); + } + } + + function mangleable_var(rhs) { + if (force_single) { + force_single = false; + return; + } + if (remaining < 1) return; + rhs = rhs.tail_node(); + var value = rhs instanceof AST_Assign && rhs.operator == "=" ? rhs.left : rhs; + if (!(value instanceof AST_SymbolRef)) return; + var def = value.definition(); + if (def.undeclared) return; + if (is_arguments(def)) return; + if (value !== rhs) { + if (is_lhs_read_only(value, compressor)) return; + var referenced = def.references.length - def.replaced; + if (referenced < 2) return; + var expr = candidate.clone(); + expr[expr instanceof AST_Assign ? "right" : "value"] = value; + if (candidate.name_index >= 0) { + expr.name_index = candidate.name_index; + expr.arg_index = candidate.arg_index; + } + candidate = expr; + } + return value_def = def; + } + + function remaining_refs(def) { + return def.references.length - def.replaced - (assignments.get(def.name) || 0); + } + + function get_lhs(expr) { + if (expr instanceof AST_Assign) { + var lhs = expr.left; + if (!(lhs instanceof AST_SymbolRef)) return lhs; + var def = lhs.definition(); + if (scope.uses_arguments && is_funarg(def)) return lhs; + if (compressor.exposed(def)) return lhs; + remaining = remaining_refs(def); + if (def.fixed && lhs.fixed) { + var matches = def.references.filter(function(ref) { + return ref.fixed === lhs.fixed; + }).length - 1; + if (matches < remaining) { + remaining = matches; + assign_pos = 0; + verify_ref = true; + } + } + if (expr.operator == "=") mangleable_var(expr.right); + return lhs; + } + if (expr instanceof AST_Binary) return expr.right.left; + if (expr instanceof AST_Unary) return expr.expression; + if (expr instanceof AST_VarDef) { + var lhs = expr.name; + var def = lhs.definition(); + if (def.const_redefs) return; + if (!member(lhs, def.orig)) return; + if (scope.uses_arguments && is_funarg(def)) return; + var declared = def.orig.length - def.eliminated - (declare_only.get(def.name) || 0); + remaining = remaining_refs(def); + if (def.fixed) remaining = Math.min(remaining, def.references.filter(function(ref) { + if (!ref.fixed) return true; + if (!ref.fixed.assigns) return true; + var assign = ref.fixed.assigns[0]; + return assign === lhs || get_rvalue(assign) === expr.value; + }).length); + if (declared > 1 && !(lhs instanceof AST_SymbolFunarg)) { + mangleable_var(expr.value); + return make_node(AST_SymbolRef, lhs); + } + if (mangleable_var(expr.value) || remaining == 1 && !compressor.exposed(def)) { + return make_node(AST_SymbolRef, lhs); + } + return; + } + } + + function get_rvalue(expr) { + if (expr instanceof AST_Assign) return expr.right; + if (expr instanceof AST_Binary) { + var node = expr.clone(); + node.right = expr.right.right; + return node; + } + if (expr instanceof AST_VarDef) return expr.value; + } + + function invariant(expr) { + if (expr instanceof AST_Array) return false; + if (expr instanceof AST_Binary && lazy_op[expr.operator]) { + return invariant(expr.left) && invariant(expr.right); + } + if (expr instanceof AST_Call) return false; + if (expr instanceof AST_Conditional) { + return invariant(expr.consequent) && invariant(expr.alternative); + } + if (expr instanceof AST_Object) return false; + return !expr.has_side_effects(compressor); + } + + function foldable(expr) { + if (expr instanceof AST_Assign && expr.right.single_use) return; + var lhs_ids = Object.create(null); + var marker = new TreeWalker(function(node) { + if (node instanceof AST_SymbolRef) lhs_ids[node.definition().id] = true; + }); + while (expr instanceof AST_Assign && expr.operator == "=") { + expr.left.walk(marker); + expr = expr.right; + } + if (expr instanceof AST_ObjectIdentity) return rhs_exact_match; + if (expr instanceof AST_SymbolRef) { + var value = expr.evaluate(compressor); + if (value === expr) return rhs_exact_match; + return rhs_fuzzy_match(value, rhs_exact_match); + } + if (expr.is_truthy()) return rhs_fuzzy_match(true, return_false); + if (expr.is_constant()) { + var ev = expr.evaluate(compressor); + if (!(ev instanceof AST_Node)) return rhs_fuzzy_match(ev, rhs_exact_match); + } + if (!(lhs instanceof AST_SymbolRef)) return false; + if (!invariant(expr)) return false; + var circular; + expr.walk(new TreeWalker(function(node) { + if (circular) return true; + if (node instanceof AST_SymbolRef && lhs_ids[node.definition().id]) circular = true; + })); + return !circular && rhs_exact_match; + + function rhs_exact_match(node) { + return expr.equals(node); + } + } + + function rhs_fuzzy_match(value, fallback) { + return function(node, tw) { + if (tw.in_boolean_context()) { + if (value && node.is_truthy() && !node.has_side_effects(compressor)) { + return true; + } + if (node.is_constant()) { + var ev = node.evaluate(compressor); + if (!(ev instanceof AST_Node)) return !ev == !value; + } + } + return fallback(node); + }; + } + + function clear_write_only(assign) { + while (assign.write_only) { + assign.write_only = false; + if (!(assign instanceof AST_Assign)) break; + assign = assign.right; + } + } + + function update_symbols(value, node) { + var scope = node.scope || find_scope(scanner) || block_scope; + value.walk(new TreeWalker(function(node) { + if (node instanceof AST_BlockScope) return true; + if (node instanceof AST_Symbol) node.scope = scope; + })); + } + + function may_be_global(node) { + if (node instanceof AST_SymbolRef) { + node = node.fixed_value(); + if (!node) return true; + } + if (node instanceof AST_Assign) return node.operator == "=" && may_be_global(node.right); + return node instanceof AST_PropAccess || node instanceof AST_ObjectIdentity; + } + + function get_lvalues(expr) { + var lvalues = new Dictionary(); + if (expr instanceof AST_VarDef) { + if (!expr.name.definition().fixed) well_defined = false; + lvalues.add(expr.name.name, lhs); + } + var find_arguments = scope.uses_arguments && !compressor.has_directive("use strict"); + var scan_toplevel = scope instanceof AST_Toplevel; + var tw = new TreeWalker(function(node) { + var value; + if (node instanceof AST_SymbolRef) { + value = node.fixed_value(); + if (!value) { + value = node; + var def = node.definition(); + var escaped = node.fixed && node.fixed.escaped || def.escaped; + if (!def.undeclared + && (def.assignments || !escaped || escaped.cross_scope) + && (has_escaped(def, node.scope, node, tw.parent()) || !same_scope(def))) { + well_defined = false; + } + } + } else if (node instanceof AST_ObjectIdentity) { + value = node; + } + if (value) { + lvalues.add(node.name, is_modified(compressor, tw, node, value, 0)); + } else if (node instanceof AST_Lambda) { + for (var level = 0, parent, child = node; parent = tw.parent(level++); child = parent) { + if (parent instanceof AST_Assign) { + if (parent.left === child) break; + if (parent.operator == "=") continue; + if (lazy_op[parent.operator.slice(0, -1)]) continue; + break; + } + if (parent instanceof AST_Binary) { + if (lazy_op[parent.operator]) continue; + break; + } + if (parent instanceof AST_Call) return; + if (parent instanceof AST_Scope) return; + if (parent instanceof AST_Sequence) { + if (parent.tail_node() === child) continue; + break; + } + if (parent instanceof AST_Template) { + if (parent.tag) return; + break; + } + } + node.enclosed.forEach(function(def) { + if (def.scope !== node) enclosed.set(def.name, true); + }); + return true; + } else if (find_arguments && node instanceof AST_Sub) { + scope.each_argname(function(argname) { + if (!compressor.option("reduce_vars") || argname.definition().assignments) { + if (!argname.definition().fixed) well_defined = false; + lvalues.add(argname.name, true); + } + }); + find_arguments = false; + } + if (!scan_toplevel) return; + if (node.TYPE == "Call") { + if (modify_toplevel) return; + var exp = node.expression; + if (exp instanceof AST_PropAccess) return; + if (exp instanceof AST_LambdaExpression && !exp.contains_this()) return; + modify_toplevel = true; + } else if (node instanceof AST_PropAccess && may_be_global(node.expression)) { + if (node === lhs && !(expr instanceof AST_Unary)) { + modify_toplevel = true; + } else { + read_toplevel = true; + } + } + }); + expr.walk(tw); + return lvalues; + } + + function remove_candidate(expr) { + var value = rvalue === rhs_value ? null : make_sequence(rhs_value, rhs_value.expressions.slice(0, -1)); + var index = expr.name_index; + if (index >= 0) { + var args, argname = scope.argnames[index]; + if (argname instanceof AST_DefaultValue) { + scope.argnames[index] = argname = argname.clone(); + argname.value = value || make_node(AST_Number, argname, { value: 0 }); + } else if ((args = compressor.parent().args)[index]) { + scope.argnames[index] = argname.clone(); + args[index] = value || make_node(AST_Number, args[index], { value: 0 }); + } + return; + } + var end = hit_stack.length - 1; + var last = hit_stack[end]; + if (last instanceof AST_VarDef || hit_stack[end - 1].body === last) end--; + var tt = new TreeTransformer(function(node, descend, in_list) { + if (hit) return node; + if (node !== hit_stack[hit_index]) return node; + hit_index++; + if (hit_index <= end) return handle_custom_scan_order(node, tt); + hit = true; + if (node instanceof AST_Definitions) { + declare_only.set(last.name.name, (declare_only.get(last.name.name) || 0) + 1); + if (value_def) value_def.replaced++; + var defns = node.definitions; + var index = defns.indexOf(last); + var defn = last.clone(); + defn.value = null; + if (!value) { + node.definitions[index] = defn; + return node; + } + var body = [ make_node(AST_SimpleStatement, value, { body: value }) ]; + if (index > 0) { + var head = node.clone(); + head.definitions = defns.slice(0, index); + body.unshift(head); + node = node.clone(); + node.definitions = defns.slice(index); + } + body.push(node); + node.definitions[0] = defn; + return in_list ? List.splice(body) : make_node(AST_BlockStatement, node, { body: body }); + } + if (!value) return in_list ? List.skip : null; + return is_statement(node) ? make_node(AST_SimpleStatement, value, { body: value }) : value; + }, function(node, in_list) { + if (node instanceof AST_For) return patch_for_init(node, in_list); + return patch_sequence(node, tt); + }); + abort = false; + hit = false; + hit_index = 0; + if (!(statements[stat_index] = statements[stat_index].transform(tt))) statements.splice(stat_index, 1); + } + + function patch_sequence(node, tt) { + if (node instanceof AST_Sequence) switch (node.expressions.length) { + case 0: return null; + case 1: return maintain_this_binding(tt.parent(), node, node.expressions[0]); + } + } + + function is_lhs_local(lhs) { + var sym = root_expr(lhs); + if (!(sym instanceof AST_SymbolRef)) return false; + if (sym.definition().scope.resolve() !== scope) return false; + if (!in_loop) return true; + if (compound) return false; + if (candidate instanceof AST_Unary) return false; + var lvalue = lvalues.get(sym.name); + return !lvalue || lvalue[0] === lhs; + } + + function value_has_side_effects() { + if (candidate instanceof AST_Unary) return false; + return rvalue.has_side_effects(compressor); + } + + function replace_all_symbols(expr) { + if (expr instanceof AST_Unary) return false; + if (side_effects) return false; + if (value_def) return true; + if (!(lhs instanceof AST_SymbolRef)) return false; + var referenced; + if (expr instanceof AST_VarDef) { + referenced = 1; + } else if (expr.operator == "=") { + referenced = 2; + } else { + return false; + } + var def = lhs.definition(); + if (def.references.length - def.replaced == referenced) return true; + if (!def.fixed) return false; + if (!lhs.fixed) return false; + var assigns = lhs.fixed.assigns; + var matched = 0; + if (!all(def.references, function(ref, index) { + var fixed = ref.fixed; + if (!fixed) return false; + if (fixed.to_binary || fixed.to_prefix) return false; + if (fixed === lhs.fixed) { + matched++; + return true; + } + return assigns && fixed.assigns && assigns[0] !== fixed.assigns[0]; + })) return false; + if (matched != referenced) return false; + verify_ref = true; + return true; + } + + function symbol_in_lvalues(sym, parent) { + var lvalue = lvalues.get(sym.name); + if (!lvalue || all(lvalue, function(lhs) { + return !lhs; + })) return; + if (lvalue[0] !== lhs) return true; + scan_rhs = false; + } + + function may_modify(sym) { + var def = sym.definition(); + if (def.orig.length == 1 && def.orig[0] instanceof AST_SymbolDefun) return false; + if (def.scope.resolve() !== scope) return true; + if (modify_toplevel && compressor.exposed(def)) return true; + return !all(def.references, function(ref) { + return ref.scope.resolve(true) === scope; + }); + } + + function side_effects_external(node, lhs) { + if (node instanceof AST_Assign) return side_effects_external(node.left, true); + if (node instanceof AST_Unary) return side_effects_external(node.expression, true); + if (node instanceof AST_VarDef) return node.value && side_effects_external(node.value); + if (lhs) { + if (node instanceof AST_Dot) return side_effects_external(node.expression, true); + if (node instanceof AST_Sub) return side_effects_external(node.expression, true); + if (node instanceof AST_SymbolRef) return node.definition().scope.resolve() !== scope; + } + return false; + } + } + + function eliminate_spurious_blocks(statements) { + var changed = false, seen_dirs = []; + for (var i = 0; i < statements.length;) { + var stat = statements[i]; + if (stat instanceof AST_BlockStatement) { + if (all(stat.body, safe_to_trim)) { + changed = true; + eliminate_spurious_blocks(stat.body); + [].splice.apply(statements, [i, 1].concat(stat.body)); + i += stat.body.length; + continue; + } + } + if (stat instanceof AST_Directive) { + if (member(stat.value, seen_dirs)) { + changed = true; + statements.splice(i, 1); + continue; + } + seen_dirs.push(stat.value); + } + if (stat instanceof AST_EmptyStatement) { + changed = true; + statements.splice(i, 1); + continue; + } + i++; + } + return changed; + } + + function handle_if_return(statements, compressor) { + var changed = false; + var parent = compressor.parent(); + var self = compressor.self(); + var declare_only, jump, merge_jump; + var in_iife = in_lambda && parent && parent.TYPE == "Call" && parent.expression === self; + var chain_if_returns = in_lambda && compressor.option("conditionals") && compressor.option("sequences"); + var drop_return_void = !(in_try && in_try.bfinally && in_async_generator(scope)); + var multiple_if_returns = has_multiple_if_returns(statements); + for (var i = statements.length; --i >= 0;) { + var stat = statements[i]; + var j = next_index(i); + var next = statements[j]; + + if (in_lambda && declare_only && !next && stat instanceof AST_Return + && drop_return_void && !(self instanceof AST_SwitchBranch)) { + var body = stat.value; + if (!body) { + changed = true; + statements.splice(i, 1); + continue; + } + var tail = body.tail_node(); + if (is_undefined(tail)) { + changed = true; + if (body instanceof AST_UnaryPrefix) { + body = body.expression; + } else if (tail instanceof AST_UnaryPrefix) { + body = body.clone(); + body.expressions[body.expressions.length - 1] = tail.expression; + } + statements[i] = make_node(AST_SimpleStatement, stat, { body: body }); + continue; + } + } + + if (stat instanceof AST_If) { + var ab = aborts(stat.body); + // if (foo()) { bar(); return; } else baz(); moo(); ---> if (foo()) bar(); else { baz(); moo(); } + if (can_merge_flow(ab)) { + if (ab.label) remove(ab.label.thedef.references, ab); + changed = true; + stat = stat.clone(); + stat.body = make_node(AST_BlockStatement, stat, { + body: as_statement_array_with_return(stat.body, ab), + }); + stat.alternative = make_node(AST_BlockStatement, stat, { + body: as_statement_array(stat.alternative).concat(extract_functions(merge_jump, jump)), + }); + adjust_refs(ab.value, merge_jump); + statements[i] = stat; + statements[i] = stat.transform(compressor); + continue; + } + // if (foo()) { bar(); return x; } return y; ---> if (!foo()) return y; bar(); return x; + if (ab && !stat.alternative && next instanceof AST_Jump) { + var cond = stat.condition; + var preference = i + 1 == j && stat.body instanceof AST_BlockStatement; + cond = best_of_expression(cond, cond.negate(compressor), preference); + if (cond !== stat.condition) { + changed = true; + stat = stat.clone(); + stat.condition = cond; + var body = stat.body; + stat.body = make_node(AST_BlockStatement, next, { + body: extract_functions(true, null, j + 1), + }); + statements.splice(i, 1, stat, body); + // proceed further only if `TreeWalker.stack` is in a consistent state + // https://github.com/mishoo/UglifyJS/issues/5595 + // https://github.com/mishoo/UglifyJS/issues/5597 + if (!in_lambda || self instanceof AST_Block && self.body === statements) { + statements[i] = stat.transform(compressor); + } + continue; + } + } + var alt = aborts(stat.alternative); + // if (foo()) bar(); else { baz(); return; } moo(); ---> if (foo()) { bar(); moo(); } else baz(); + if (can_merge_flow(alt)) { + if (alt.label) remove(alt.label.thedef.references, alt); + changed = true; + stat = stat.clone(); + stat.body = make_node(AST_BlockStatement, stat.body, { + body: as_statement_array(stat.body).concat(extract_functions(merge_jump, jump)), + }); + stat.alternative = make_node(AST_BlockStatement, stat.alternative, { + body: as_statement_array_with_return(stat.alternative, alt), + }); + adjust_refs(alt.value, merge_jump); + statements[i] = stat; + statements[i] = stat.transform(compressor); + continue; + } + if (compressor.option("typeofs")) { + if (ab && !alt) { + var stats = make_node(AST_BlockStatement, self, { body: statements.slice(i + 1) }); + mark_locally_defined(stat.condition, null, stats); + } + if (!ab && alt) { + var stats = make_node(AST_BlockStatement, self, { body: statements.slice(i + 1) }); + mark_locally_defined(stat.condition, stats); + } + } + } + + if (stat instanceof AST_If && stat.body instanceof AST_Return) { + var value = stat.body.value; + var in_bool = stat.body.in_bool || next instanceof AST_Return && next.in_bool; + // if (foo()) return x; return y; ---> return foo() ? x : y; + if (!stat.alternative && next instanceof AST_Return + && (drop_return_void || !value == !next.value)) { + changed = true; + stat = stat.clone(); + stat.alternative = make_node(AST_BlockStatement, next, { + body: extract_functions(true, null, j + 1), + }); + statements[i] = stat; + statements[i] = stat.transform(compressor); + continue; + } + // if (foo()) return x; [ return ; ] ---> return foo() ? x : undefined; + // if (foo()) return bar() ? x : void 0; ---> return foo() && bar() ? x : void 0; + // if (foo()) return bar() ? void 0 : x; ---> return !foo() || bar() ? void 0 : x; + if (in_lambda && declare_only && !next && !stat.alternative && (in_bool + || value && multiple_if_returns + || value instanceof AST_Conditional && (is_undefined(value.consequent, compressor) + || is_undefined(value.alternative, compressor)))) { + changed = true; + stat = stat.clone(); + stat.alternative = make_node(AST_Return, stat, { value: null }); + statements[i] = stat; + statements[i] = stat.transform(compressor); + continue; + } + // if (a) return b; if (c) return d; e; ---> return a ? b : c ? d : void e; + // + // if sequences is not enabled, this can lead to an endless loop (issue #866). + // however, with sequences on this helps producing slightly better output for + // the example code. + var prev, prev_stat; + if (chain_if_returns && !stat.alternative + && (!(prev_stat = statements[prev = prev_index(i)]) && in_iife + || prev_stat instanceof AST_If && prev_stat.body instanceof AST_Return) + && (!next ? !declare_only + : next instanceof AST_SimpleStatement && next_index(j) == statements.length)) { + changed = true; + var exprs = []; + stat = stat.clone(); + exprs.push(stat.condition); + stat.condition = make_sequence(stat, exprs); + stat.alternative = make_node(AST_BlockStatement, self, { + body: extract_functions().concat(make_node(AST_Return, self, { value: null })), + }); + statements[i] = stat.transform(compressor); + i = prev + 1; + continue; + } + } + + if (stat instanceof AST_Break || stat instanceof AST_Exit) { + jump = stat; + continue; + } + + if (declare_only && jump && jump === next) eliminate_returns(stat); + } + return changed; + + function has_multiple_if_returns(statements) { + var n = 0; + for (var i = statements.length; --i >= 0;) { + var stat = statements[i]; + if (stat instanceof AST_If && stat.body instanceof AST_Return) { + if (++n > 1) return true; + } + } + return false; + } + + function match_target(target) { + return last_of(compressor, function(node) { + return node === target; + }); + } + + function match_return(ab, exact) { + if (!jump) return false; + if (jump.TYPE != ab.TYPE) return false; + var value = ab.value; + if (!value) return false; + var equals = jump.equals(ab); + if (!equals && value instanceof AST_Sequence) { + value = value.tail_node(); + if (jump.value && jump.value.equals(value)) equals = 2; + } + if (!equals && !exact && jump.value instanceof AST_Sequence) { + if (jump.value.tail_node().equals(value)) equals = 3; + } + return equals; + } + + function can_drop_abort(ab) { + if (ab instanceof AST_Exit) { + if (merge_jump = match_return(ab)) return true; + if (!in_lambda) return false; + if (!(ab instanceof AST_Return)) return false; + var value = ab.value; + if (value) { + if (!drop_return_void) return false; + if (!is_undefined(value.tail_node())) return false; + } + if (!(self instanceof AST_SwitchBranch)) return true; + if (!jump) return false; + if (jump instanceof AST_Exit && jump.value) return false; + merge_jump = 4; + return true; + } + if (!(ab instanceof AST_LoopControl)) return false; + if (self instanceof AST_SwitchBranch) { + if (jump instanceof AST_Exit) { + if (!in_lambda) return false; + if (jump.value) return false; + merge_jump = true; + } else if (jump) { + if (compressor.loopcontrol_target(jump) !== parent) return false; + merge_jump = true; + } else if (jump === false) { + return false; + } + } + var lct = compressor.loopcontrol_target(ab); + if (ab instanceof AST_Continue) return match_target(loop_body(lct)); + if (lct instanceof AST_IterationStatement) return false; + return match_target(lct); + } + + function can_merge_flow(ab) { + merge_jump = false; + if (!can_drop_abort(ab)) return false; + for (var j = statements.length; --j > i;) { + var stat = statements[j]; + if (stat instanceof AST_DefClass) { + if (stat.name.definition().preinit) return false; + } else if (stat instanceof AST_Const || stat instanceof AST_Let) { + if (!all(stat.definitions, function(defn) { + return !defn.name.match_symbol(function(node) { + return node instanceof AST_SymbolDeclaration && node.definition().preinit; + }); + })) return false; + } + } + return true; + } + + function extract_functions(mode, stop, end) { + var defuns = []; + var lexical = false; + var start = i + 1; + if (!mode) { + end = statements.length; + jump = null; + } else if (stop) { + end = statements.lastIndexOf(stop); + } else { + stop = statements[end]; + if (stop !== jump) jump = false; + } + var tail = statements.splice(start, end - start).filter(function(stat) { + if (stat instanceof AST_LambdaDefinition) { + defuns.push(stat); + return false; + } + if (is_lexical_definition(stat)) lexical = true; + return true; + }); + if (mode === 3) { + tail.push(make_node(AST_SimpleStatement, stop.value, { + body: make_sequence(stop.value, stop.value.expressions.slice(0, -1)), + })); + stop.value = stop.value.tail_node(); + } + [].push.apply(lexical ? tail : statements, defuns); + return tail; + } + + function trim_return(value, mode) { + if (value) switch (mode) { + case 4: + return value; + case 3: + if (!(value instanceof AST_Sequence)) break; + case 2: + return make_sequence(value, value.expressions.slice(0, -1)); + } + } + + function as_statement_array_with_return(node, ab) { + var body = as_statement_array(node); + var block = body, last; + while ((last = block[block.length - 1]) !== ab) { + block = last.body; + } + block.pop(); + var value = ab.value; + if (merge_jump) value = trim_return(value, merge_jump); + if (value) block.push(make_node(AST_SimpleStatement, value, { body: value })); + return body; + } + + function adjust_refs(value, mode) { + if (!mode) return; + if (!value) return; + switch (mode) { + case 4: + return; + case 3: + case 2: + value = value.tail_node(); + } + merge_expression(value, jump.value); + } + + function next_index(i) { + declare_only = true; + for (var j = i; ++j < statements.length;) { + var stat = statements[j]; + if (is_declaration(stat)) continue; + if (stat instanceof AST_Var) { + declare_only = false; + continue; + } + break; + } + return j; + } + + function prev_index(i) { + for (var j = i; --j >= 0;) { + var stat = statements[j]; + if (stat instanceof AST_Var) continue; + if (is_declaration(stat)) continue; + break; + } + return j; + } + + function eliminate_returns(stat, keep_throws, in_block) { + if (stat instanceof AST_Exit) { + var mode = !(keep_throws && stat instanceof AST_Throw) && match_return(stat, true); + if (mode) { + changed = true; + var value = trim_return(stat.value, mode); + if (value) return make_node(AST_SimpleStatement, value, { body: value }); + return in_block ? null : make_node(AST_EmptyStatement, stat); + } + } else if (stat instanceof AST_If) { + stat.body = eliminate_returns(stat.body, keep_throws); + if (stat.alternative) stat.alternative = eliminate_returns(stat.alternative, keep_throws); + } else if (stat instanceof AST_LabeledStatement) { + stat.body = eliminate_returns(stat.body, keep_throws); + } else if (stat instanceof AST_Try) { + if (!stat.bfinally || !jump.value || jump.value.is_constant()) { + if (stat.bcatch) eliminate_returns(stat.bcatch, keep_throws); + var trimmed = eliminate_returns(stat.body.pop(), true, true); + if (trimmed) stat.body.push(trimmed); + } + } else if (stat instanceof AST_Block && !(stat instanceof AST_Scope || stat instanceof AST_Switch)) { + var trimmed = eliminate_returns(stat.body.pop(), keep_throws, true); + if (trimmed) stat.body.push(trimmed); + } + return stat; + } + } + + function eliminate_dead_code(statements, compressor) { + var has_quit; + var self = compressor.self(); + if (self instanceof AST_Catch) { + self = compressor.parent(); + } else if (self instanceof AST_LabeledStatement) { + self = self.body; + } + for (var i = 0, n = 0, len = statements.length; i < len; i++) { + var stat = statements[i]; + if (stat instanceof AST_LoopControl) { + var lct = compressor.loopcontrol_target(stat); + if (loop_body(lct) !== self + || stat instanceof AST_Break && lct instanceof AST_IterationStatement) { + statements[n++] = stat; + } else if (stat.label) { + remove(stat.label.thedef.references, stat); + } + } else { + statements[n++] = stat; + } + if (aborts(stat)) { + has_quit = statements.slice(i + 1); + break; + } + } + statements.length = n; + if (has_quit) has_quit.forEach(function(stat) { + extract_declarations_from_unreachable_code(compressor, stat, statements); + }); + return statements.length != len; + } + + function trim_awaits(statements, compressor) { + if (!in_lambda || in_try && in_try.bfinally) return; + var changed = false; + for (var index = statements.length; --index >= 0;) { + var stat = statements[index]; + if (!(stat instanceof AST_SimpleStatement)) break; + var node = stat.body; + if (!(node instanceof AST_Await)) break; + var exp = node.expression; + if (!needs_enqueuing(compressor, exp)) break; + changed = true; + exp = exp.drop_side_effect_free(compressor, true); + if (exp) { + stat.body = exp; + break; + } + } + statements.length = index + 1; + return changed; + } + + function inline_iife(statements, compressor) { + var changed = false; + var index = statements.length - 1; + if (in_lambda && index >= 0) { + var no_return = in_try && in_try.bfinally && in_async_generator(scope); + var inlined = statements[index].try_inline(compressor, block_scope, no_return); + if (inlined) { + statements[index--] = inlined; + changed = true; + } + } + var loop = in_loop && in_try && in_try.bfinally ? "try" : in_loop; + for (; index >= 0; index--) { + var inlined = statements[index].try_inline(compressor, block_scope, true, loop); + if (!inlined) continue; + statements[index] = inlined; + changed = true; + } + return changed; + } + + function sequencesize(statements, compressor) { + if (statements.length < 2) return; + var seq = [], n = 0; + function push_seq() { + if (!seq.length) return; + var body = make_sequence(seq[0], seq); + statements[n++] = make_node(AST_SimpleStatement, body, { body: body }); + seq = []; + } + for (var i = 0, len = statements.length; i < len; i++) { + var stat = statements[i]; + if (stat instanceof AST_SimpleStatement) { + if (seq.length >= compressor.sequences_limit) push_seq(); + merge_sequence(seq, stat.body); + } else if (is_declaration(stat)) { + statements[n++] = stat; + } else { + push_seq(); + statements[n++] = stat; + } + } + push_seq(); + statements.length = n; + return n != len; + } + + function to_simple_statement(block, decls) { + if (!(block instanceof AST_BlockStatement)) return block; + var stat = null; + for (var i = 0; i < block.body.length; i++) { + var line = block.body[i]; + if (line instanceof AST_Var && declarations_only(line)) { + decls.push(line); + } else if (stat || is_lexical_definition(line)) { + return false; + } else { + stat = line; + } + } + return stat; + } + + function sequencesize_2(statements, compressor) { + var changed = false, n = 0, prev; + for (var i = 0; i < statements.length; i++) { + var stat = statements[i]; + if (prev) { + if (stat instanceof AST_Exit) { + if (stat.value || !in_async_generator(scope)) { + stat.value = cons_seq(stat.value || make_node(AST_Undefined, stat)).optimize(compressor); + } + } else if (stat instanceof AST_For) { + if (!(stat.init instanceof AST_Definitions)) { + var abort = false; + prev.body.walk(new TreeWalker(function(node) { + if (abort || node instanceof AST_Scope) return true; + if (node instanceof AST_Binary && node.operator == "in") { + abort = true; + return true; + } + })); + if (!abort) { + if (stat.init) stat.init = cons_seq(stat.init); + else { + stat.init = prev.body; + n--; + changed = true; + } + } + } + } else if (stat instanceof AST_ForIn) { + if (!is_lexical_definition(stat.init)) stat.object = cons_seq(stat.object); + } else if (stat instanceof AST_If) { + stat.condition = cons_seq(stat.condition); + } else if (stat instanceof AST_Switch) { + stat.expression = cons_seq(stat.expression); + } else if (stat instanceof AST_With) { + stat.expression = cons_seq(stat.expression); + } + } + if (compressor.option("conditionals") && stat instanceof AST_If) { + var decls = []; + var body = to_simple_statement(stat.body, decls); + var alt = to_simple_statement(stat.alternative, decls); + if (body !== false && alt !== false && decls.length > 0) { + var len = decls.length; + decls.push(make_node(AST_If, stat, { + condition: stat.condition, + body: body || make_node(AST_EmptyStatement, stat.body), + alternative: alt, + })); + decls.unshift(n, 1); + [].splice.apply(statements, decls); + i += len; + n += len + 1; + prev = null; + changed = true; + continue; + } + } + statements[n++] = stat; + prev = stat instanceof AST_SimpleStatement ? stat : null; + } + statements.length = n; + return changed; + + function cons_seq(right) { + n--; + changed = true; + var left = prev.body; + return make_sequence(left, [ left, right ]); + } + } + + function extract_exprs(body) { + if (body instanceof AST_Assign) return [ body ]; + if (body instanceof AST_Sequence) return body.expressions.slice(); + } + + function join_assigns(defn, body, keep) { + var exprs = extract_exprs(body); + if (!exprs) return; + keep = keep || 0; + var trimmed = false; + for (var i = exprs.length - keep; --i >= 0;) { + var expr = exprs[i]; + if (!can_trim(expr)) continue; + var tail; + if (expr.left instanceof AST_SymbolRef) { + tail = exprs.slice(i + 1); + } else if (expr.left instanceof AST_PropAccess && can_trim(expr.left.expression)) { + tail = exprs.slice(i + 1); + var flattened = expr.clone(); + expr = expr.left.expression; + flattened.left = flattened.left.clone(); + flattened.left.expression = expr.left.clone(); + tail.unshift(flattened); + } else { + continue; + } + if (tail.length == 0) continue; + if (!trim_assigns(expr.left, expr.right, tail)) continue; + trimmed = true; + exprs = exprs.slice(0, i).concat(expr, tail); + } + if (defn instanceof AST_Definitions) { + for (var i = defn.definitions.length; --i >= 0;) { + var def = defn.definitions[i]; + if (!def.value) continue; + if (trim_assigns(def.name, def.value, exprs)) trimmed = true; + if (merge_conditional_assignments(def, exprs, keep)) trimmed = true; + break; + } + if (defn instanceof AST_Var && join_var_assign(defn.definitions, exprs, keep)) trimmed = true; + } + return trimmed && exprs; + + function can_trim(node) { + return node instanceof AST_Assign && node.operator == "="; + } + } + + function merge_assigns(prev, defn) { + if (!(prev instanceof AST_SimpleStatement)) return; + if (declarations_only(defn)) return; + var exprs = extract_exprs(prev.body); + if (!exprs) return; + var definitions = []; + if (!join_var_assign(definitions, exprs.reverse(), 0)) return; + defn.definitions = definitions.reverse().concat(defn.definitions); + return exprs.reverse(); + } + + function merge_conditional_assignments(var_def, exprs, keep) { + if (!compressor.option("conditionals")) return; + if (var_def.name instanceof AST_Destructured) return; + var trimmed = false; + var def = var_def.name.definition(); + while (exprs.length > keep) { + var cond = to_conditional_assignment(compressor, def, var_def.value, exprs[0]); + if (!cond) break; + var_def.value = cond; + exprs.shift(); + trimmed = true; + } + return trimmed; + } + + function join_var_assign(definitions, exprs, keep) { + var trimmed = false; + while (exprs.length > keep) { + var expr = exprs[0]; + if (!(expr instanceof AST_Assign)) break; + if (expr.operator != "=") break; + var lhs = expr.left; + if (!(lhs instanceof AST_SymbolRef)) break; + if (is_undeclared_ref(lhs)) break; + if (lhs.scope.resolve() !== scope) break; + var def = lhs.definition(); + if (def.scope !== scope) break; + if (def.orig.length > def.eliminated + 1) break; + if (def.orig[0].TYPE != "SymbolVar") break; + var name = make_node(AST_SymbolVar, lhs); + definitions.push(make_node(AST_VarDef, expr, { + name: name, + value: expr.right, + })); + def.orig.push(name); + def.replaced++; + exprs.shift(); + trimmed = true; + } + return trimmed; + } + + function trim_assigns(name, value, exprs) { + var names = new Dictionary(); + names.set(name.name, true); + while (value instanceof AST_Assign && value.operator == "=") { + if (value.left instanceof AST_SymbolRef) names.set(value.left.name, true); + value = value.right; + } + if (!(value instanceof AST_Object)) return; + var trimmed = false; + do { + if (!try_join(exprs[0])) break; + exprs.shift(); + trimmed = true; + } while (exprs.length); + return trimmed; + + function try_join(node) { + if (!(node instanceof AST_Assign)) return; + if (node.operator != "=") return; + if (!(node.left instanceof AST_PropAccess)) return; + var sym = node.left.expression; + if (!(sym instanceof AST_SymbolRef)) return; + if (!names.has(sym.name)) return; + if (!node.right.is_constant_expression(scope)) return; + var prop = node.left.property; + if (prop instanceof AST_Node) { + if (try_join(prop)) prop = node.left.property = prop.right.clone(); + prop = prop.evaluate(compressor); + } + if (prop instanceof AST_Node) return; + prop = "" + prop; + var diff = prop == "__proto__" || compressor.has_directive("use strict") ? function(node) { + var key = node.key; + return typeof key == "string" && key != prop && key != "__proto__"; + } : function(node) { + var key = node.key; + if (node instanceof AST_ObjectGetter || node instanceof AST_ObjectSetter) { + return typeof key == "string" && key != prop; + } + return key !== "__proto__"; + }; + if (!all(value.properties, diff)) return; + value.properties.push(make_node(AST_ObjectKeyVal, node, { + key: prop, + value: node.right, + })); + return true; + } + } + + function join_consecutive_vars(statements) { + var changed = false, defs, prev_defs; + for (var i = 0, j = -1; i < statements.length; i++) { + var stat = statements[i]; + var prev = statements[j]; + if (stat instanceof AST_Definitions) { + if (prev && prev.TYPE == stat.TYPE) { + prev.definitions = prev.definitions.concat(stat.definitions); + changed = true; + } else if (stat && prev instanceof AST_Let && stat.can_letify(compressor)) { + prev.definitions = prev.definitions.concat(to_let(stat, block_scope).definitions); + changed = true; + } else if (prev && stat instanceof AST_Let && prev.can_letify(compressor)) { + defs = prev_defs; + statements[j] = prev = to_let(prev, block_scope); + prev.definitions = prev.definitions.concat(stat.definitions); + changed = true; + } else if (defs && defs.TYPE == stat.TYPE && declarations_only(stat)) { + defs.definitions = defs.definitions.concat(stat.definitions); + changed = true; + } else if (stat instanceof AST_Var) { + var exprs = merge_assigns(prev, stat); + if (exprs) { + if (exprs.length) { + prev.body = make_sequence(prev, exprs); + j++; + } + changed = true; + } else { + j++; + } + prev_defs = defs; + statements[j] = defs = stat; + } else { + statements[++j] = stat; + } + continue; + } else if (stat instanceof AST_Exit) { + stat.value = join_assigns_expr(stat.value); + } else if (stat instanceof AST_For) { + var exprs = join_assigns(prev, stat.init); + if (exprs) { + changed = true; + stat.init = exprs.length ? make_sequence(stat.init, exprs) : null; + } else if (prev instanceof AST_Var && (!stat.init || stat.init.TYPE == prev.TYPE)) { + if (stat.init) { + prev.definitions = prev.definitions.concat(stat.init.definitions); + } + stat = stat.clone(); + prev_defs = defs; + defs = stat.init = prev; + statements[j] = merge_defns(stat); + changed = true; + continue; + } else if (defs && stat.init && defs.TYPE == stat.init.TYPE && declarations_only(stat.init)) { + defs.definitions = defs.definitions.concat(stat.init.definitions); + stat.init = null; + changed = true; + } else if (stat.init instanceof AST_Var) { + prev_defs = defs; + defs = stat.init; + exprs = merge_assigns(prev, stat.init); + if (exprs) { + changed = true; + if (exprs.length == 0) { + statements[j] = merge_defns(stat); + continue; + } + prev.body = make_sequence(prev, exprs); + } + } + } else if (stat instanceof AST_ForEnumeration) { + if (defs && defs.TYPE == stat.init.TYPE) { + var defns = defs.definitions.slice(); + stat.init = stat.init.definitions[0].name.convert_symbol(AST_SymbolRef, function(ref, name) { + defns.push(make_node(AST_VarDef, name, { + name: name, + value: null, + })); + name.definition().references.push(ref); + }); + defs.definitions = defns; + changed = true; + } + stat.object = join_assigns_expr(stat.object); + } else if (stat instanceof AST_If) { + stat.condition = join_assigns_expr(stat.condition); + } else if (stat instanceof AST_SimpleStatement) { + var exprs = join_assigns(prev, stat.body), next; + if (exprs) { + changed = true; + if (!exprs.length) continue; + stat.body = make_sequence(stat.body, exprs); + } else if (prev instanceof AST_Definitions + && (next = statements[i + 1]) + && prev.TYPE == next.TYPE + && (next = next.definitions[0]).value) { + changed = true; + next.value = make_sequence(stat, [ stat.body, next.value ]); + continue; + } + } else if (stat instanceof AST_Switch) { + stat.expression = join_assigns_expr(stat.expression); + } else if (stat instanceof AST_With) { + stat.expression = join_assigns_expr(stat.expression); + } + statements[++j] = defs ? merge_defns(stat) : stat; + } + statements.length = j + 1; + return changed; + + function join_assigns_expr(value) { + var exprs = join_assigns(prev, value, 1); + if (!exprs) return value; + changed = true; + var tail = value.tail_node(); + if (exprs[exprs.length - 1] !== tail) exprs.push(tail.left); + return make_sequence(value, exprs); + } + + function merge_defns(stat) { + return stat.transform(new TreeTransformer(function(node, descend, in_list) { + if (node instanceof AST_Definitions) { + if (defs === node) return node; + if (defs.TYPE != node.TYPE) return node; + var parent = this.parent(); + if (parent instanceof AST_ForEnumeration && parent.init === node) return node; + if (!declarations_only(node)) return node; + defs.definitions = defs.definitions.concat(node.definitions); + changed = true; + if (parent instanceof AST_For && parent.init === node) return null; + return in_list ? List.skip : make_node(AST_EmptyStatement, node); + } + if (node instanceof AST_ExportDeclaration) return node; + if (node instanceof AST_Scope) return node; + if (!is_statement(node)) return node; + })); + } + } + } + + function extract_declarations_from_unreachable_code(compressor, stat, target) { + var block; + var dropped = false; + stat.walk(new TreeWalker(function(node, descend) { + if (node instanceof AST_DefClass) { + node.extends = null; + node.properties = []; + push(node); + return true; + } + if (node instanceof AST_Definitions) { + var defns = []; + if (node.remove_initializers(compressor, defns)) { + AST_Node.warn("Dropping initialization in unreachable code [{start}]", node); + } + if (defns.length > 0) { + node.definitions = defns; + push(node); + } + return true; + } + if (node instanceof AST_LambdaDefinition) { + push(node); + return true; + } + if (node instanceof AST_Scope) return true; + if (node instanceof AST_BlockScope) { + var save = block; + block = []; + descend(); + if (block.required) { + target.push(make_node(AST_BlockStatement, stat, { body: block })); + } else if (block.length) { + [].push.apply(target, block); + } + block = save; + return true; + } + if (!(node instanceof AST_LoopControl)) dropped = true; + })); + if (dropped) AST_Node.warn("Dropping unreachable code [{start}]", stat); + + function push(node) { + if (block) { + block.push(node); + if (!safe_to_trim(node)) block.required = true; + } else { + target.push(node); + } + } + } + + function is_undefined(node, compressor) { + return node == null + || node.is_undefined + || node instanceof AST_Undefined + || node instanceof AST_UnaryPrefix + && node.operator == "void" + && !(compressor && node.expression.has_side_effects(compressor)); + } + + // in_strict_mode() + // return true if scope executes in Strict Mode + (function(def) { + def(AST_Class, return_true); + def(AST_Scope, function(compressor) { + var body = this.body; + for (var i = 0; i < body.length; i++) { + var stat = body[i]; + if (!(stat instanceof AST_Directive)) break; + if (stat.value == "use strict") return true; + } + var parent = this.parent_scope; + if (!parent) return compressor.option("module"); + return parent.resolve(true).in_strict_mode(compressor); + }); + })(function(node, func) { + node.DEFMETHOD("in_strict_mode", func); + }); + + // is_truthy() + // return true if `!!node === true` + (function(def) { + def(AST_Node, return_false); + def(AST_Array, return_true); + def(AST_Assign, function() { + return this.operator == "=" && this.right.is_truthy(); + }); + def(AST_Lambda, return_true); + def(AST_Object, return_true); + def(AST_RegExp, return_true); + def(AST_Sequence, function() { + return this.tail_node().is_truthy(); + }); + def(AST_SymbolRef, function() { + var fixed = this.fixed_value(); + if (!fixed) return false; + this.is_truthy = return_false; + var result = fixed.is_truthy(); + delete this.is_truthy; + return result; + }); + })(function(node, func) { + node.DEFMETHOD("is_truthy", func); + }); + + // is_negative_zero() + // return true if the node may represent -0 + (function(def) { + def(AST_Node, return_true); + def(AST_Array, return_false); + function binary(op, left, right) { + switch (op) { + case "-": + return left.is_negative_zero() + && (!(right instanceof AST_Constant) || right.value == 0); + case "&&": + case "||": + return left.is_negative_zero() || right.is_negative_zero(); + case "*": + case "/": + case "%": + case "**": + return true; + default: + return false; + } + } + def(AST_Assign, function() { + var op = this.operator; + if (op == "=") return this.right.is_negative_zero(); + return binary(op.slice(0, -1), this.left, this.right); + }); + def(AST_Binary, function() { + return binary(this.operator, this.left, this.right); + }); + def(AST_Constant, function() { + return this.value == 0 && 1 / this.value < 0; + }); + def(AST_Lambda, return_false); + def(AST_Object, return_false); + def(AST_RegExp, return_false); + def(AST_Sequence, function() { + return this.tail_node().is_negative_zero(); + }); + def(AST_SymbolRef, function() { + var fixed = this.fixed_value(); + if (!fixed) return true; + this.is_negative_zero = return_true; + var result = fixed.is_negative_zero(); + delete this.is_negative_zero; + return result; + }); + def(AST_UnaryPrefix, function() { + return this.operator == "+" && this.expression.is_negative_zero() + || this.operator == "-"; + }); + })(function(node, func) { + node.DEFMETHOD("is_negative_zero", func); + }); + + // may_throw_on_access() + // returns true if this node may be null, undefined or contain `AST_Accessor` + (function(def) { + AST_Node.DEFMETHOD("may_throw_on_access", function(compressor, force) { + return !compressor.option("pure_getters") || this._dot_throw(compressor, force); + }); + function is_strict(compressor, force) { + return force || /strict/.test(compressor.option("pure_getters")); + } + def(AST_Node, is_strict); + def(AST_Array, return_false); + def(AST_Assign, function(compressor) { + var op = this.operator; + var sym = this.left; + var rhs = this.right; + if (op != "=") { + return lazy_op[op.slice(0, -1)] && (sym._dot_throw(compressor) || rhs._dot_throw(compressor)); + } + if (!rhs._dot_throw(compressor)) return false; + if (!(sym instanceof AST_SymbolRef)) return true; + if (rhs instanceof AST_Binary && rhs.operator == "||" && sym.name == rhs.left.name) { + return rhs.right._dot_throw(compressor); + } + return true; + }); + def(AST_Binary, function(compressor) { + return lazy_op[this.operator] && (this.left._dot_throw(compressor) || this.right._dot_throw(compressor)); + }); + def(AST_Class, function(compressor, force) { + return is_strict(compressor, force) && !all(this.properties, function(prop) { + if (prop.private) return true; + if (!prop.static) return true; + return !(prop instanceof AST_ClassGetter || prop instanceof AST_ClassSetter); + }); + }); + def(AST_Conditional, function(compressor) { + return this.consequent._dot_throw(compressor) || this.alternative._dot_throw(compressor); + }); + def(AST_Constant, return_false); + def(AST_Dot, function(compressor, force) { + if (!is_strict(compressor, force)) return false; + var exp = this.expression; + if (exp instanceof AST_SymbolRef) exp = exp.fixed_value(); + return !(this.property == "prototype" && is_lambda(exp)); + }); + def(AST_Lambda, return_false); + def(AST_Null, return_true); + def(AST_Object, function(compressor, force) { + return is_strict(compressor, force) && !all(this.properties, function(prop) { + if (prop instanceof AST_ObjectGetter || prop instanceof AST_ObjectSetter) return false; + return !(prop.key === "__proto__" && prop.value._dot_throw(compressor, force)); + }); + }); + def(AST_ObjectIdentity, function(compressor, force) { + return is_strict(compressor, force) && !this.scope.resolve().new; + }); + def(AST_Sequence, function(compressor) { + return this.tail_node()._dot_throw(compressor); + }); + def(AST_SymbolRef, function(compressor, force) { + if (this.is_undefined) return true; + if (!is_strict(compressor, force)) return false; + if (is_undeclared_ref(this) && this.is_declared(compressor)) return false; + if (this.is_immutable()) return false; + var def = this.definition(); + if (is_arguments(def) && !def.scope.rest && all(def.scope.argnames, function(argname) { + return argname instanceof AST_SymbolFunarg; + })) return def.scope.uses_arguments > 2; + var fixed = this.fixed_value(true); + if (!fixed) return true; + this._dot_throw = return_true; + if (fixed._dot_throw(compressor)) { + delete this._dot_throw; + return true; + } + this._dot_throw = return_false; + return false; + }); + def(AST_UnaryPrefix, function() { + return this.operator == "void"; + }); + def(AST_UnaryPostfix, return_false); + def(AST_Undefined, return_true); + })(function(node, func) { + node.DEFMETHOD("_dot_throw", func); + }); + + (function(def) { + def(AST_Node, return_false); + def(AST_Array, return_true); + function is_binary_defined(compressor, op, node) { + switch (op) { + case "&&": + return node.left.is_defined(compressor) && node.right.is_defined(compressor); + case "||": + return node.left.is_truthy() || node.right.is_defined(compressor); + case "??": + return node.left.is_defined(compressor) || node.right.is_defined(compressor); + default: + return true; + } + } + def(AST_Assign, function(compressor) { + var op = this.operator; + if (op == "=") return this.right.is_defined(compressor); + return is_binary_defined(compressor, op.slice(0, -1), this); + }); + def(AST_Binary, function(compressor) { + return is_binary_defined(compressor, this.operator, this); + }); + def(AST_Conditional, function(compressor) { + return this.consequent.is_defined(compressor) && this.alternative.is_defined(compressor); + }); + def(AST_Constant, return_true); + def(AST_Hole, return_false); + def(AST_Lambda, return_true); + def(AST_Object, return_true); + def(AST_Sequence, function(compressor) { + return this.tail_node().is_defined(compressor); + }); + def(AST_SymbolRef, function(compressor) { + if (this.is_undefined) return false; + if (is_undeclared_ref(this) && this.is_declared(compressor)) return true; + if (this.is_immutable()) return true; + var fixed = this.fixed_value(); + if (!fixed) return false; + this.is_defined = return_false; + var result = fixed.is_defined(compressor); + delete this.is_defined; + return result; + }); + def(AST_UnaryPrefix, function() { + return this.operator != "void"; + }); + def(AST_UnaryPostfix, return_true); + def(AST_Undefined, return_false); + })(function(node, func) { + node.DEFMETHOD("is_defined", func); + }); + + /* -----[ boolean/negation helpers ]----- */ + + // methods to determine whether an expression has a boolean result type + (function(def) { + def(AST_Node, return_false); + def(AST_Assign, function(compressor) { + return this.operator == "=" && this.right.is_boolean(compressor); + }); + var binary = makePredicate("in instanceof == != === !== < <= >= >"); + def(AST_Binary, function(compressor) { + return binary[this.operator] || lazy_op[this.operator] + && this.left.is_boolean(compressor) + && this.right.is_boolean(compressor); + }); + def(AST_Boolean, return_true); + var fn = makePredicate("every hasOwnProperty isPrototypeOf propertyIsEnumerable some"); + def(AST_Call, function(compressor) { + if (!compressor.option("unsafe")) return false; + var exp = this.expression; + return exp instanceof AST_Dot && (fn[exp.property] + || exp.property == "test" && exp.expression instanceof AST_RegExp); + }); + def(AST_Conditional, function(compressor) { + return this.consequent.is_boolean(compressor) && this.alternative.is_boolean(compressor); + }); + def(AST_New, return_false); + def(AST_Sequence, function(compressor) { + return this.tail_node().is_boolean(compressor); + }); + def(AST_SymbolRef, function(compressor) { + var fixed = this.fixed_value(); + if (!fixed) return false; + this.is_boolean = return_false; + var result = fixed.is_boolean(compressor); + delete this.is_boolean; + return result; + }); + var unary = makePredicate("! delete"); + def(AST_UnaryPrefix, function() { + return unary[this.operator]; + }); + })(function(node, func) { + node.DEFMETHOD("is_boolean", func); + }); + + // methods to determine if an expression has a numeric result type + (function(def) { + def(AST_Node, return_false); + var binary = makePredicate("- * / % ** & | ^ << >> >>>"); + def(AST_Assign, function(compressor) { + return binary[this.operator.slice(0, -1)] + || this.operator == "=" && this.right.is_number(compressor); + }); + def(AST_Binary, function(compressor) { + if (binary[this.operator]) return true; + if (this.operator != "+") return false; + return (this.left.is_boolean(compressor) || this.left.is_number(compressor)) + && (this.right.is_boolean(compressor) || this.right.is_number(compressor)); + }); + var fn = makePredicate([ + "charCodeAt", + "getDate", + "getDay", + "getFullYear", + "getHours", + "getMilliseconds", + "getMinutes", + "getMonth", + "getSeconds", + "getTime", + "getTimezoneOffset", + "getUTCDate", + "getUTCDay", + "getUTCFullYear", + "getUTCHours", + "getUTCMilliseconds", + "getUTCMinutes", + "getUTCMonth", + "getUTCSeconds", + "getYear", + "indexOf", + "lastIndexOf", + "localeCompare", + "push", + "search", + "setDate", + "setFullYear", + "setHours", + "setMilliseconds", + "setMinutes", + "setMonth", + "setSeconds", + "setTime", + "setUTCDate", + "setUTCFullYear", + "setUTCHours", + "setUTCMilliseconds", + "setUTCMinutes", + "setUTCMonth", + "setUTCSeconds", + "setYear", + ]); + def(AST_Call, function(compressor) { + if (!compressor.option("unsafe")) return false; + var exp = this.expression; + return exp instanceof AST_Dot && (fn[exp.property] + || is_undeclared_ref(exp.expression) && exp.expression.name == "Math"); + }); + def(AST_Conditional, function(compressor) { + return this.consequent.is_number(compressor) && this.alternative.is_number(compressor); + }); + def(AST_New, return_false); + def(AST_Number, return_true); + def(AST_Sequence, function(compressor) { + return this.tail_node().is_number(compressor); + }); + def(AST_SymbolRef, function(compressor, keep_unary) { + var fixed = this.fixed_value(); + if (!fixed) return false; + if (keep_unary + && fixed instanceof AST_UnaryPrefix + && fixed.operator == "+" + && fixed.expression.equals(this)) { + return false; + } + this.is_number = return_false; + var result = fixed.is_number(compressor); + delete this.is_number; + return result; + }); + var unary = makePredicate("+ - ~ ++ --"); + def(AST_Unary, function() { + return unary[this.operator]; + }); + })(function(node, func) { + node.DEFMETHOD("is_number", func); + }); + + // methods to determine if an expression has a string result type + (function(def) { + def(AST_Node, return_false); + def(AST_Assign, function(compressor) { + switch (this.operator) { + case "+=": + if (this.left.is_string(compressor)) return true; + case "=": + return this.right.is_string(compressor); + } + }); + def(AST_Binary, function(compressor) { + return this.operator == "+" && + (this.left.is_string(compressor) || this.right.is_string(compressor)); + }); + var fn = makePredicate([ + "charAt", + "substr", + "substring", + "toExponential", + "toFixed", + "toLowerCase", + "toPrecision", + "toString", + "toUpperCase", + "trim", + ]); + def(AST_Call, function(compressor) { + if (!compressor.option("unsafe")) return false; + var exp = this.expression; + return exp instanceof AST_Dot && fn[exp.property]; + }); + def(AST_Conditional, function(compressor) { + return this.consequent.is_string(compressor) && this.alternative.is_string(compressor); + }); + def(AST_Sequence, function(compressor) { + return this.tail_node().is_string(compressor); + }); + def(AST_String, return_true); + def(AST_SymbolRef, function(compressor) { + var fixed = this.fixed_value(); + if (!fixed) return false; + this.is_string = return_false; + var result = fixed.is_string(compressor); + delete this.is_string; + return result; + }); + def(AST_Template, function(compressor) { + return !this.tag || is_raw_tag(compressor, this.tag); + }); + def(AST_UnaryPrefix, function() { + return this.operator == "typeof"; + }); + })(function(node, func) { + node.DEFMETHOD("is_string", func); + }); + + var lazy_op = makePredicate("&& || ??"); + + (function(def) { + function to_node(value, orig) { + if (value instanceof AST_Node) return value.clone(true); + if (Array.isArray(value)) return make_node(AST_Array, orig, { + elements: value.map(function(value) { + return to_node(value, orig); + }) + }); + if (value && typeof value == "object") { + var props = []; + for (var key in value) if (HOP(value, key)) { + props.push(make_node(AST_ObjectKeyVal, orig, { + key: key, + value: to_node(value[key], orig), + })); + } + return make_node(AST_Object, orig, { properties: props }); + } + return make_node_from_constant(value, orig); + } + + function warn(node) { + AST_Node.warn("global_defs {this} redefined [{start}]", node); + } + + AST_Toplevel.DEFMETHOD("resolve_defines", function(compressor) { + if (!compressor.option("global_defs")) return this; + this.figure_out_scope({ ie: compressor.option("ie") }); + return this.transform(new TreeTransformer(function(node) { + var def = node._find_defs(compressor, ""); + if (!def) return; + var level = 0, child = node, parent; + while (parent = this.parent(level++)) { + if (!(parent instanceof AST_PropAccess)) break; + if (parent.expression !== child) break; + child = parent; + } + if (is_lhs(child, parent)) { + warn(node); + return; + } + return def; + })); + }); + def(AST_Node, noop); + def(AST_Dot, function(compressor, suffix) { + return this.expression._find_defs(compressor, "." + this.property + suffix); + }); + def(AST_SymbolDeclaration, function(compressor) { + if (!this.definition().global) return; + if (HOP(compressor.option("global_defs"), this.name)) warn(this); + }); + def(AST_SymbolRef, function(compressor, suffix) { + if (!this.definition().global) return; + var defines = compressor.option("global_defs"); + var name = this.name + suffix; + if (HOP(defines, name)) return to_node(defines[name], this); + }); + })(function(node, func) { + node.DEFMETHOD("_find_defs", func); + }); + + function best_of_expression(ast1, ast2, threshold) { + var delta = ast2.print_to_string().length - ast1.print_to_string().length; + return delta < (threshold || 0) ? ast2 : ast1; + } + + function best_of_statement(ast1, ast2, threshold) { + return best_of_expression(make_node(AST_SimpleStatement, ast1, { + body: ast1, + }), make_node(AST_SimpleStatement, ast2, { + body: ast2, + }), threshold).body; + } + + function best_of(compressor, ast1, ast2, threshold) { + return (first_in_statement(compressor) ? best_of_statement : best_of_expression)(ast1, ast2, threshold); + } + + function convert_to_predicate(obj) { + var map = Object.create(null); + Object.keys(obj).forEach(function(key) { + map[key] = makePredicate(obj[key]); + }); + return map; + } + + function skip_directives(body) { + for (var i = 0; i < body.length; i++) { + var stat = body[i]; + if (!(stat instanceof AST_Directive)) return stat; + } + } + + function arrow_first_statement() { + if (this.value) return make_node(AST_Return, this.value, { value: this.value }); + return skip_directives(this.body); + } + AST_Arrow.DEFMETHOD("first_statement", arrow_first_statement); + AST_AsyncArrow.DEFMETHOD("first_statement", arrow_first_statement); + AST_Lambda.DEFMETHOD("first_statement", function() { + return skip_directives(this.body); + }); + + AST_Lambda.DEFMETHOD("length", function() { + var argnames = this.argnames; + for (var i = 0; i < argnames.length; i++) { + if (argnames[i] instanceof AST_DefaultValue) break; + } + return i; + }); + + function try_evaluate(compressor, node) { + var ev = node.evaluate(compressor); + if (ev === node) return node; + ev = make_node_from_constant(ev, node).optimize(compressor); + return best_of(compressor, node, ev, compressor.eval_threshold); + } + + var object_fns = [ + "constructor", + "toString", + "valueOf", + ]; + var native_fns = convert_to_predicate({ + Array: [ + "indexOf", + "join", + "lastIndexOf", + "slice", + ].concat(object_fns), + Boolean: object_fns, + Function: object_fns, + Number: [ + "toExponential", + "toFixed", + "toPrecision", + ].concat(object_fns), + Object: object_fns, + RegExp: [ + "exec", + "test", + ].concat(object_fns), + String: [ + "charAt", + "charCodeAt", + "concat", + "indexOf", + "italics", + "lastIndexOf", + "match", + "replace", + "search", + "slice", + "split", + "substr", + "substring", + "toLowerCase", + "toUpperCase", + "trim", + ].concat(object_fns), + }); + var static_fns = convert_to_predicate({ + Array: [ + "isArray", + ], + Math: [ + "abs", + "acos", + "asin", + "atan", + "ceil", + "cos", + "exp", + "floor", + "log", + "round", + "sin", + "sqrt", + "tan", + "atan2", + "pow", + "max", + "min", + ], + Number: [ + "isFinite", + "isNaN", + ], + Object: [ + "create", + "getOwnPropertyDescriptor", + "getOwnPropertyNames", + "getPrototypeOf", + "isExtensible", + "isFrozen", + "isSealed", + "keys", + ], + String: [ + "fromCharCode", + "raw", + ], + }); + + function is_static_fn(node) { + if (!(node instanceof AST_Dot)) return false; + var expr = node.expression; + if (!is_undeclared_ref(expr)) return false; + var static_fn = static_fns[expr.name]; + return static_fn && (static_fn[node.property] || expr.name == "Math" && node.property == "random"); + } + + // Accommodate when compress option evaluate=false + // as well as the common constant expressions !0 and -1 + (function(def) { + def(AST_Node, return_false); + def(AST_Constant, return_true); + def(AST_RegExp, return_false); + var unaryPrefix = makePredicate("! ~ - + void"); + def(AST_UnaryPrefix, function() { + return unaryPrefix[this.operator] && this.expression instanceof AST_Constant; + }); + })(function(node, func) { + node.DEFMETHOD("is_constant", func); + }); + + // methods to evaluate a constant expression + (function(def) { + // If the node has been successfully reduced to a constant, + // then its value is returned; otherwise the element itself + // is returned. + // + // They can be distinguished as constant value is never a + // descendant of AST_Node. + // + // When `ignore_side_effects` is `true`, inspect the constant value + // produced without worrying about any side effects caused by said + // expression. + AST_Node.DEFMETHOD("evaluate", function(compressor, ignore_side_effects) { + if (!compressor.option("evaluate")) return this; + var cached = []; + var val = this._eval(compressor, ignore_side_effects, cached, 1); + cached.forEach(function(node) { + delete node._eval; + }); + if (ignore_side_effects) return val; + if (!val || val instanceof RegExp) return val; + if (typeof val == "function" || typeof val == "object") return this; + return val; + }); + var scan_modified = new TreeWalker(function(node) { + if (node instanceof AST_Assign) modified(node.left); + if (node instanceof AST_ForEnumeration) modified(node.init); + if (node instanceof AST_Unary && UNARY_POSTFIX[node.operator]) modified(node.expression); + }); + function modified(node) { + if (node instanceof AST_DestructuredArray) { + node.elements.forEach(modified); + } else if (node instanceof AST_DestructuredObject) { + node.properties.forEach(function(prop) { + modified(prop.value); + }); + } else if (node instanceof AST_PropAccess) { + modified(node.expression); + } else if (node instanceof AST_SymbolRef) { + node.definition().references.forEach(function(ref) { + delete ref._eval; + }); + } + } + def(AST_Statement, function() { + throw new Error(string_template("Cannot evaluate a statement [{start}]", this)); + }); + def(AST_Accessor, return_this); + def(AST_BigInt, return_this); + def(AST_Class, return_this); + def(AST_Node, return_this); + def(AST_Constant, function() { + return this.value; + }); + def(AST_Assign, function(compressor, ignore_side_effects, cached, depth) { + var lhs = this.left; + if (!ignore_side_effects) { + if (!(lhs instanceof AST_SymbolRef)) return this; + if (!HOP(lhs, "_eval")) { + if (!lhs.fixed) return this; + var def = lhs.definition(); + if (!def.fixed) return this; + if (def.undeclared) return this; + if (def.last_ref !== lhs) return this; + if (def.single_use == "m") return this; + if (this.right.has_side_effects(compressor)) return this; + } + } + var op = this.operator; + var node; + if (!HOP(lhs, "_eval") && lhs instanceof AST_SymbolRef && lhs.fixed && lhs.definition().fixed) { + node = lhs; + } else if (op == "=") { + node = this.right; + } else { + node = make_node(AST_Binary, this, { + operator: op.slice(0, -1), + left: lhs, + right: this.right, + }); + } + lhs.walk(scan_modified); + var value = node._eval(compressor, ignore_side_effects, cached, depth); + if (typeof value == "object") return this; + modified(lhs); + return value; + }); + def(AST_Sequence, function(compressor, ignore_side_effects, cached, depth) { + if (!ignore_side_effects) return this; + var exprs = this.expressions; + for (var i = 0, last = exprs.length - 1; i < last; i++) { + exprs[i].walk(scan_modified); + } + var tail = exprs[last]; + var value = tail._eval(compressor, ignore_side_effects, cached, depth); + return value === tail ? this : value; + }); + def(AST_Lambda, function(compressor) { + if (compressor.option("unsafe")) { + var fn = function() {}; + fn.node = this; + fn.toString = function() { + return "function(){}"; + }; + return fn; + } + return this; + }); + def(AST_Array, function(compressor, ignore_side_effects, cached, depth) { + if (compressor.option("unsafe")) { + var elements = []; + for (var i = 0; i < this.elements.length; i++) { + var element = this.elements[i]; + if (element instanceof AST_Hole) return this; + var value = element._eval(compressor, ignore_side_effects, cached, depth); + if (element === value) return this; + elements.push(value); + } + return elements; + } + return this; + }); + def(AST_Object, function(compressor, ignore_side_effects, cached, depth) { + if (compressor.option("unsafe")) { + var val = {}; + for (var i = 0; i < this.properties.length; i++) { + var prop = this.properties[i]; + if (!(prop instanceof AST_ObjectKeyVal)) return this; + var key = prop.key; + if (key instanceof AST_Node) { + key = key._eval(compressor, ignore_side_effects, cached, depth); + if (key === prop.key) return this; + } + switch (key) { + case "__proto__": + case "toString": + case "valueOf": + return this; + } + val[key] = prop.value._eval(compressor, ignore_side_effects, cached, depth); + if (val[key] === prop.value) return this; + } + return val; + } + return this; + }); + var non_converting_unary = makePredicate("! typeof void"); + def(AST_UnaryPrefix, function(compressor, ignore_side_effects, cached, depth) { + var e = this.expression; + var op = this.operator; + // Function would be evaluated to an array and so typeof would + // incorrectly return "object". Hence making is a special case. + if (compressor.option("typeofs") + && op == "typeof" + && (e instanceof AST_Lambda + || e instanceof AST_SymbolRef + && e.fixed_value() instanceof AST_Lambda)) { + return typeof function(){}; + } + var def = e instanceof AST_SymbolRef && e.definition(); + if (!non_converting_unary[op] && !(def && def.fixed)) depth++; + e.walk(scan_modified); + var v = e._eval(compressor, ignore_side_effects, cached, depth); + if (v === e) { + if (ignore_side_effects && op == "void") return; + return this; + } + switch (op) { + case "!": return !v; + case "typeof": + // typeof returns "object" or "function" on different platforms + // so cannot evaluate reliably + if (v instanceof RegExp) return this; + return typeof v; + case "void": return; + case "~": return ~v; + case "-": return -v; + case "+": return +v; + case "++": + case "--": + if (!def) return this; + if (!ignore_side_effects) { + if (def.undeclared) return this; + if (def.last_ref !== e) return this; + } + if (HOP(e, "_eval")) v = +(op[0] + 1) + +v; + modified(e); + return v; + } + return this; + }); + def(AST_UnaryPostfix, function(compressor, ignore_side_effects, cached, depth) { + var e = this.expression; + if (!(e instanceof AST_SymbolRef)) { + if (!ignore_side_effects) return this; + } else if (!HOP(e, "_eval")) { + if (!e.fixed) return this; + if (!ignore_side_effects) { + var def = e.definition(); + if (!def.fixed) return this; + if (def.undeclared) return this; + if (def.last_ref !== e) return this; + } + } + if (!(e instanceof AST_SymbolRef && e.definition().fixed)) depth++; + e.walk(scan_modified); + var v = e._eval(compressor, ignore_side_effects, cached, depth); + if (v === e) return this; + modified(e); + return +v; + }); + var non_converting_binary = makePredicate("&& || === !=="); + def(AST_Binary, function(compressor, ignore_side_effects, cached, depth) { + if (!non_converting_binary[this.operator]) depth++; + var left = this.left._eval(compressor, ignore_side_effects, cached, depth); + if (left === this.left) return this; + if (this.operator == (left ? "||" : "&&")) return left; + var rhs_ignore_side_effects = ignore_side_effects && !(left && typeof left == "object"); + var right = this.right._eval(compressor, rhs_ignore_side_effects, cached, depth); + if (right === this.right) return this; + var result; + switch (this.operator) { + case "&&" : result = left && right; break; + case "||" : result = left || right; break; + case "??" : + result = left == null ? right : left; + 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 = 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 = 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 = left > right; break; + case ">=" : result = left >= right; break; + case "**": + result = Math.pow(left, right); + break; + case "in": + if (right && typeof right == "object" && HOP(right, left)) { + result = true; + break; + } + default: + return this; + } + if (isNaN(result)) return compressor.find_parent(AST_With) ? this : result; + if (compressor.option("unsafe_math") + && !ignore_side_effects + && result + && typeof result == "number" + && (this.operator == "+" || this.operator == "-")) { + var digits = Math.max(0, decimals(left), decimals(right)); + // 53-bit significand ---> 15.95 decimal places + if (digits < 16) return +result.toFixed(digits); + } + return result; + + function decimals(operand) { + var match = /(\.[0-9]*)?(e[^e]+)?$/.exec(+operand); + return (match[1] || ".").length - 1 - (match[2] || "").slice(1); + } + }); + def(AST_Conditional, function(compressor, ignore_side_effects, cached, depth) { + var condition = this.condition._eval(compressor, ignore_side_effects, cached, depth); + if (condition === this.condition) return this; + var node = condition ? this.consequent : this.alternative; + var value = node._eval(compressor, ignore_side_effects, cached, depth); + return value === node ? this : value; + }); + function verify_escaped(ref, depth) { + var escaped = ref.definition().escaped; + switch (escaped.length) { + case 0: + return true; + case 1: + var found = false; + escaped[0].walk(new TreeWalker(function(node) { + if (found) return true; + if (node === ref) return found = true; + if (node instanceof AST_Scope) return true; + })); + return found; + default: + return depth <= escaped.depth; + } + } + def(AST_SymbolRef, function(compressor, ignore_side_effects, cached, depth) { + this._eval = return_this; + try { + var fixed = this.fixed_value(); + if (!fixed) return this; + var value; + if (HOP(fixed, "_eval")) { + value = fixed._eval(); + } else { + value = fixed._eval(compressor, ignore_side_effects, cached, depth); + if (value === fixed) return this; + fixed._eval = function() { + return value; + }; + cached.push(fixed); + } + return value && typeof value == "object" && !verify_escaped(this, depth) ? this : value; + } finally { + delete this._eval; + } + }); + var global_objs = { + Array: Array, + Math: Math, + Number: Number, + Object: Object, + String: String, + }; + var static_values = convert_to_predicate({ + Math: [ + "E", + "LN10", + "LN2", + "LOG2E", + "LOG10E", + "PI", + "SQRT1_2", + "SQRT2", + ], + Number: [ + "MAX_VALUE", + "MIN_VALUE", + "NaN", + "NEGATIVE_INFINITY", + "POSITIVE_INFINITY", + ], + }); + var regexp_props = makePredicate("global ignoreCase multiline source"); + def(AST_PropAccess, function(compressor, ignore_side_effects, cached, depth) { + if (compressor.option("unsafe")) { + var val; + var exp = this.expression; + if (!is_undeclared_ref(exp)) { + val = exp._eval(compressor, ignore_side_effects, cached, depth + 1); + if (val == null || val === exp) return this; + } + var key = this.property; + if (key instanceof AST_Node) { + key = key._eval(compressor, ignore_side_effects, cached, depth); + if (key === this.property) return this; + } + if (val === undefined) { + var static_value = static_values[exp.name]; + if (!static_value || !static_value[key]) return this; + val = global_objs[exp.name]; + } else if (val instanceof RegExp) { + if (!regexp_props[key]) return this; + } else if (typeof val == "object") { + if (!HOP(val, key)) return this; + } else if (typeof val == "function") switch (key) { + case "name": + return val.node.name ? val.node.name.name : ""; + case "length": + return val.node.length(); + default: + return this; + } + return val[key]; + } + return this; + }); + function eval_all(nodes, compressor, ignore_side_effects, cached, depth) { + var values = []; + for (var i = 0; i < nodes.length; i++) { + var node = nodes[i]; + var value = node._eval(compressor, ignore_side_effects, cached, depth); + if (node === value) return; + values.push(value); + } + return values; + } + def(AST_Call, function(compressor, ignore_side_effects, cached, depth) { + var exp = this.expression; + var fn = exp instanceof AST_SymbolRef ? exp.fixed_value() : exp; + if (fn instanceof AST_Arrow || fn instanceof AST_Defun || fn instanceof AST_Function) { + if (fn.evaluating) return this; + if (fn.name && fn.name.definition().recursive_refs > 0) return this; + if (this.is_expr_pure(compressor)) return this; + var args = eval_all(this.args, compressor, ignore_side_effects, cached, depth); + if (!all(fn.argnames, function(sym, index) { + if (sym instanceof AST_DefaultValue) { + if (!args) return false; + if (args[index] === undefined) { + var value = sym.value._eval(compressor, ignore_side_effects, cached, depth); + if (value === sym.value) return false; + args[index] = value; + } + sym = sym.name; + } + return !(sym instanceof AST_Destructured); + })) return this; + if (fn.rest instanceof AST_Destructured) return this; + if (!args && !ignore_side_effects) return this; + var stat = fn.first_statement(); + if (!(stat instanceof AST_Return)) { + if (ignore_side_effects) { + fn.walk(scan_modified); + var found = false; + fn.evaluating = true; + walk_body(fn, new TreeWalker(function(node) { + if (found) return true; + if (node instanceof AST_Return) { + if (node.value && node.value._eval(compressor, true, cached, depth) !== undefined) { + found = true; + } + return true; + } + if (node instanceof AST_Scope && node !== fn) return true; + })); + fn.evaluating = false; + if (!found) return; + } + return this; + } + var val = stat.value; + if (!val) return; + var cached_args = []; + if (!args || all(fn.argnames, function(sym, i) { + return assign(sym, args[i]); + }) && !(fn.rest && !assign(fn.rest, args.slice(fn.argnames.length))) || ignore_side_effects) { + if (ignore_side_effects) fn.argnames.forEach(function(sym) { + if (sym instanceof AST_DefaultValue) sym.value.walk(scan_modified); + }); + fn.evaluating = true; + val = val._eval(compressor, ignore_side_effects, cached, depth); + fn.evaluating = false; + } + cached_args.forEach(function(node) { + delete node._eval; + }); + return val === stat.value ? this : val; + } else if (compressor.option("unsafe") && exp instanceof AST_PropAccess) { + var key = exp.property; + if (key instanceof AST_Node) { + key = key._eval(compressor, ignore_side_effects, cached, depth); + if (key === exp.property) return this; + } + var val; + var e = exp.expression; + if (is_undeclared_ref(e)) { + var static_fn = static_fns[e.name]; + if (!static_fn || !static_fn[key]) return this; + val = global_objs[e.name]; + } else { + val = e._eval(compressor, ignore_side_effects, cached, depth + 1); + if (val == null || val === e) return this; + var native_fn = native_fns[val.constructor.name]; + if (!native_fn || !native_fn[key]) return this; + if (val instanceof RegExp && val.global && !(e instanceof AST_RegExp)) return this; + } + var args = eval_all(this.args, compressor, ignore_side_effects, cached, depth); + if (!args) return this; + if (key == "replace" && typeof args[1] == "function") return this; + try { + return val[key].apply(val, args); + } catch (ex) { + AST_Node.warn("Error evaluating {this} [{start}]", this); + } finally { + if (val instanceof RegExp) val.lastIndex = 0; + } + } + return this; + + function assign(sym, arg) { + if (sym instanceof AST_DefaultValue) sym = sym.name; + var def = sym.definition(); + if (def.orig[def.orig.length - 1] !== sym) return false; + var value = arg; + def.references.forEach(function(node) { + node._eval = function() { + return value; + }; + cached_args.push(node); + }); + return true; + } + }); + def(AST_New, return_this); + def(AST_Template, function(compressor, ignore_side_effects, cached, depth) { + if (!compressor.option("templates")) return this; + if (this.tag) { + if (!is_raw_tag(compressor, this.tag)) return this; + decode = function(str) { + return str; + }; + } + var exprs = eval_all(this.expressions, compressor, ignore_side_effects, cached, depth); + if (!exprs) return this; + var malformed = false; + var ret = decode(this.strings[0]); + for (var i = 0; i < exprs.length; i++) { + ret += exprs[i] + decode(this.strings[i + 1]); + } + if (!malformed) return ret; + this._eval = return_this; + return this; + + function decode(str) { + str = decode_template(str); + if (typeof str != "string") malformed = true; + return str; + } + }); + })(function(node, func) { + node.DEFMETHOD("_eval", func); + }); + + // method to negate an expression + (function(def) { + function basic_negation(exp) { + return make_node(AST_UnaryPrefix, exp, { + operator: "!", + expression: exp, + }); + } + function best(orig, alt, first_in_statement) { + var negated = basic_negation(orig); + if (first_in_statement) return best_of_expression(negated, make_node(AST_SimpleStatement, alt, { + body: alt, + })) === negated ? negated : alt; + return best_of_expression(negated, alt); + } + def(AST_Node, function() { + return basic_negation(this); + }); + def(AST_Statement, function() { + throw new Error("Cannot negate a statement"); + }); + def(AST_Binary, function(compressor, first_in_statement) { + var self = this.clone(), op = this.operator; + if (compressor.option("unsafe_comps")) { + switch (op) { + case "<=" : self.operator = ">" ; return self; + case "<" : self.operator = ">=" ; return self; + case ">=" : self.operator = "<" ; return self; + case ">" : self.operator = "<=" ; return self; + } + } + switch (op) { + case "==" : self.operator = "!="; return self; + case "!=" : self.operator = "=="; return self; + case "===": self.operator = "!=="; return self; + case "!==": self.operator = "==="; return self; + case "&&": + self.operator = "||"; + self.left = self.left.negate(compressor, first_in_statement); + self.right = self.right.negate(compressor); + return best(this, self, first_in_statement); + case "||": + self.operator = "&&"; + self.left = self.left.negate(compressor, first_in_statement); + self.right = self.right.negate(compressor); + return best(this, self, first_in_statement); + } + return basic_negation(this); + }); + def(AST_ClassExpression, function() { + return basic_negation(this); + }); + def(AST_Conditional, function(compressor, first_in_statement) { + var self = this.clone(); + self.consequent = self.consequent.negate(compressor); + self.alternative = self.alternative.negate(compressor); + return best(this, self, first_in_statement); + }); + def(AST_LambdaExpression, function() { + return basic_negation(this); + }); + def(AST_Sequence, function(compressor) { + var expressions = this.expressions.slice(); + expressions.push(expressions.pop().negate(compressor)); + return make_sequence(this, expressions); + }); + def(AST_UnaryPrefix, function() { + if (this.operator == "!") + return this.expression; + return basic_negation(this); + }); + })(function(node, func) { + node.DEFMETHOD("negate", function(compressor, first_in_statement) { + return func.call(this, compressor, first_in_statement); + }); + }); + + var global_pure_fns = makePredicate("Boolean decodeURI decodeURIComponent Date encodeURI encodeURIComponent Error escape EvalError isFinite isNaN Number Object parseFloat parseInt RangeError ReferenceError String SyntaxError TypeError unescape URIError"); + var global_pure_constructors = makePredicate("Map Set WeakMap WeakSet"); + AST_Call.DEFMETHOD("is_expr_pure", function(compressor) { + if (compressor.option("unsafe")) { + var expr = this.expression; + if (is_undeclared_ref(expr)) { + if (global_pure_fns[expr.name]) return true; + if (this instanceof AST_New && global_pure_constructors[expr.name]) return true; + } + if (is_static_fn(expr)) return true; + } + return compressor.option("annotations") && this.pure || !compressor.pure_funcs(this); + }); + AST_Template.DEFMETHOD("is_expr_pure", function(compressor) { + var tag = this.tag; + if (!tag) return true; + if (compressor.option("unsafe")) { + if (is_undeclared_ref(tag) && global_pure_fns[tag.name]) return true; + if (tag instanceof AST_Dot && is_undeclared_ref(tag.expression)) { + var static_fn = static_fns[tag.expression.name]; + return static_fn && (static_fn[tag.property] + || tag.expression.name == "Math" && tag.property == "random"); + } + } + return !compressor.pure_funcs(this); + }); + AST_Node.DEFMETHOD("is_call_pure", return_false); + AST_Call.DEFMETHOD("is_call_pure", function(compressor) { + if (!compressor.option("unsafe")) return false; + var dot = this.expression; + if (!(dot instanceof AST_Dot)) return false; + var exp = dot.expression; + var map; + var prop = dot.property; + if (exp instanceof AST_Array) { + map = native_fns.Array; + } else if (exp.is_boolean(compressor)) { + map = native_fns.Boolean; + } else if (exp.is_number(compressor)) { + map = native_fns.Number; + } else if (exp instanceof AST_RegExp) { + map = native_fns.RegExp; + } else if (exp.is_string(compressor)) { + map = native_fns.String; + if (prop == "replace") { + var arg = this.args[1]; + if (arg && !arg.is_string(compressor)) return false; + } + } else if (!dot.may_throw_on_access(compressor)) { + map = native_fns.Object; + } + return map && map[prop]; + }); + + // determine if object spread syntax may cause runtime exception + (function(def) { + def(AST_Node, return_false); + def(AST_Array, return_true); + def(AST_Assign, function() { + switch (this.operator) { + case "=": + return this.right.safe_to_spread(); + case "&&=": + case "||=": + case "??=": + return this.left.safe_to_spread() && this.right.safe_to_spread(); + } + return true; + }); + def(AST_Binary, function() { + return !lazy_op[this.operator] || this.left.safe_to_spread() && this.right.safe_to_spread(); + }); + def(AST_Constant, return_true); + def(AST_Lambda, return_true); + def(AST_Object, function() { + return all(this.properties, function(prop) { + return !(prop instanceof AST_ObjectGetter || prop instanceof AST_Spread); + }); + }); + def(AST_Sequence, function() { + return this.tail_node().safe_to_spread(); + }); + def(AST_SymbolRef, function() { + var fixed = this.fixed_value(); + return fixed && fixed.safe_to_spread(); + }); + def(AST_Unary, return_true); + })(function(node, func) { + node.DEFMETHOD("safe_to_spread", func); + }); + + // determine if expression has side effects + (function(def) { + function any(list, compressor, spread) { + return !all(list, spread ? function(node) { + return node instanceof AST_Spread ? !spread(node, compressor) : !node.has_side_effects(compressor); + } : function(node) { + return !node.has_side_effects(compressor); + }); + } + function array_spread(node, compressor) { + var exp = node.expression; + return !exp.is_string(compressor) || exp.has_side_effects(compressor); + } + def(AST_Node, return_true); + def(AST_Array, function(compressor) { + return any(this.elements, compressor, array_spread); + }); + def(AST_Assign, function(compressor) { + var lhs = this.left; + if (!(lhs instanceof AST_PropAccess)) return true; + var node = lhs.expression; + return !(node instanceof AST_ObjectIdentity) + || !node.scope.resolve().new + || lhs instanceof AST_Sub && lhs.property.has_side_effects(compressor) + || this.right.has_side_effects(compressor); + }); + def(AST_Binary, function(compressor) { + return this.left.has_side_effects(compressor) + || this.right.has_side_effects(compressor) + || !can_drop_op(this, compressor); + }); + def(AST_Block, function(compressor) { + return any(this.body, compressor); + }); + def(AST_Call, function(compressor) { + if (!this.is_expr_pure(compressor) + && (!this.is_call_pure(compressor) || this.expression.has_side_effects(compressor))) { + return true; + } + return any(this.args, compressor, array_spread); + }); + def(AST_Case, function(compressor) { + return this.expression.has_side_effects(compressor) + || any(this.body, compressor); + }); + def(AST_Class, function(compressor) { + var base = this.extends; + if (base) { + if (base instanceof AST_SymbolRef) base = base.fixed_value(); + if (!safe_for_extends(base)) return true; + } + return any(this.properties, compressor); + }); + def(AST_ClassProperty, function(compressor) { + return this.key instanceof AST_Node && this.key.has_side_effects(compressor) + || this.static && this.value && this.value.has_side_effects(compressor); + }); + def(AST_Conditional, function(compressor) { + return this.condition.has_side_effects(compressor) + || this.consequent.has_side_effects(compressor) + || this.alternative.has_side_effects(compressor); + }); + def(AST_Constant, return_false); + def(AST_Definitions, function(compressor) { + return any(this.definitions, compressor); + }); + def(AST_DestructuredArray, function(compressor) { + return any(this.elements, compressor); + }); + def(AST_DestructuredKeyVal, function(compressor) { + return this.key instanceof AST_Node && this.key.has_side_effects(compressor) + || this.value.has_side_effects(compressor); + }); + def(AST_DestructuredObject, function(compressor) { + return any(this.properties, compressor); + }); + def(AST_Dot, function(compressor) { + return this.expression.may_throw_on_access(compressor) + || this.expression.has_side_effects(compressor); + }); + def(AST_EmptyStatement, return_false); + def(AST_If, function(compressor) { + return this.condition.has_side_effects(compressor) + || this.body && this.body.has_side_effects(compressor) + || this.alternative && this.alternative.has_side_effects(compressor); + }); + def(AST_LabeledStatement, function(compressor) { + return this.body.has_side_effects(compressor); + }); + def(AST_Lambda, return_false); + def(AST_Object, function(compressor) { + return any(this.properties, compressor, function(node, compressor) { + var exp = node.expression; + return !exp.safe_to_spread() || exp.has_side_effects(compressor); + }); + }); + def(AST_ObjectIdentity, return_false); + def(AST_ObjectProperty, function(compressor) { + return this.key instanceof AST_Node && this.key.has_side_effects(compressor) + || this.value.has_side_effects(compressor); + }); + def(AST_Sequence, function(compressor) { + return any(this.expressions, compressor); + }); + def(AST_SimpleStatement, function(compressor) { + return this.body.has_side_effects(compressor); + }); + def(AST_Sub, function(compressor) { + return this.expression.may_throw_on_access(compressor) + || this.expression.has_side_effects(compressor) + || this.property.has_side_effects(compressor); + }); + def(AST_Switch, function(compressor) { + return this.expression.has_side_effects(compressor) + || any(this.body, compressor); + }); + def(AST_SymbolDeclaration, return_false); + def(AST_SymbolRef, function(compressor) { + return !this.is_declared(compressor) || !can_drop_symbol(this, compressor); + }); + def(AST_Template, function(compressor) { + return !this.is_expr_pure(compressor) || any(this.expressions, compressor); + }); + def(AST_Try, function(compressor) { + return any(this.body, compressor) + || this.bcatch && this.bcatch.has_side_effects(compressor) + || this.bfinally && this.bfinally.has_side_effects(compressor); + }); + def(AST_Unary, function(compressor) { + return unary_side_effects[this.operator] + || this.expression.has_side_effects(compressor); + }); + def(AST_VarDef, function() { + return this.value; + }); + })(function(node, func) { + node.DEFMETHOD("has_side_effects", func); + }); + + // determine if expression may throw + (function(def) { + def(AST_Node, return_true); + + def(AST_Constant, return_false); + def(AST_EmptyStatement, return_false); + def(AST_Lambda, return_false); + def(AST_ObjectIdentity, return_false); + def(AST_SymbolDeclaration, return_false); + + function any(list, compressor) { + for (var i = list.length; --i >= 0;) + if (list[i].may_throw(compressor)) + return true; + return false; + } + + function call_may_throw(exp, compressor) { + if (exp.may_throw(compressor)) return true; + if (exp instanceof AST_SymbolRef) exp = exp.fixed_value(); + if (!(exp instanceof AST_Lambda)) return true; + if (any(exp.argnames, compressor)) return true; + if (any(exp.body, compressor)) return true; + return is_arrow(exp) && exp.value && exp.value.may_throw(compressor); + } + + def(AST_Array, function(compressor) { + return any(this.elements, compressor); + }); + def(AST_Assign, function(compressor) { + if (this.right.may_throw(compressor)) return true; + if (!compressor.has_directive("use strict") + && this.operator == "=" + && this.left instanceof AST_SymbolRef) { + return false; + } + return this.left.may_throw(compressor); + }); + def(AST_Await, function(compressor) { + return this.expression.may_throw(compressor); + }); + def(AST_Binary, function(compressor) { + return this.left.may_throw(compressor) + || this.right.may_throw(compressor) + || !can_drop_op(this, compressor); + }); + def(AST_Block, function(compressor) { + return any(this.body, compressor); + }); + def(AST_Call, function(compressor) { + if (any(this.args, compressor)) return true; + if (this.is_expr_pure(compressor)) return false; + this.may_throw = return_true; + var ret = call_may_throw(this.expression, compressor); + delete this.may_throw; + return ret; + }); + def(AST_Case, function(compressor) { + return this.expression.may_throw(compressor) + || any(this.body, compressor); + }); + def(AST_Conditional, function(compressor) { + return this.condition.may_throw(compressor) + || this.consequent.may_throw(compressor) + || this.alternative.may_throw(compressor); + }); + def(AST_DefaultValue, function(compressor) { + return this.name.may_throw(compressor) + || this.value && this.value.may_throw(compressor); + }); + def(AST_Definitions, function(compressor) { + return any(this.definitions, compressor); + }); + def(AST_Dot, function(compressor) { + return !this.optional && this.expression.may_throw_on_access(compressor) + || this.expression.may_throw(compressor); + }); + def(AST_ForEnumeration, function(compressor) { + if (this.init.may_throw(compressor)) return true; + var obj = this.object; + if (obj.may_throw(compressor)) return true; + obj = obj.tail_node(); + if (!(obj instanceof AST_Array || obj.is_string(compressor))) return true; + return this.body.may_throw(compressor); + }); + def(AST_If, function(compressor) { + return this.condition.may_throw(compressor) + || this.body && this.body.may_throw(compressor) + || this.alternative && this.alternative.may_throw(compressor); + }); + def(AST_LabeledStatement, function(compressor) { + return this.body.may_throw(compressor); + }); + def(AST_Object, function(compressor) { + return any(this.properties, compressor); + }); + def(AST_ObjectProperty, function(compressor) { + return this.value.may_throw(compressor) + || this.key instanceof AST_Node && this.key.may_throw(compressor); + }); + def(AST_Return, function(compressor) { + return this.value && this.value.may_throw(compressor); + }); + def(AST_Sequence, function(compressor) { + return any(this.expressions, compressor); + }); + def(AST_SimpleStatement, function(compressor) { + return this.body.may_throw(compressor); + }); + def(AST_Sub, function(compressor) { + return !this.optional && this.expression.may_throw_on_access(compressor) + || this.expression.may_throw(compressor) + || this.property.may_throw(compressor); + }); + def(AST_Switch, function(compressor) { + return this.expression.may_throw(compressor) + || any(this.body, compressor); + }); + def(AST_SymbolRef, function(compressor) { + return !this.is_declared(compressor) || !can_drop_symbol(this, compressor); + }); + def(AST_Template, function(compressor) { + if (any(this.expressions, compressor)) return true; + if (this.is_expr_pure(compressor)) return false; + if (!this.tag) return false; + this.may_throw = return_true; + var ret = call_may_throw(this.tag, compressor); + delete this.may_throw; + return ret; + }); + def(AST_Try, function(compressor) { + return (this.bcatch ? this.bcatch.may_throw(compressor) : any(this.body, compressor)) + || this.bfinally && this.bfinally.may_throw(compressor); + }); + def(AST_Unary, function(compressor) { + return this.expression.may_throw(compressor) + && !(this.operator == "typeof" && this.expression instanceof AST_SymbolRef); + }); + def(AST_VarDef, function(compressor) { + return this.name.may_throw(compressor) + || this.value && this.value.may_throw(compressor); + }); + })(function(node, func) { + node.DEFMETHOD("may_throw", func); + }); + + // determine if expression is constant + (function(def) { + function all_constant(list, scope) { + for (var i = list.length; --i >= 0;) + if (!list[i].is_constant_expression(scope)) + return false; + return true; + } + def(AST_Node, return_false); + def(AST_Array, function(scope) { + return all_constant(this.elements, scope); + }); + def(AST_Binary, function(scope) { + return this.left.is_constant_expression(scope) + && this.right.is_constant_expression(scope) + && can_drop_op(this); + }); + def(AST_Class, function(scope) { + var base = this.extends; + if (base && !safe_for_extends(base)) return false; + return all_constant(this.properties, scope); + }); + def(AST_ClassProperty, function(scope) { + return typeof this.key == "string" && (!this.value || this.value.is_constant_expression(scope)); + }); + def(AST_Constant, return_true); + def(AST_Lambda, function(scope) { + var self = this; + var result = true; + var scopes = []; + self.walk(new TreeWalker(function(node, descend) { + if (!result) return true; + if (node instanceof AST_BlockScope) { + if (node === self) return; + scopes.push(node); + descend(); + scopes.pop(); + return true; + } + if (node instanceof AST_SymbolRef) { + if (self.inlined || node.redef || node.in_arg) { + result = false; + return true; + } + if (self.variables.has(node.name)) return true; + var def = node.definition(); + if (member(def.scope, scopes)) return true; + if (scope && !def.redefined()) { + var scope_def = scope.find_variable(node.name); + if (scope_def ? scope_def === def : def.undeclared) { + result = "f"; + return true; + } + } + result = false; + return true; + } + if (node instanceof AST_ObjectIdentity) { + if (is_arrow(self) && all(scopes, function(s) { + return !(s instanceof AST_Scope) || is_arrow(s); + })) result = false; + return true; + } + })); + return result; + }); + def(AST_Object, function(scope) { + return all_constant(this.properties, scope); + }); + def(AST_ObjectProperty, function(scope) { + return typeof this.key == "string" && this.value.is_constant_expression(scope); + }); + def(AST_Unary, function(scope) { + return this.expression.is_constant_expression(scope); + }); + })(function(node, func) { + node.DEFMETHOD("is_constant_expression", func); + }); + + // tell me if a statement aborts + function aborts(thing) { + return thing && thing.aborts(); + } + (function(def) { + def(AST_Statement, return_null); + def(AST_Jump, return_this); + function block_aborts() { + var n = this.body.length; + return n > 0 && aborts(this.body[n - 1]); + } + def(AST_BlockStatement, block_aborts); + def(AST_SwitchBranch, block_aborts); + def(AST_If, function() { + return this.alternative && aborts(this.body) && aborts(this.alternative) && this; + }); + })(function(node, func) { + node.DEFMETHOD("aborts", func); + }); + + /* -----[ optimizers ]----- */ + + var directives = makePredicate(["use asm", "use strict"]); + OPT(AST_Directive, function(self, compressor) { + if (compressor.option("directives") + && (!directives[self.value] || compressor.has_directive(self.value) !== self)) { + return make_node(AST_EmptyStatement, self); + } + return self; + }); + + OPT(AST_Debugger, function(self, compressor) { + if (compressor.option("drop_debugger")) + return make_node(AST_EmptyStatement, self); + return self; + }); + + OPT(AST_LabeledStatement, function(self, compressor) { + if (self.body instanceof AST_If || self.body instanceof AST_Break) { + var body = tighten_body([ self.body ], compressor); + switch (body.length) { + case 0: + self.body = make_node(AST_EmptyStatement, self); + break; + case 1: + self.body = body[0]; + break; + default: + self.body = make_node(AST_BlockStatement, self, { body: body }); + break; + } + } + return compressor.option("unused") && self.label.references.length == 0 ? self.body : self; + }); + + OPT(AST_LoopControl, function(self, compressor) { + if (!compressor.option("dead_code")) return self; + var label = self.label; + if (label) { + var lct = compressor.loopcontrol_target(self); + self.label = null; + if (compressor.loopcontrol_target(self) === lct) { + remove(label.thedef.references, self); + } else { + self.label = label; + } + } + return self; + }); + + OPT(AST_Block, function(self, compressor) { + self.body = tighten_body(self.body, compressor); + return self; + }); + + function trim_block(node, parent, in_list) { + switch (node.body.length) { + case 0: + return in_list ? List.skip : make_node(AST_EmptyStatement, node); + case 1: + var stat = node.body[0]; + if (!safe_to_trim(stat)) return node; + if (parent instanceof AST_IterationStatement && stat instanceof AST_LambdaDefinition) return node; + return stat; + } + return node; + } + + OPT(AST_BlockStatement, function(self, compressor) { + self.body = tighten_body(self.body, compressor); + return trim_block(self, compressor.parent()); + }); + + function drop_rest_farg(fn, compressor) { + if (!compressor.option("rests")) return; + if (fn.uses_arguments) return; + if (!(fn.rest instanceof AST_DestructuredArray)) return; + if (!compressor.drop_fargs(fn, compressor.parent())) return; + fn.argnames = fn.argnames.concat(fn.rest.elements); + fn.rest = fn.rest.rest; + } + + OPT(AST_Lambda, function(self, compressor) { + drop_rest_farg(self, compressor); + self.body = tighten_body(self.body, compressor); + return self; + }); + + function opt_arrow(self, compressor) { + if (!compressor.option("arrows")) return self; + drop_rest_farg(self, compressor); + if (self.value) self.body = [ self.first_statement() ]; + var body = tighten_body(self.body, compressor); + switch (body.length) { + case 1: + var stat = body[0]; + if (stat instanceof AST_Return) { + self.body.length = 0; + self.value = stat.value; + break; + } + default: + self.body = body; + self.value = null; + break; + } + return self; + } + OPT(AST_Arrow, opt_arrow); + OPT(AST_AsyncArrow, opt_arrow); + + OPT(AST_Function, function(self, compressor) { + drop_rest_farg(self, compressor); + self.body = tighten_body(self.body, compressor); + var parent = compressor.parent(); + if (compressor.option("inline")) for (var i = 0; i < self.body.length; i++) { + var stat = self.body[i]; + if (stat instanceof AST_Directive) continue; + if (stat instanceof AST_Return) { + if (i != self.body.length - 1) break; + var call = stat.value; + if (!call || call.TYPE != "Call") break; + if (call.is_expr_pure(compressor)) break; + var exp = call.expression, fn; + if (!(exp instanceof AST_SymbolRef)) { + fn = exp; + } else if (self.name && self.name.definition() === exp.definition()) { + break; + } else { + fn = exp.fixed_value(); + } + if (!(fn instanceof AST_Defun || fn instanceof AST_Function)) break; + if (fn.rest) break; + if (fn.uses_arguments) break; + if (fn === exp) { + if (fn.parent_scope !== self) break; + if (!all(fn.enclosed, function(def) { + return def.scope !== self; + })) break; + } + if ((fn !== exp || fn.name) + && (parent instanceof AST_ClassMethod || parent instanceof AST_ObjectMethod) + && parent.value === compressor.self()) break; + if (fn.contains_this()) break; + var len = fn.argnames.length; + if (len > 0 && compressor.option("inline") < 2) break; + if (len > self.argnames.length) break; + if (!all(self.argnames, function(argname) { + return argname instanceof AST_SymbolFunarg; + })) break; + if (!all(call.args, function(arg) { + return !(arg instanceof AST_Spread); + })) break; + for (var j = 0; j < len; j++) { + var arg = call.args[j]; + if (!(arg instanceof AST_SymbolRef)) break; + if (arg.definition() !== self.argnames[j].definition()) break; + } + if (j < len) break; + for (; j < call.args.length; j++) { + if (call.args[j].has_side_effects(compressor)) break; + } + if (j < call.args.length) break; + if (len < self.argnames.length && !compressor.drop_fargs(self, parent)) { + if (!compressor.drop_fargs(fn, call)) break; + do { + fn.argnames.push(fn.make_var(AST_SymbolFunarg, fn, "argument_" + len)); + } while (++len < self.argnames.length); + } + return exp; + } + break; + } + return self; + }); + + var NO_MERGE = makePredicate("arguments await yield"); + AST_Scope.DEFMETHOD("merge_variables", function(compressor) { + if (!compressor.option("merge_vars")) return; + var in_arg = [], in_try, root, segment = {}, self = this; + var first = [], last = [], index = 0; + var declarations = new Dictionary(); + var references = Object.create(null); + var prev = Object.create(null); + var tw = new TreeWalker(function(node, descend) { + if (node instanceof AST_Assign) { + var lhs = node.left; + var rhs = node.right; + if (lhs instanceof AST_Destructured) { + rhs.walk(tw); + walk_destructured(AST_SymbolRef, mark, lhs); + return true; + } + if (lazy_op[node.operator.slice(0, -1)]) { + lhs.walk(tw); + push(); + rhs.walk(tw); + if (lhs instanceof AST_SymbolRef) mark(lhs); + pop(); + return true; + } + if (lhs instanceof AST_SymbolRef) { + if (node.operator != "=") mark(lhs, true); + rhs.walk(tw); + mark(lhs); + return true; + } + return; + } + if (node instanceof AST_Binary) { + if (!lazy_op[node.operator]) return; + walk_cond(node); + return true; + } + if (node instanceof AST_Break) { + var target = tw.loopcontrol_target(node); + if (!(target instanceof AST_IterationStatement)) insert(target); + return true; + } + if (node instanceof AST_Call) { + var exp = node.expression; + if (exp instanceof AST_LambdaExpression) { + node.args.forEach(function(arg) { + arg.walk(tw); + }); + exp.walk(tw); + } else { + descend(); + mark_expression(exp); + } + return true; + } + if (node instanceof AST_Class) { + if (node.name) node.name.walk(tw); + if (node.extends) node.extends.walk(tw); + node.properties.filter(function(prop) { + if (prop.key instanceof AST_Node) prop.key.walk(tw); + return prop.value; + }).forEach(function(prop) { + if (prop.static) { + prop.value.walk(tw); + } else { + push(); + segment.block = node; + prop.value.walk(tw); + pop(); + } + }); + return true; + } + if (node instanceof AST_Conditional) { + walk_cond(node.condition, node.consequent, node.alternative); + return true; + } + if (node instanceof AST_Continue) { + var target = tw.loopcontrol_target(node); + if (target instanceof AST_Do) insert(target); + return true; + } + if (node instanceof AST_Do) { + push(); + segment.block = node; + segment.loop = true; + var save = segment; + node.body.walk(tw); + if (segment.inserted === node) segment = save; + node.condition.walk(tw); + pop(); + return true; + } + if (node instanceof AST_For) { + if (node.init) node.init.walk(tw); + push(); + segment.block = node; + segment.loop = true; + if (node.condition) node.condition.walk(tw); + node.body.walk(tw); + if (node.step) node.step.walk(tw); + pop(); + return true; + } + if (node instanceof AST_ForEnumeration) { + node.object.walk(tw); + push(); + segment.block = node; + segment.loop = true; + node.init.walk(tw); + node.body.walk(tw); + pop(); + return true; + } + if (node instanceof AST_If) { + walk_cond(node.condition, node.body, node.alternative); + return true; + } + if (node instanceof AST_LabeledStatement) { + push(); + segment.block = node; + var save = segment; + node.body.walk(tw); + if (segment.inserted === node) segment = save; + pop(); + return true; + } + if (node instanceof AST_Scope) { + push(); + segment.block = node; + if (node === self) root = segment; + if (node instanceof AST_Lambda) { + if (node.name) references[node.name.definition().id] = false; + var marker = node.uses_arguments && !tw.has_directive("use strict") ? function(node) { + references[node.definition().id] = false; + } : function(node) { + mark(node); + }; + in_arg.push(node); + node.argnames.forEach(function(argname) { + walk_destructured(AST_SymbolFunarg, marker, argname); + }); + if (node.rest) walk_destructured(AST_SymbolFunarg, marker, node.rest); + in_arg.pop(); + } + walk_lambda(node, tw); + pop(); + return true; + } + if (node instanceof AST_Sub) { + var exp = node.expression; + if (node.optional) { + exp.walk(tw); + push(); + node.property.walk(tw); + pop(); + } else { + descend(); + } + mark_expression(exp); + return true; + } + if (node instanceof AST_Switch) { + node.expression.walk(tw); + var save = segment; + node.body.forEach(function(branch) { + if (branch instanceof AST_Default) return; + branch.expression.walk(tw); + if (save === segment) push(); + }); + segment = save; + node.body.forEach(function(branch) { + push(); + segment.block = node; + var save = segment; + walk_body(branch, tw); + if (segment.inserted === node) segment = save; + pop(); + }); + return true; + } + if (node instanceof AST_SymbolDeclaration) { + references[node.definition().id] = false; + return true; + } + if (node instanceof AST_SymbolRef) { + mark(node, true); + return true; + } + if (node instanceof AST_Try) { + var save_try = in_try; + in_try = node; + walk_body(node, tw); + if (node.bcatch) { + if (node.bcatch.argname) node.bcatch.argname.mark_symbol(function(node) { + if (node instanceof AST_SymbolCatch) { + var def = node.definition(); + references[def.id] = false; + if (def = def.redefined()) references[def.id] = false; + } + }, tw); + if (node.bfinally || (in_try = save_try)) { + walk_body(node.bcatch, tw); + } else { + push(); + walk_body(node.bcatch, tw); + pop(); + } + } + in_try = save_try; + if (node.bfinally) node.bfinally.walk(tw); + return true; + } + if (node instanceof AST_Unary) { + if (!UNARY_POSTFIX[node.operator]) return; + var sym = node.expression; + if (!(sym instanceof AST_SymbolRef)) return; + mark(sym, true); + return true; + } + if (node instanceof AST_VarDef) { + var assigned = node.value; + if (assigned) { + assigned.walk(tw); + } else { + assigned = segment.block instanceof AST_ForEnumeration && segment.block.init === tw.parent(); + } + walk_destructured(AST_SymbolDeclaration, assigned ? function(node) { + if (node instanceof AST_SymbolVar) { + mark(node); + } else { + node.walk(tw); + } + } : function(node) { + if (node instanceof AST_SymbolVar) { + var id = node.definition().id; + var refs = references[id]; + if (refs) { + refs.push(node); + } else if (!(id in references)) { + declarations.add(id, node); + } + } else { + node.walk(tw); + } + }, node.name); + return true; + } + if (node instanceof AST_While) { + push(); + segment.block = node; + segment.loop = true; + descend(); + pop(); + return true; + } + + function mark_expression(exp) { + if (!compressor.option("ie")) return; + var sym = root_expr(exp); + if (sym instanceof AST_SymbolRef) sym.walk(tw); + } + + function walk_cond(condition, consequent, alternative) { + var save = segment; + var segments = scan_branches(1, condition, consequent, alternative); + if (consequent) { + segment = segments.consequent.segment; + for (var i = segments.consequent.level; --i >= 0;) pop(); + if (segment !== save) return; + } + if (alternative) { + segment = segments.alternative.segment; + for (var i = segments.alternative.level; --i >= 0;) pop(); + if (segment !== save) return; + } + segment = save; + } + + function scan_branches(level, condition, consequent, alternative) { + var segments = { + consequent: { + segment: segment, + level: level, + }, + alternative: { + segment: segment, + level: level, + }, + } + if (condition instanceof AST_Binary) switch (condition.operator) { + case "&&": + segments.consequent = scan_branches(level + 1, condition.left, condition.right).consequent; + break; + case "||": + case "??": + segments.alternative = scan_branches(level + 1, condition.left, null, condition.right).alternative; + break; + default: + condition.walk(tw); + break; + } else if (condition instanceof AST_Conditional) { + scan_branches(level + 1, condition.condition, condition.consequent, condition.alternative); + } else { + condition.walk(tw); + } + if (consequent) { + segment = segments.consequent.segment; + push(); + consequent.walk(tw); + segments.consequent.segment = segment; + } + if (alternative) { + segment = segments.alternative.segment; + push(); + alternative.walk(tw); + segments.alternative.segment = segment; + } + return segments; + } + }); + tw.directives = Object.create(compressor.directives); + self.walk(tw); + var changed = false; + var merged = Object.create(null); + while (first.length && last.length) { + var tail = last.shift(); + if (!tail) continue; + var def = tail.definition; + var tail_refs = references[def.id]; + if (!tail_refs) continue; + tail_refs = { end: tail_refs.end }; + while (def.id in merged) def = merged[def.id]; + tail_refs.start = references[def.id].start; + var skipped = []; + do { + var head = first.shift(); + if (tail.index > head.index) continue; + var prev_def = head.definition; + if (!(prev_def.id in prev)) continue; + var head_refs = references[prev_def.id]; + if (!head_refs) continue; + if (head_refs.start.block !== tail_refs.start.block + || !mergeable(head_refs, tail_refs) + || (head_refs.start.loop || !same_scope(def)) && !mergeable(tail_refs, head_refs) + || compressor.option("webkit") && is_funarg(def) !== is_funarg(prev_def) + || prev_def.const_redefs + || !all(head_refs.scopes, function(scope) { + return scope.find_variable(def.name) === def; + })) { + skipped.push(head); + continue; + } + head_refs.forEach(function(sym) { + sym.thedef = def; + sym.name = def.name; + if (sym instanceof AST_SymbolRef) { + def.references.push(sym); + prev_def.replaced++; + } else { + def.orig.push(sym); + prev_def.eliminated++; + } + }); + if (!prev_def.fixed) def.fixed = false; + merged[prev_def.id] = def; + changed = true; + break; + } while (first.length); + if (skipped.length) first = skipped.concat(first); + } + return changed; + + function push() { + segment = Object.create(segment); + } + + function pop() { + segment = Object.getPrototypeOf(segment); + } + + function walk_destructured(symbol_type, mark, lhs) { + var marker = new TreeWalker(function(node) { + if (node instanceof AST_Destructured) return; + if (node instanceof AST_DefaultValue) { + push(); + node.value.walk(tw); + pop(); + node.name.walk(marker); + } else if (node instanceof AST_DestructuredKeyVal) { + if (!(node.key instanceof AST_Node)) { + node.value.walk(marker); + } else if (node.value instanceof AST_PropAccess) { + push(); + segment.block = node; + node.key.walk(tw); + node.value.walk(marker); + pop(); + } else { + node.key.walk(tw); + node.value.walk(marker); + } + } else if (node instanceof symbol_type) { + mark(node); + } else { + node.walk(tw); + } + return true; + }); + lhs.walk(marker); + } + + function mark(sym, read) { + var def = sym.definition(), ldef; + if (read && !all(in_arg, function(fn) { + ldef = fn.variables.get(sym.name); + if (!ldef) return true; + if (!is_funarg(ldef)) return true; + return ldef !== def + && !def.undeclared + && fn.parent_scope.find_variable(sym.name) !== def; + })) return references[def.id] = references[ldef.id] = false; + var seg = segment; + if (in_try) { + push(); + seg = segment; + pop(); + } + if (def.id in references) { + var refs = references[def.id]; + if (!refs) return; + if (refs.start.block !== seg.block) return references[def.id] = false; + push_ref(sym); + refs.end = seg; + if (def.id in prev) { + last[prev[def.id]] = null; + } else if (!read) { + return; + } + } else if ((ldef = self.variables.get(def.name)) !== def) { + if (ldef && root === seg) references[ldef.id] = false; + return references[def.id] = false; + } else if (compressor.exposed(def) || NO_MERGE[sym.name]) { + return references[def.id] = false; + } else { + var refs = declarations.get(def.id) || []; + refs.scopes = []; + push_ref(sym); + references[def.id] = refs; + if (!read) { + refs.start = seg; + return first.push({ + index: index++, + definition: def, + }); + } + if (seg.block !== self) return references[def.id] = false; + refs.start = root; + } + prev[def.id] = last.length; + last.push({ + index: index++, + definition: def, + }); + + function push_ref(sym) { + refs.push(sym); + push_uniq(refs.scopes, sym.scope); + var scope = find_scope(tw); + if (scope !== sym.scope) push_uniq(refs.scopes, scope); + } + } + + function insert(target) { + var stack = []; + while (true) { + if (HOP(segment, "block")) { + var block = segment.block; + if (block instanceof AST_LabeledStatement) block = block.body; + if (block === target) break; + } + stack.push(segment); + pop(); + } + segment.inserted = segment.block; + push(); + while (stack.length) { + var seg = stack.pop(); + push(); + if (HOP(seg, "block")) segment.block = seg.block; + if (HOP(seg, "loop")) segment.loop = seg.loop; + } + } + + function must_visit(base, segment) { + return base === segment || base.isPrototypeOf(segment); + } + + function mergeable(head, tail) { + return must_visit(head.start, head.end) || must_visit(head.start, tail.start); + } + }); + + function fill_holes(orig, elements) { + for (var i = elements.length; --i >= 0;) { + if (!elements[i]) elements[i] = make_node(AST_Hole, orig); + } + } + + function to_class_expr(defcl, drop_name) { + var cl = make_node(AST_ClassExpression, defcl); + if (cl.name) cl.name = drop_name ? null : make_node(AST_SymbolClass, cl.name); + return cl; + } + + function to_func_expr(defun, drop_name) { + var ctor; + switch (defun.CTOR) { + case AST_AsyncDefun: + ctor = AST_AsyncFunction; + break; + case AST_AsyncGeneratorDefun: + ctor = AST_AsyncGeneratorFunction; + break; + case AST_Defun: + ctor = AST_Function; + break; + case AST_GeneratorDefun: + ctor = AST_GeneratorFunction; + break; + } + var fn = make_node(ctor, defun); + fn.name = drop_name ? null : make_node(AST_SymbolLambda, defun.name); + return fn; + } + + AST_Scope.DEFMETHOD("drop_unused", function(compressor) { + if (!compressor.option("unused")) return; + var self = this; + var drop_funcs = !(self instanceof AST_Toplevel) || compressor.toplevel.funcs; + var drop_vars = !(self instanceof AST_Toplevel) || compressor.toplevel.vars; + var assign_as_unused = /keep_assign/.test(compressor.option("unused")) ? return_false : function(node, props) { + var sym, nested = false; + if (node instanceof AST_Assign) { + if (node.write_only || node.operator == "=") sym = extract_reference(node.left, props); + } else if (node instanceof AST_Unary) { + if (node.write_only) sym = extract_reference(node.expression, props); + } + if (!(sym instanceof AST_SymbolRef)) return; + var def = sym.definition(); + if (export_defaults[def.id]) return; + if (compressor.exposed(def)) return; + if (!can_drop_symbol(sym, compressor, nested)) return; + return sym; + + function extract_reference(node, props) { + if (node instanceof AST_PropAccess) { + var expr = node.expression; + if (!expr.may_throw_on_access(compressor, true)) { + nested = true; + if (props && node instanceof AST_Sub) props.unshift(node.property); + return extract_reference(expr, props); + } + } else if (node instanceof AST_Assign && node.operator == "=") { + node.write_only = "p"; + var ref = extract_reference(node.right); + if (!props) return ref; + props.assign = node; + return ref instanceof AST_SymbolRef ? ref : node.left; + } + return node; + } + }; + var assign_in_use = Object.create(null); + var export_defaults = Object.create(null); + var find_variable = function(name) { + find_variable = compose(self, 0, noop); + return find_variable(name); + + function compose(child, level, find) { + var parent = compressor.parent(level); + if (!parent) return find; + var in_arg = parent instanceof AST_Lambda && member(child, parent.argnames); + return compose(parent, level + 1, in_arg ? function(name) { + var def = find(name); + if (def) return def; + def = parent.variables.get(name); + if (def) { + var sym = def.orig[0]; + if (sym instanceof AST_SymbolFunarg || sym instanceof AST_SymbolLambda) return def; + } + } : parent.variables ? function(name) { + return find(name) || parent.variables.get(name); + } : find); + } + }; + var for_ins = Object.create(null); + var in_use = []; + var in_use_ids = Object.create(null); // avoid expensive linear scans of in_use + var lambda_ids = Object.create(null); + var value_read = Object.create(null); + var value_modified = Object.create(null); + var var_defs = Object.create(null); + if (self instanceof AST_Toplevel && compressor.top_retain) { + self.variables.each(function(def) { + if (compressor.top_retain(def) && !(def.id in in_use_ids)) { + AST_Node.info("Retaining variable {name}", def); + in_use_ids[def.id] = true; + in_use.push(def); + } + }); + } + var assignments = new Dictionary(); + var initializations = new Dictionary(); + // pass 1: find out which symbols are directly used in + // this scope (not in nested scopes). + var scope = this; + var tw = new TreeWalker(function(node, descend) { + if (node instanceof AST_Lambda && node.uses_arguments && !tw.has_directive("use strict")) { + node.each_argname(function(argname) { + var def = argname.definition(); + if (!(def.id in in_use_ids)) { + in_use_ids[def.id] = true; + in_use.push(def); + } + }); + } + if (node === self) return; + if (scope === self) { + if (node instanceof AST_DefClass) { + var def = node.name.definition(); + var drop = drop_funcs && !def.exported; + if (!drop && !(def.id in in_use_ids)) { + in_use_ids[def.id] = true; + in_use.push(def); + } + var used = tw.parent() instanceof AST_ExportDefault; + if (used) { + export_defaults[def.id] = true; + } else if (drop && !(def.id in lambda_ids)) { + lambda_ids[def.id] = 1; + } + if (node.extends) node.extends.walk(tw); + var values = []; + node.properties.forEach(function(prop) { + if (prop.key instanceof AST_Node) prop.key.walk(tw); + var value = prop.value; + if (!value) return; + if (is_static_field_or_init(prop)) { + if (!used && value.contains_this()) used = true; + walk_class_prop(value); + } else { + values.push(value); + } + }); + values.forEach(drop && used ? walk_class_prop : function(value) { + initializations.add(def.id, value); + }); + return true; + } + if (node instanceof AST_LambdaDefinition) { + var def = node.name.definition(); + var drop = drop_funcs && !def.exported; + if (!drop && !(def.id in in_use_ids)) { + in_use_ids[def.id] = true; + in_use.push(def); + } + initializations.add(def.id, node); + if (tw.parent() instanceof AST_ExportDefault) { + export_defaults[def.id] = true; + return scan_ref_scoped(node, descend, true); + } + if (drop && !(def.id in lambda_ids)) lambda_ids[def.id] = 1; + return true; + } + if (node instanceof AST_Definitions) { + node.definitions.forEach(function(defn) { + var value = defn.value; + var side_effects = value + && (defn.name instanceof AST_Destructured || value.has_side_effects(compressor)); + var shared = side_effects && value.tail_node().operator == "="; + defn.name.mark_symbol(function(name) { + if (!(name instanceof AST_SymbolDeclaration)) return; + var def = name.definition(); + var_defs[def.id] = (var_defs[def.id] || 0) + 1; + if (node instanceof AST_Var && def.orig[0] instanceof AST_SymbolCatch) { + var redef = def.redefined(); + if (redef) var_defs[redef.id] = (var_defs[redef.id] || 0) + 1; + } + if (!(def.id in in_use_ids) && (!drop_vars || def.exported + || (node instanceof AST_Const ? def.redefined() : def.const_redefs) + || !(node instanceof AST_Var || is_safe_lexical(def)))) { + in_use_ids[def.id] = true; + in_use.push(def); + } + if (value) { + if (!side_effects) { + initializations.add(def.id, value); + } else if (shared) { + verify_safe_usage(def, name, value_modified[def.id]); + } + assignments.add(def.id, defn); + } + unmark_lambda(def); + return true; + }, tw); + if (side_effects) value.walk(tw); + }); + return true; + } + if (node instanceof AST_SymbolFunarg) { + var def = node.definition(); + var_defs[def.id] = (var_defs[def.id] || 0) + 1; + assignments.add(def.id, node); + return true; + } + if (node instanceof AST_SymbolImport) { + var def = node.definition(); + if (!(def.id in in_use_ids) && (!drop_vars || !is_safe_lexical(def))) { + in_use_ids[def.id] = true; + in_use.push(def); + } + return true; + } + } + return scan_ref_scoped(node, descend, true); + + function walk_class_prop(value) { + var save_scope = scope; + scope = node; + value.walk(tw); + scope = save_scope; + } + }); + tw.directives = Object.create(compressor.directives); + self.walk(tw); + var drop_fn_name = compressor.option("keep_fnames") ? return_false : compressor.option("ie") ? function(def) { + return !compressor.exposed(def) && def.references.length == def.replaced; + } : function(def) { + if (!(def.id in in_use_ids)) return true; + if (def.orig.length - def.eliminated < 2) return false; + // function argument will always overshadow its name + if (def.orig[1] instanceof AST_SymbolFunarg) return true; + // retain if referenced within destructured object of argument + return all(def.references, function(ref) { + return !ref.in_arg; + }); + }; + if (compressor.option("ie")) initializations.each(function(init, id) { + if (id in in_use_ids) return; + init.forEach(function(init) { + init.walk(new TreeWalker(function(node) { + if (node instanceof AST_Function && node.name && !drop_fn_name(node.name.definition())) { + node.walk(tw); + return true; + } + if (node instanceof AST_Scope) return true; + })); + }); + }); + // pass 2: for every used symbol we need to walk its + // initialization code to figure out if it uses other + // symbols (that may not be in_use). + tw = new TreeWalker(scan_ref_scoped); + for (var i = 0; i < in_use.length; i++) { + var init = initializations.get(in_use[i].id); + if (init) init.forEach(function(init) { + init.walk(tw); + }); + } + Object.keys(assign_in_use).forEach(function(id) { + var assigns = assign_in_use[id]; + if (!assigns) { + delete assign_in_use[id]; + return; + } + assigns = assigns.reduce(function(in_use, assigns) { + assigns.forEach(function(assign) { + push_uniq(in_use, assign); + }); + return in_use; + }, []); + var in_use = (assignments.get(id) || []).filter(function(node) { + return find_if(node instanceof AST_Unary ? function(assign) { + return assign === node; + } : function(assign) { + if (assign === node) return true; + if (assign instanceof AST_Unary) return false; + return get_rvalue(assign) === get_rvalue(node); + }, assigns); + }); + if (assigns.length == in_use.length) { + assign_in_use[id] = in_use; + } else { + delete assign_in_use[id]; + } + }); + // pass 3: we should drop declarations not in_use + var calls_to_drop_args = []; + var fns_with_marked_args = []; + var trimmer = new TreeTransformer(function(node) { + if (node instanceof AST_DefaultValue) return trim_default(trimmer, node); + if (node instanceof AST_Destructured && node.rest) node.rest = node.rest.transform(trimmer); + if (node instanceof AST_DestructuredArray) { + var trim = !node.rest; + for (var i = node.elements.length; --i >= 0;) { + var element = node.elements[i].transform(trimmer); + if (element) { + node.elements[i] = element; + trim = false; + } else if (trim) { + node.elements.pop(); + } else { + node.elements[i] = make_node(AST_Hole, node.elements[i]); + } + } + return node; + } + if (node instanceof AST_DestructuredObject) { + var properties = []; + node.properties.forEach(function(prop) { + var retain = false; + if (prop.key instanceof AST_Node) { + prop.key = prop.key.transform(tt); + retain = prop.key.has_side_effects(compressor); + } + if ((retain || node.rest) && is_decl(prop.value)) { + prop.value = prop.value.transform(tt); + properties.push(prop); + } else { + var value = prop.value.transform(trimmer); + if (!value && node.rest) { + if (prop.value instanceof AST_DestructuredArray) { + value = make_node(AST_DestructuredArray, prop.value, { elements: [] }); + } else { + value = make_node(AST_DestructuredObject, prop.value, { properties: [] }); + } + } + if (value) { + prop.value = value; + properties.push(prop); + } + } + }); + node.properties = properties; + return node; + } + if (node instanceof AST_SymbolDeclaration) return trim_decl(node); + }); + var tt = new TreeTransformer(function(node, descend, in_list) { + var parent = tt.parent(); + if (drop_vars) { + var props = [], sym = assign_as_unused(node, props); + if (sym) { + var value; + if (can_drop_lhs(sym, node)) { + if (node instanceof AST_Assign) { + value = get_rhs(node); + if (node.write_only === true) value = value.drop_side_effect_free(compressor); + } + if (!value) value = make_node(AST_Number, node, { value: 0 }); + } + if (value) { + if (props.assign) { + var assign = props.assign.drop_side_effect_free(compressor); + if (assign) { + assign.write_only = true; + props.unshift(assign); + } + } + if (!(parent instanceof AST_Sequence) + || parent.tail_node() === node + || value.has_side_effects(compressor)) { + props.push(value); + } + switch (props.length) { + case 0: + return List.skip; + case 1: + return maintain_this_binding(parent, node, props[0].transform(tt)); + default: + return make_sequence(node, props.map(function(prop) { + return prop.transform(tt); + })); + } + } + } else if (node instanceof AST_UnaryPostfix + && node.expression instanceof AST_SymbolRef + && indexOf_assign(node.expression.definition(), node) < 0) { + return make_node(AST_UnaryPrefix, node, { + operator: "+", + expression: node.expression, + }); + } + } + if (node instanceof AST_Binary && node.operator == "instanceof") { + var sym = node.right; + if (!(sym instanceof AST_SymbolRef)) return; + if (sym.definition().id in in_use_ids) return; + var lhs = node.left.drop_side_effect_free(compressor); + var value = make_node(AST_False, node).optimize(compressor); + return lhs ? make_sequence(node, [ lhs, value ]) : value; + } + if (node instanceof AST_Call) { + calls_to_drop_args.push(node); + node.args = node.args.map(function(arg) { + return arg.transform(tt); + }); + node.expression = node.expression.transform(tt); + return node; + } + if (scope !== self) return; + if (drop_funcs && node !== self && node instanceof AST_DefClass) { + var def = node.name.definition(); + if (!(def.id in in_use_ids)) { + log(node.name, "Dropping unused class {name}"); + def.eliminated++; + descend(node, tt); + var trimmed = to_class_expr(node, true); + if (parent instanceof AST_ExportDefault) return trimmed; + trimmed = trimmed.drop_side_effect_free(compressor, true); + if (trimmed) return make_node(AST_SimpleStatement, node, { body: trimmed }); + return in_list ? List.skip : make_node(AST_EmptyStatement, node); + } + } + if (node instanceof AST_ClassExpression && node.name && drop_fn_name(node.name.definition())) { + node.name = null; + } + if (node instanceof AST_Lambda) { + if (drop_funcs && node !== self && node instanceof AST_LambdaDefinition) { + var def = node.name.definition(); + if (!(def.id in in_use_ids)) { + log(node.name, "Dropping unused function {name}"); + def.eliminated++; + if (parent instanceof AST_ExportDefault) { + descend_scope(); + return to_func_expr(node, true); + } + return in_list ? List.skip : make_node(AST_EmptyStatement, node); + } + } + descend_scope(); + if (node instanceof AST_LambdaExpression && node.name && drop_fn_name(node.name.definition())) { + node.name = null; + } + if (!(node instanceof AST_Accessor)) { + var args, spread, trim = compressor.drop_fargs(node, parent); + if (trim && parent instanceof AST_Call && parent.expression === node) { + args = parent.args; + for (spread = 0; spread < args.length; spread++) { + if (args[spread] instanceof AST_Spread) break; + } + } + var argnames = node.argnames; + var rest = node.rest; + var after = false, before = false; + if (rest) { + before = true; + if (!args || spread < argnames.length || rest instanceof AST_SymbolFunarg) { + rest = rest.transform(trimmer); + } else { + var trimmed = trim_destructured(rest, make_node(AST_Array, parent, { + elements: args.slice(argnames.length), + }), trim_decl, !node.uses_arguments, rest); + rest = trimmed.name; + args.length = argnames.length; + if (trimmed.value.elements.length) [].push.apply(args, trimmed.value.elements); + } + if (rest instanceof AST_Destructured && !rest.rest) { + if (rest instanceof AST_DestructuredArray) { + if (rest.elements.length == 0) rest = null; + } else if (rest.properties.length == 0) { + rest = null; + } + } + node.rest = rest; + if (rest) { + trim = false; + after = true; + } + } + var default_length = trim ? -1 : node.length(); + var trim_value = args && !node.uses_arguments && parent !== compressor.parent(); + for (var i = argnames.length; --i >= 0;) { + var sym = argnames[i]; + if (sym instanceof AST_SymbolFunarg) { + var def = sym.definition(); + if (def.id in in_use_ids) { + trim = false; + if (indexOf_assign(def, sym) < 0) sym.unused = null; + } else if (trim) { + log(sym, "Dropping unused function argument {name}"); + argnames.pop(); + def.eliminated++; + sym.unused = true; + } else { + sym.unused = true; + } + } else { + before = true; + var funarg; + if (!args || spread < i) { + funarg = sym.transform(trimmer); + } else { + var trimmed = trim_destructured(sym, args[i], trim_decl, trim_value, sym); + funarg = trimmed.name; + if (trimmed.value) args[i] = trimmed.value; + } + if (funarg) { + trim = false; + argnames[i] = funarg; + if (!after) after = !(funarg instanceof AST_SymbolFunarg); + } else if (trim) { + log_default(sym, "Dropping unused default argument {name}"); + argnames.pop(); + } else if (i > default_length) { + log_default(sym, "Dropping unused default argument assignment {name}"); + if (sym.name instanceof AST_SymbolFunarg) { + sym.name.unused = true; + } else { + after = true; + } + argnames[i] = sym.name; + } else { + log_default(sym, "Dropping unused default argument value {name}"); + argnames[i] = sym = sym.clone(); + sym.value = make_node(AST_Number, sym, { value: 0 }); + after = true; + } + } + } + if (before && !after && node.uses_arguments && !tt.has_directive("use strict")) { + node.rest = make_node(AST_DestructuredArray, node, { elements: [] }); + } + fns_with_marked_args.push(node); + } + return node; + } + if (node instanceof AST_Catch && node.argname instanceof AST_Destructured) { + node.argname.transform(trimmer); + } + if (node instanceof AST_Definitions && !(parent instanceof AST_ForEnumeration && parent.init === node)) { + // place uninitialized names at the start + var body = [], head = [], tail = []; + // for unused names whose initialization has + // side effects, we can cascade the init. code + // into the next one, or next statement. + var side_effects = []; + var duplicated = 0; + var is_var = node instanceof AST_Var; + node.definitions.forEach(function(def) { + if (def.value) def.value = def.value.transform(tt); + var value = def.value; + if (def.name instanceof AST_Destructured) { + var trimmed = trim_destructured(def.name, value, function(node) { + if (!drop_vars) return node; + if (node.definition().id in in_use_ids) return node; + if (is_catch(node)) return node; + if (is_var && !can_drop_symbol(node)) return node; + return null; + }, true); + if (trimmed.name) { + def = make_node(AST_VarDef, def, { + name: trimmed.name, + value: value = trimmed.value, + }); + flush(); + } else if (trimmed.value) { + side_effects.push(trimmed.value); + } + return; + } + var sym = def.name.definition(); + var drop_sym = is_var ? can_drop_symbol(def.name) : is_safe_lexical(sym); + if (!drop_sym || !drop_vars || sym.id in in_use_ids) { + var index; + if (value && ((index = indexOf_assign(sym, def)) < 0 || self_assign(value.tail_node()))) { + def = def.clone(); + value = value.drop_side_effect_free(compressor); + if (value) AST_Node.warn("Side effects in definition of variable {name} [{start}]", def.name); + if (node instanceof AST_Const) { + def.value = value || make_node(AST_Number, def, { value: 0 }); + } else { + def.value = null; + if (value) side_effects.push(value); + } + value = null; + if (index >= 0) assign_in_use[sym.id][index] = def; + } + var old_def, fn; + if (!value && !(node instanceof AST_Let)) { + if (parent instanceof AST_ExportDeclaration) { + flush(); + } else if (drop_sym && var_defs[sym.id] > 1) { + AST_Node.info("Dropping declaration of variable {name} [{start}]", def.name); + var_defs[sym.id]--; + sym.eliminated++; + } else { + head.push(def); + } + } else if (compressor.option("functions") + && !compressor.option("ie") + && drop_sym + && value + && var_defs[sym.id] == 1 + && sym.assignments == 0 + && (fn = value.tail_node()) instanceof AST_LambdaExpression + && !is_arguments(sym) + && !is_arrow(fn) + && assigned_once(fn, sym.references) + && can_declare_defun(fn) + && (old_def = rename_def(fn, def.name.name)) !== false) { + AST_Node.warn("Declaring {name} as function [{start}]", def.name); + var ctor; + switch (fn.CTOR) { + case AST_AsyncFunction: + ctor = AST_AsyncDefun; + break; + case AST_AsyncGeneratorFunction: + ctor = AST_AsyncGeneratorDefun; + break; + case AST_Function: + ctor = AST_Defun; + break; + case AST_GeneratorFunction: + ctor = AST_GeneratorDefun; + break; + } + var defun = make_node(ctor, fn); + defun.name = make_node(AST_SymbolDefun, def.name); + var name_def = def.name.scope.resolve().def_function(defun.name); + if (old_def) old_def.forEach(function(node) { + node.name = name_def.name; + node.thedef = name_def; + node.reference(); + }); + body.push(defun); + if (value !== fn) [].push.apply(side_effects, value.expressions.slice(0, -1)); + sym.eliminated++; + } else { + if (drop_sym + && var_defs[sym.id] > 1 + && !(parent instanceof AST_ExportDeclaration) + && sym.orig.indexOf(def.name) > sym.eliminated) { + var_defs[sym.id]--; + duplicated++; + } + flush(); + } + } else if (is_catch(def.name)) { + value = value && value.drop_side_effect_free(compressor); + if (value) side_effects.push(value); + if (var_defs[sym.id] > 1) { + AST_Node.warn("Dropping duplicated declaration of variable {name} [{start}]", def.name); + var_defs[sym.id]--; + sym.eliminated++; + } else { + def.value = null; + head.push(def); + } + } else { + value = value && value.drop_side_effect_free(compressor); + if (value) { + AST_Node.warn("Side effects in initialization of unused variable {name} [{start}]", def.name); + side_effects.push(value); + } else { + log(def.name, "Dropping unused variable {name}"); + } + sym.eliminated++; + } + + function self_assign(ref) { + return ref instanceof AST_SymbolRef && ref.definition() === sym; + } + + function assigned_once(fn, refs) { + if (refs.length == 0) return fn === def.name.fixed_value(); + return all(refs, function(ref) { + return fn === ref.fixed_value(); + }); + } + + function can_declare_defun(fn) { + if (!is_var || compressor.has_directive("use strict") || !(fn instanceof AST_Function)) { + return parent instanceof AST_Scope; + } + return parent instanceof AST_Block + || parent instanceof AST_For && parent.init === node + || parent instanceof AST_If; + } + + function rename_def(fn, name) { + if (!fn.name) return null; + var def = fn.name.definition(); + if (def.orig.length > 1) return null; + if (def.assignments > 0) return false; + if (def.name == name) return def; + if (compressor.option("keep_fnames")) return false; + var forbidden; + switch (name) { + case "await": + forbidden = is_async; + break; + case "yield": + forbidden = is_generator; + break; + } + return all(def.references, function(ref) { + var scope = ref.scope; + if (scope.find_variable(name) !== sym) return false; + if (forbidden) do { + scope = scope.resolve(); + if (forbidden(scope)) return false; + } while (scope !== fn && (scope = scope.parent_scope)); + return true; + }) && def; + } + + function is_catch(node) { + var sym = node.definition(); + return sym.orig[0] instanceof AST_SymbolCatch && sym.scope.resolve() === node.scope.resolve(); + } + + function flush() { + if (side_effects.length > 0) { + if (tail.length == 0) { + body.push(make_node(AST_SimpleStatement, node, { + body: make_sequence(node, side_effects), + })); + } else if (value) { + side_effects.push(value); + def.value = make_sequence(value, side_effects); + } else { + def.value = make_node(AST_UnaryPrefix, def, { + operator: "void", + expression: make_sequence(def, side_effects), + }); + } + side_effects = []; + } + tail.push(def); + } + }); + switch (head.length) { + case 0: + if (tail.length == 0) break; + if (tail.length == duplicated) { + [].unshift.apply(side_effects, tail.map(function(def) { + AST_Node.info("Dropping duplicated definition of variable {name} [{start}]", def.name); + var sym = def.name.definition(); + var ref = make_node(AST_SymbolRef, def.name); + sym.references.push(ref); + var assign = make_node(AST_Assign, def, { + operator: "=", + left: ref, + right: def.value, + }); + var index = indexOf_assign(sym, def); + if (index >= 0) assign_in_use[sym.id][index] = assign; + sym.assignments++; + sym.eliminated++; + return assign; + })); + break; + } + case 1: + if (tail.length == 0) { + var id = head[0].name.definition().id; + if (id in for_ins) { + node.definitions = head; + for_ins[id].init = node; + break; + } + } + default: + var seq; + if (tail.length > 0 && (seq = tail[0].value) instanceof AST_Sequence) { + tail[0].value = seq.tail_node(); + body.push(make_node(AST_SimpleStatement, node, { + body: make_sequence(seq, seq.expressions.slice(0, -1)), + })); + } + node.definitions = head.concat(tail); + body.push(node); + } + if (side_effects.length > 0) { + body.push(make_node(AST_SimpleStatement, node, { body: make_sequence(node, side_effects) })); + } + return insert_statements(body, node, in_list); + } + if (node instanceof AST_Assign) { + descend(node, tt); + if (!(node.left instanceof AST_Destructured)) return node; + var trimmed = trim_destructured(node.left, node.right, function(node) { + return node; + }, node.write_only === true); + if (trimmed.name) return make_node(AST_Assign, node, { + operator: node.operator, + left: trimmed.name, + right: trimmed.value, + }); + if (trimmed.value) return trimmed.value; + if (parent instanceof AST_Sequence && parent.tail_node() !== node) return List.skip; + return make_node(AST_Number, node, { value: 0 }); + } + if (node instanceof AST_LabeledStatement && node.body instanceof AST_For) { + // Certain combination of unused name + side effect leads to invalid AST: + // https://github.com/mishoo/UglifyJS/issues/1830 + // We fix it at this stage by moving the label inwards, back to the `for`. + descend(node, tt); + if (node.body instanceof AST_BlockStatement) { + var block = node.body; + node.body = block.body.pop(); + block.body.push(node); + return in_list ? List.splice(block.body) : block; + } + return node; + } + if (node instanceof AST_Scope) { + descend_scope(); + return node; + } + if (node instanceof AST_SymbolImport) { + if (!compressor.option("imports") || node.definition().id in in_use_ids) return node; + return in_list ? List.skip : null; + } + + function descend_scope() { + var save_scope = scope; + scope = node; + descend(node, tt); + scope = save_scope; + } + }, function(node, in_list) { + if (node instanceof AST_BlockStatement) return trim_block(node, tt.parent(), in_list); + if (node instanceof AST_ExportDeclaration) { + var block = node.body; + if (!(block instanceof AST_BlockStatement)) return; + node.body = block.body.pop(); + block.body.push(node); + return in_list ? List.splice(block.body) : block; + } + if (node instanceof AST_For) return patch_for_init(node, in_list); + if (node instanceof AST_ForIn) { + if (!drop_vars || !compressor.option("loops")) return; + if (!is_empty(node.body)) return; + var sym = get_init_symbol(node); + if (!sym) return; + var def = sym.definition(); + if (def.id in in_use_ids) return; + log(sym, "Dropping unused loop variable {name}"); + if (for_ins[def.id] === node) delete for_ins[def.id]; + var body = []; + var value = node.object.drop_side_effect_free(compressor); + if (value) { + AST_Node.warn("Side effects in object of for-in loop [{start}]", value); + body.push(make_node(AST_SimpleStatement, node, { body: value })); + } + if (node.init instanceof AST_Definitions && def.orig[0] instanceof AST_SymbolCatch) { + body.push(node.init); + } + return insert_statements(body, node, in_list); + } + if (node instanceof AST_Import) { + if (node.properties && node.properties.length == 0) node.properties = null; + return node; + } + if (node instanceof AST_Sequence) { + if (node.expressions.length > 1) return; + return maintain_this_binding(tt.parent(), node, node.expressions[0]); + } + }); + tt.push(compressor.parent()); + tt.directives = Object.create(compressor.directives); + self.transform(tt); + if (self instanceof AST_Lambda + && self.body.length == 1 + && self.body[0] instanceof AST_Directive + && self.body[0].value == "use strict") { + self.body.length = 0; + } + calls_to_drop_args.forEach(function(call) { + drop_unused_call_args(call, compressor, fns_with_marked_args); + }); + + function log(sym, text) { + AST_Node[sym.definition().references.length > 0 ? "info" : "warn"](text + " [{start}]", sym); + } + + function log_default(node, text) { + if (node.name instanceof AST_SymbolFunarg) { + log(node.name, text); + } else { + AST_Node.info(text + " [{start}]", node); + } + } + + function get_rvalue(expr) { + return expr[expr instanceof AST_Assign ? "right" : "value"]; + } + + function insert_statements(body, orig, in_list) { + switch (body.length) { + case 0: + return in_list ? List.skip : make_node(AST_EmptyStatement, orig); + case 1: + return body[0]; + default: + return in_list ? List.splice(body) : make_node(AST_BlockStatement, orig, { body: body }); + } + } + + function track_assigns(def, node) { + if (def.scope.resolve() !== self) return false; + if (!def.fixed || !node.fixed) assign_in_use[def.id] = false; + return assign_in_use[def.id] !== false; + } + + function add_assigns(def, node) { + if (!assign_in_use[def.id]) assign_in_use[def.id] = []; + if (node.fixed.assigns) push_uniq(assign_in_use[def.id], node.fixed.assigns); + } + + function indexOf_assign(def, node) { + var nodes = assign_in_use[def.id]; + return nodes && nodes.indexOf(node); + } + + function unmark_lambda(def) { + if (lambda_ids[def.id] > 1 && !(def.id in in_use_ids)) { + in_use_ids[def.id] = true; + in_use.push(def); + } + lambda_ids[def.id] = 0; + } + + function verify_safe_usage(def, read, modified) { + if (def.id in in_use_ids) return; + if (read && modified) { + in_use_ids[def.id] = read; + in_use.push(def); + } else { + value_read[def.id] = read; + value_modified[def.id] = modified; + } + } + + function can_drop_lhs(sym, node) { + var def = sym.definition(); + var in_use = in_use_ids[def.id]; + if (!in_use) return true; + if (node[node instanceof AST_Assign ? "left" : "expression"] !== sym) return false; + return in_use === sym && def.references.length - def.replaced == 1 || indexOf_assign(def, node) < 0; + } + + function get_rhs(assign) { + var rhs = assign.right; + if (!assign.write_only) return rhs; + if (!(rhs instanceof AST_Binary && lazy_op[rhs.operator])) return rhs; + if (!(rhs.left instanceof AST_SymbolRef)) return rhs; + if (!(assign.left instanceof AST_SymbolRef)) return rhs; + var def = assign.left.definition(); + if (rhs.left.definition() !== def) return rhs; + if (rhs.right.has_side_effects(compressor)) return rhs; + if (track_assigns(def, rhs.left)) add_assigns(def, rhs.left); + return rhs.right; + } + + function get_init_symbol(for_in) { + var init = for_in.init; + if (init instanceof AST_Definitions) { + init = init.definitions[0].name; + return init instanceof AST_SymbolDeclaration && init; + } + while (init instanceof AST_PropAccess) init = init.expression.tail_node(); + if (init instanceof AST_SymbolRef) return init; + } + + function scan_ref_scoped(node, descend, init) { + if (node instanceof AST_Assign && node.left instanceof AST_SymbolRef) { + var def = node.left.definition(); + if (def.scope.resolve() === self) assignments.add(def.id, node); + } + if (node instanceof AST_SymbolRef && node.in_arg) var_defs[node.definition().id] = 0; + if (node instanceof AST_Unary && node.expression instanceof AST_SymbolRef) { + var def = node.expression.definition(); + if (def.scope.resolve() === self) assignments.add(def.id, node); + } + var props = [], sym = assign_as_unused(node, props); + if (sym) { + var node_def = sym.definition(); + if (node_def.scope.resolve() !== self && self.variables.get(sym.name) !== node_def) return; + if (is_arguments(node_def) && !all(self.argnames, function(argname) { + return !argname.match_symbol(function(node) { + if (node instanceof AST_SymbolFunarg) { + var def = node.definition(); + return def.references.length > def.replaced; + } + }, true); + })) return; + if (node.write_only === "p" && node.right.may_throw_on_access(compressor, true)) return; + var assign = props.assign; + if (assign) { + assign.write_only = true; + assign.walk(tw); + } + props.forEach(function(prop) { + prop.walk(tw); + }); + if (node instanceof AST_Assign) { + var right = get_rhs(node), shared = false; + if (init && node.write_only === true && !right.has_side_effects(compressor)) { + initializations.add(node_def.id, right); + } else { + right.walk(tw); + shared = right.tail_node().operator == "="; + } + if (node.left === sym) { + if (!node.write_only || shared) { + verify_safe_usage(node_def, sym, value_modified[node_def.id]); + } + } else { + var fixed = sym.fixed_value(); + if (!fixed || !fixed.is_constant()) { + verify_safe_usage(node_def, value_read[node_def.id], true); + } + } + } + if (track_assigns(node_def, sym) && is_lhs(sym, node) !== sym) add_assigns(node_def, sym); + unmark_lambda(node_def); + return true; + } + if (node instanceof AST_Binary) { + if (node.operator != "instanceof") return; + var sym = node.right; + if (!(sym instanceof AST_SymbolRef)) return; + var id = sym.definition().id; + if (!lambda_ids[id]) return; + node.left.walk(tw); + lambda_ids[id]++; + return true; + } + if (node instanceof AST_ForIn) { + if (node.init instanceof AST_SymbolRef && scope === self) { + var id = node.init.definition().id; + if (!(id in for_ins)) for_ins[id] = node; + } + if (!drop_vars || !compressor.option("loops")) return; + if (!is_empty(node.body)) return; + if (node.init.has_side_effects(compressor)) return; + var sym = get_init_symbol(node); + if (!sym) return; + var def = sym.definition(); + if (def.scope.resolve() !== self) { + var d = find_variable(sym.name); + if (d === def || d && d.redefined() === def) return; + } + node.object.walk(tw); + return true; + } + if (node instanceof AST_SymbolRef) { + var node_def = node.definition(); + if (!(node_def.id in in_use_ids)) { + in_use_ids[node_def.id] = true; + in_use.push(node_def); + } + if (cross_scope(node_def.scope, node.scope)) { + var redef = node_def.redefined(); + if (redef && !(redef.id in in_use_ids)) { + in_use_ids[redef.id] = true; + in_use.push(redef); + } + } + if (track_assigns(node_def, node)) add_assigns(node_def, node); + return true; + } + if (node instanceof AST_Scope) { + var save_scope = scope; + scope = node; + descend(); + scope = save_scope; + return true; + } + } + + function is_decl(node) { + return (node instanceof AST_DefaultValue ? node.name : node) instanceof AST_SymbolDeclaration; + } + + function trim_decl(node) { + if (node.definition().id in in_use_ids) return node; + if (node instanceof AST_SymbolFunarg) node.unused = true; + return null; + } + + function trim_default(trimmer, node) { + node.value = node.value.transform(tt); + var name = node.name.transform(trimmer); + if (!name) { + if (node.name instanceof AST_Destructured) return null; + var value = node.value.drop_side_effect_free(compressor); + if (!value) return null; + log(node.name, "Side effects in default value of unused variable {name}"); + node = node.clone(); + node.name.unused = null; + node.value = value; + } + return node; + } + + function trim_destructured(node, value, process, drop, root) { + var trimmer = new TreeTransformer(function(node) { + if (node instanceof AST_DefaultValue) { + if (!(compressor.option("default_values") && value && value.is_defined(compressor))) { + var save_drop = drop; + drop = false; + var trimmed = trim_default(trimmer, node); + drop = save_drop; + if (!trimmed && drop && value) value = value.drop_side_effect_free(compressor); + return trimmed; + } else if (node === root) { + root = node = node.name; + } else { + node = node.name; + } + } + if (node instanceof AST_DestructuredArray) { + var save_drop = drop; + var save_value = value; + if (value instanceof AST_SymbolRef) { + drop = false; + value = value.fixed_value(); + } + var native, values; + if (value instanceof AST_Array) { + native = true; + values = value.elements; + } else { + native = value && value.is_string(compressor); + values = false; + } + var elements = [], newValues = drop && [], pos = 0; + node.elements.forEach(function(element, index) { + value = values && values[index]; + if (value instanceof AST_Hole) { + value = null; + } else if (value instanceof AST_Spread) { + if (drop) { + newValues.length = pos; + fill_holes(save_value, newValues); + [].push.apply(newValues, values.slice(index)); + save_value.elements = newValues; + } + value = values = false; + } + element = element.transform(trimmer); + if (element) elements[pos] = element; + if (drop && value) newValues[pos] = value; + if (element || value || !drop || !values) pos++; + }); + value = values && make_node(AST_Array, save_value, { + elements: values.slice(node.elements.length), + }); + if (node.rest) { + var was_drop = drop; + drop = false; + node.rest = node.rest.transform(compressor.option("rests") ? trimmer : tt); + drop = was_drop; + if (node.rest) elements.length = pos; + } + if (drop) { + if (value && !node.rest) value = value.drop_side_effect_free(compressor); + if (value instanceof AST_Array) { + value = value.elements; + } else if (value instanceof AST_Sequence) { + value = value.expressions; + } else if (value) { + value = [ value ]; + } + if (value && value.length) { + newValues.length = pos; + [].push.apply(newValues, value); + } + } + value = save_value; + drop = save_drop; + if (values && newValues) { + fill_holes(value, newValues); + value = value.clone(); + value.elements = newValues; + } + if (!native) { + elements.length = node.elements.length; + } else if (!node.rest) switch (elements.length) { + case 0: + if (node === root) break; + if (drop) value = value.drop_side_effect_free(compressor); + return null; + case 1: + if (!drop) break; + if (node === root) break; + var sym = elements[0]; + if (sym.has_side_effects(compressor)) break; + if (value.has_side_effects(compressor) && sym.match_symbol(function(node) { + return node instanceof AST_PropAccess; + })) break; + value = make_node(AST_Sub, node, { + expression: value, + property: make_node(AST_Number, node, { value: 0 }), + }); + return sym; + } + fill_holes(node, elements); + node.elements = elements; + return node; + } + if (node instanceof AST_DestructuredObject) { + var save_drop = drop; + var save_value = value; + if (value instanceof AST_SymbolRef) { + drop = false; + value = value.fixed_value(); + } + var prop_keys, prop_map, values; + if (value instanceof AST_Object) { + prop_keys = []; + prop_map = new Dictionary(); + values = value.properties.map(function(prop, index) { + prop = prop.clone(); + if (prop instanceof AST_Spread) { + prop_map = false; + } else { + var key = prop.key; + if (key instanceof AST_Node) key = key.evaluate(compressor, true); + if (key instanceof AST_Node) { + prop_map = false; + } else if (prop_map && !(prop instanceof AST_ObjectSetter)) { + prop_map.set(key, prop); + } + prop_keys[index] = key; + } + return prop; + }); + } + if (node.rest) { + value = false; + node.rest = node.rest.transform(compressor.option("rests") ? trimmer : tt); + } + var can_drop = new Dictionary(); + var drop_keys = drop && new Dictionary(); + var properties = []; + node.properties.map(function(prop) { + var key = prop.key; + if (key instanceof AST_Node) { + prop.key = key = key.transform(tt); + key = key.evaluate(compressor, true); + } + if (key instanceof AST_Node) { + drop_keys = false; + } else { + can_drop.set(key, !can_drop.has(key)); + } + return key; + }).forEach(function(key, index) { + var prop = node.properties[index], trimmed; + if (key instanceof AST_Node) { + drop = false; + value = false; + trimmed = prop.value.transform(trimmer) || retain_lhs(prop.value); + } else { + drop = drop_keys && can_drop.get(key); + var mapped = prop_map && prop_map.get(key); + if (mapped) { + value = mapped.value; + if (value instanceof AST_Accessor) value = false; + } else { + value = false; + } + trimmed = prop.value.transform(trimmer); + if (!trimmed) { + if (node.rest || retain_key(prop)) trimmed = retain_lhs(prop.value); + if (drop_keys && !drop_keys.has(key)) { + if (mapped) { + drop_keys.set(key, mapped); + if (value === null) { + prop_map.set(key, retain_key(mapped) && make_node(AST_ObjectKeyVal, mapped, { + key: mapped.key, + value: make_node(AST_Number, mapped, { value: 0 }), + })); + } + } else { + drop_keys.set(key, true); + } + } + } else if (drop_keys) { + drop_keys.set(key, false); + } + if (value) mapped.value = value; + } + if (trimmed) { + prop.value = trimmed; + properties.push(prop); + } + }); + value = save_value; + drop = save_drop; + if (drop_keys && prop_keys) { + value = value.clone(); + value.properties = List(values, function(prop, index) { + if (prop instanceof AST_Spread) return prop; + var key = prop_keys[index]; + if (key instanceof AST_Node) return prop; + if (drop_keys.has(key)) { + var mapped = drop_keys.get(key); + if (!mapped) return prop; + if (mapped === prop) return prop_map.get(key) || List.skip; + } else if (node.rest) { + return prop; + } + var trimmed = prop.value.drop_side_effect_free(compressor); + if (trimmed) { + prop.value = trimmed; + return prop; + } + return retain_key(prop) ? make_node(AST_ObjectKeyVal, prop, { + key: prop.key, + value: make_node(AST_Number, prop, { value: 0 }), + }) : List.skip; + }); + } + if (value && !node.rest) switch (properties.length) { + case 0: + if (node === root) break; + if (value.may_throw_on_access(compressor, true)) break; + if (drop) value = value.drop_side_effect_free(compressor); + return null; + case 1: + if (!drop) break; + if (node === root) break; + var prop = properties[0]; + if (prop.key instanceof AST_Node) break; + if (prop.value.has_side_effects(compressor)) break; + if (value.has_side_effects(compressor) && prop.value.match_symbol(function(node) { + return node instanceof AST_PropAccess; + })) break; + value = make_node(AST_Sub, node, { + expression: value, + property: make_node_from_constant(prop.key, prop), + }); + return prop.value; + } + node.properties = properties; + return node; + } + if (node instanceof AST_Hole) { + node = null; + } else { + node = process(node); + } + if (!node && drop && value) value = value.drop_side_effect_free(compressor); + return node; + }); + return { + name: node.transform(trimmer), + value: value, + }; + + function retain_key(prop) { + return prop.key instanceof AST_Node && prop.key.has_side_effects(compressor); + } + + function clear_write_only(node) { + if (node instanceof AST_Assign) { + node.write_only = false; + clear_write_only(node.right); + } else if (node instanceof AST_Binary) { + if (!lazy_op[node.operator]) return; + clear_write_only(node.left); + clear_write_only(node.right); + } else if (node instanceof AST_Conditional) { + clear_write_only(node.consequent); + clear_write_only(node.alternative); + } else if (node instanceof AST_Sequence) { + clear_write_only(node.tail_node()); + } else if (node instanceof AST_Unary) { + node.write_only = false; + } + } + + function retain_lhs(node) { + if (node instanceof AST_DefaultValue) return retain_lhs(node.name); + if (node instanceof AST_Destructured) { + if (value === null) { + value = make_node(AST_Number, node, { value: 0 }); + } else if (value) { + if (value.may_throw_on_access(compressor, true)) { + value = make_node(AST_Array, node, { + elements: value instanceof AST_Sequence ? value.expressions : [ value ], + }); + } else { + clear_write_only(value); + } + } + return make_node(AST_DestructuredObject, node, { properties: [] }); + } + node.unused = null; + return node; + } + } + }); + + AST_Scope.DEFMETHOD("hoist_declarations", function(compressor) { + if (compressor.has_directive("use asm")) return; + var hoist_funs = compressor.option("hoist_funs"); + var hoist_vars = compressor.option("hoist_vars"); + var self = this; + if (hoist_vars) { + // let's count var_decl first, we seem to waste a lot of + // space if we hoist `var` when there's only one. + var var_decl = 0; + self.walk(new TreeWalker(function(node) { + if (var_decl > 1) return true; + if (node instanceof AST_ExportDeclaration) return true; + if (node instanceof AST_Scope && node !== self) return true; + if (node instanceof AST_Var) { + var_decl++; + return true; + } + })); + if (var_decl <= 1) hoist_vars = false; + } + if (!hoist_funs && !hoist_vars) return; + var consts = new Dictionary(); + var dirs = []; + var hoisted = []; + var vars = new Dictionary(); + var tt = new TreeTransformer(function(node, descend, in_list) { + if (node === self) return; + if (node instanceof AST_Directive) { + dirs.push(node); + return in_list ? List.skip : make_node(AST_EmptyStatement, node); + } + if (node instanceof AST_LambdaDefinition) { + if (!hoist_funs) return node; + var p = tt.parent(); + if (p instanceof AST_ExportDeclaration) return node; + if (p instanceof AST_ExportDefault) return node; + if (p !== self && compressor.has_directive("use strict")) return node; + hoisted.push(node); + return in_list ? List.skip : make_node(AST_EmptyStatement, node); + } + if (node instanceof AST_Var) { + if (!hoist_vars) return node; + var p = tt.parent(); + if (p instanceof AST_ExportDeclaration) return node; + if (!all(node.definitions, function(defn) { + var sym = defn.name; + return sym instanceof AST_SymbolVar + && !consts.has(sym.name) + && self.find_variable(sym.name) === sym.definition(); + })) return node; + node.definitions.forEach(function(defn) { + vars.set(defn.name.name, defn); + }); + var seq = node.to_assignments(); + if (p instanceof AST_ForEnumeration && p.init === node) { + if (seq) return seq; + var sym = node.definitions[0].name; + return make_node(AST_SymbolRef, sym); + } + if (p instanceof AST_For && p.init === node) return seq; + if (!seq) return in_list ? List.skip : make_node(AST_EmptyStatement, node); + return make_node(AST_SimpleStatement, node, { body: seq }); + } + if (node instanceof AST_Scope) return node; + if (node instanceof AST_SymbolConst) { + consts.set(node.name, true); + return node; + } + }); + self.transform(tt); + if (vars.size() > 0) { + // collect only vars which don't show up in self's arguments list + var defns = []; + if (self instanceof AST_Lambda) self.each_argname(function(argname) { + if (all(argname.definition().references, function(ref) { + return !ref.in_arg; + })) vars.del(argname.name); + }); + vars.each(function(defn, name) { + defn = defn.clone(); + defn.name = defn.name.clone(); + defn.value = null; + defns.push(defn); + vars.set(name, defn); + defn.name.definition().orig.unshift(defn.name); + }); + if (defns.length > 0) hoisted.push(make_node(AST_Var, self, { definitions: defns })); + } + self.body = dirs.concat(hoisted, self.body); + }); + + function scan_local_returns(fn, transform) { + fn.walk(new TreeWalker(function(node) { + if (node instanceof AST_Return) { + transform(node); + return true; + } + if (node instanceof AST_Scope && node !== fn) return true; + })); + } + + function map_self_returns(fn) { + var map = Object.create(null); + scan_local_returns(fn, function(node) { + var value = node.value; + if (value) value = value.tail_node(); + if (value instanceof AST_SymbolRef) { + var id = value.definition().id; + map[id] = (map[id] || 0) + 1; + } + }); + return map; + } + + function can_trim_returns(def, self_returns, compressor) { + if (compressor.exposed(def)) return false; + switch (def.references.length - def.replaced - (self_returns[def.id] || 0)) { + case def.drop_return: + return "d"; + case def.bool_return: + return true; + } + } + + function process_boolean_returns(fn, compressor) { + scan_local_returns(fn, function(node) { + node.in_bool = true; + var value = node.value; + if (value) { + var ev = fuzzy_eval(compressor, value); + if (!ev) { + value = value.drop_side_effect_free(compressor); + node.value = value ? make_sequence(node.value, [ + value, + make_node(AST_Number, node.value, { value: 0 }), + ]) : null; + } else if (!(ev instanceof AST_Node)) { + value = value.drop_side_effect_free(compressor); + node.value = value ? make_sequence(node.value, [ + value, + make_node(AST_Number, node.value, { value: 1 }), + ]) : make_node(AST_Number, node.value, { value: 1 }); + } + } + }); + } + + AST_Scope.DEFMETHOD("process_returns", noop); + AST_Defun.DEFMETHOD("process_returns", function(compressor) { + if (!compressor.option("booleans")) return; + if (compressor.parent() instanceof AST_ExportDefault) return; + switch (can_trim_returns(this.name.definition(), map_self_returns(this), compressor)) { + case "d": + drop_returns(compressor, this, true); + break; + case true: + process_boolean_returns(this, compressor); + break; + } + }); + AST_Function.DEFMETHOD("process_returns", function(compressor) { + if (!compressor.option("booleans")) return; + var drop = true; + var self_returns = map_self_returns(this); + if (this.name && !can_trim(this.name.definition())) return; + var parent = compressor.parent(); + if (parent instanceof AST_Assign) { + if (parent.operator != "=") return; + var sym = parent.left; + if (!(sym instanceof AST_SymbolRef)) return; + if (!can_trim(sym.definition())) return; + } else if (parent instanceof AST_Call && parent.expression !== this) { + var exp = parent.expression; + if (exp instanceof AST_SymbolRef) exp = exp.fixed_value(); + if (!(exp instanceof AST_Lambda)) return; + if (exp.uses_arguments || exp.pinned()) return; + var args = parent.args, sym; + for (var i = 0; i < args.length; i++) { + var arg = args[i]; + if (arg === this) { + sym = exp.argnames[i]; + if (!sym && exp.rest) return; + break; + } + if (arg instanceof AST_Spread) return; + } + if (sym instanceof AST_DefaultValue) sym = sym.name; + if (sym instanceof AST_SymbolFunarg && !can_trim(sym.definition())) return; + } else if (parent.TYPE == "Call") { + compressor.pop(); + var in_bool = compressor.in_boolean_context(); + compressor.push(this); + switch (in_bool) { + case true: + drop = false; + case "d": + break; + default: + return; + } + } else return; + if (drop) { + drop_returns(compressor, this, true); + } else { + process_boolean_returns(this, compressor); + } + + function can_trim(def) { + switch (can_trim_returns(def, self_returns, compressor)) { + case true: + drop = false; + case "d": + return true; + } + } + }); + + AST_BlockScope.DEFMETHOD("var_names", function() { + var var_names = this._var_names; + if (!var_names) { + this._var_names = var_names = new Dictionary(); + this.enclosed.forEach(function(def) { + var_names.set(def.name, true); + }); + this.variables.each(function(def, name) { + var_names.set(name, true); + }); + } + return var_names; + }); + + AST_Scope.DEFMETHOD("make_var", function(type, orig, prefix) { + var scopes = [ this ]; + if (orig instanceof AST_SymbolDeclaration) orig.definition().references.forEach(function(ref) { + var s = ref.scope; + do { + if (!push_uniq(scopes, s)) return; + s = s.parent_scope; + } while (s && s !== this); + }); + prefix = prefix.replace(/^[^a-z_$]|[^a-z0-9_$]/gi, "_"); + var name = prefix; + for (var i = 0; !all(scopes, function(scope) { + return !scope.var_names().has(name); + }); i++) name = prefix + "$" + i; + var sym = make_node(type, orig, { + name: name, + scope: this, + }); + var def = this.def_variable(sym); + scopes.forEach(function(scope) { + scope.enclosed.push(def); + scope.var_names().set(name, true); + }); + return sym; + }); + + AST_Scope.DEFMETHOD("hoist_properties", function(compressor) { + if (!compressor.option("hoist_props") || compressor.has_directive("use asm")) return; + var self = this; + if (is_arrow(self) && self.value) return; + var top_retain = self instanceof AST_Toplevel && compressor.top_retain || return_false; + var defs_by_id = Object.create(null); + var tt = new TreeTransformer(function(node, descend) { + if (node instanceof AST_Assign) { + if (node.operator != "=") return; + if (!node.write_only) return; + if (!can_hoist(node.left, node.right, 1)) return; + descend(node, tt); + var defs = new Dictionary(); + var assignments = []; + var decls = []; + node.right.properties.forEach(function(prop) { + var decl = make_sym(AST_SymbolVar, node.left, prop.key); + decls.push(make_node(AST_VarDef, node, { + name: decl, + value: null, + })); + var sym = make_node(AST_SymbolRef, node, { + name: decl.name, + scope: self, + thedef: decl.definition(), + }); + sym.reference(); + assignments.push(make_node(AST_Assign, node, { + operator: "=", + left: sym, + right: prop.value, + })); + }); + defs.value = node.right; + defs_by_id[node.left.definition().id] = defs; + self.body.splice(self.body.indexOf(tt.stack[1]) + 1, 0, make_node(AST_Var, node, { + definitions: decls, + })); + return make_sequence(node, assignments); + } + if (node instanceof AST_Scope) { + if (node === self) return; + var parent = tt.parent(); + if (parent.TYPE == "Call" && parent.expression === node) return; + return node; + } + if (node instanceof AST_VarDef) { + if (!can_hoist(node.name, node.value, 0)) return; + descend(node, tt); + var defs = new Dictionary(); + var var_defs = []; + var decl = node.clone(); + decl.value = node.name instanceof AST_SymbolConst ? make_node(AST_Number, node, { value: 0 }) : null; + var_defs.push(decl); + node.value.properties.forEach(function(prop) { + var_defs.push(make_node(AST_VarDef, node, { + name: make_sym(node.name.CTOR, node.name, prop.key), + value: prop.value, + })); + }); + defs.value = node.value; + defs_by_id[node.name.definition().id] = defs; + return List.splice(var_defs); + } + + function make_sym(type, sym, key) { + var new_var = self.make_var(type, sym, sym.name + "_" + key); + defs.set(key, new_var.definition()); + return new_var; + } + }); + self.transform(tt); + self.transform(new TreeTransformer(function(node, descend) { + if (node instanceof AST_PropAccess) { + if (!(node.expression instanceof AST_SymbolRef)) return; + var defs = defs_by_id[node.expression.definition().id]; + if (!defs) return; + if (node.expression.fixed_value() !== defs.value) return; + var def = defs.get(node.get_property()); + var sym = make_node(AST_SymbolRef, node, { + name: def.name, + scope: node.expression.scope, + thedef: def, + }); + sym.reference(); + return sym; + } + if (node instanceof AST_SymbolRef) { + var defs = defs_by_id[node.definition().id]; + if (!defs) return; + if (node.fixed_value() !== defs.value) return; + return make_node(AST_Object, node, { properties: [] }); + } + })); + + function can_hoist(sym, right, count) { + if (!(sym instanceof AST_Symbol)) return; + var def = sym.definition(); + if (def.assignments != count) return; + if (def.references.length - def.replaced == count) return; + if (def.single_use) return; + if (self.find_variable(sym.name) !== def) return; + if (top_retain(def)) return; + if (sym.fixed_value() !== right) return; + var fixed = sym.fixed || def.fixed; + if (fixed.direct_access) return; + if (fixed.escaped && fixed.escaped.depth == 1) return; + return right instanceof AST_Object + && right.properties.length > 0 + && can_drop_symbol(sym, compressor) + && all(right.properties, function(prop) { + return can_hoist_property(prop) && prop.key !== "__proto__"; + }); + } + }); + + function fn_name_unused(fn, compressor) { + if (!fn.name || !compressor.option("ie")) return true; + var def = fn.name.definition(); + if (compressor.exposed(def)) return false; + return all(def.references, function(sym) { + return !(sym instanceof AST_SymbolRef); + }); + } + + function drop_returns(compressor, exp, ignore_name) { + if (!(exp instanceof AST_Lambda)) return; + var arrow = is_arrow(exp); + var async = is_async(exp); + var changed = false; + var drop_body = false; + if (arrow && compressor.option("arrows")) { + if (!exp.value) { + drop_body = true; + } else if (!async || needs_enqueuing(compressor, exp.value)) { + var dropped = exp.value.drop_side_effect_free(compressor); + if (dropped !== exp.value) { + changed = true; + exp.value = dropped; + } + } + } else if (!is_generator(exp)) { + if (!ignore_name && exp.name) { + var def = exp.name.definition(); + drop_body = def.references.length == def.replaced; + } else { + drop_body = true; + } + } + if (drop_body) { + exp.process_expression(false, function(node) { + var value = node.value; + if (value) { + if (async && !needs_enqueuing(compressor, value)) return node; + value = value.drop_side_effect_free(compressor, true); + } + changed = true; + if (!value) return make_node(AST_EmptyStatement, node); + return make_node(AST_SimpleStatement, node, { body: value }); + }); + scan_local_returns(exp, function(node) { + var value = node.value; + if (value) { + if (async && !needs_enqueuing(compressor, value)) return; + var dropped = value.drop_side_effect_free(compressor); + if (dropped !== value) { + changed = true; + if (dropped && async && !needs_enqueuing(compressor, dropped)) { + dropped = dropped.negate(compressor); + } + node.value = dropped; + } + } + }); + } + if (async && compressor.option("awaits")) { + if (drop_body) exp.process_expression("awaits", function(node) { + var body = node.body; + if (body instanceof AST_Await) { + if (needs_enqueuing(compressor, body.expression)) { + changed = true; + body = body.expression.drop_side_effect_free(compressor, true); + if (!body) return make_node(AST_EmptyStatement, node); + node.body = body; + } + } else if (body instanceof AST_Sequence) { + var exprs = body.expressions; + for (var i = exprs.length; --i >= 0;) { + var tail = exprs[i]; + if (!(tail instanceof AST_Await)) break; + var value = tail.expression; + if (!needs_enqueuing(compressor, value)) break; + changed = true; + if (exprs[i] = value.drop_side_effect_free(compressor)) break; + } + switch (i) { + case -1: + return make_node(AST_EmptyStatement, node); + case 0: + node.body = exprs[0]; + break; + default: + exprs.length = i + 1; + break; + } + } + return node; + }); + var abort = !drop_body && exp.name || arrow && exp.value && !needs_enqueuing(compressor, exp.value); + var tw = new TreeWalker(function(node) { + if (abort) return true; + if (tw.parent() === exp && node.may_throw(compressor)) return abort = true; + if (node instanceof AST_Await) return abort = true; + if (node instanceof AST_ForAwaitOf) return abort = true; + if (node instanceof AST_Return) { + if (node.value && !needs_enqueuing(compressor, node.value)) return abort = true; + return; + } + if (node instanceof AST_Scope && node !== exp) return true; + }); + exp.walk(tw); + if (!abort) { + var ctor; + switch (exp.CTOR) { + case AST_AsyncArrow: + ctor = AST_Arrow; + break; + case AST_AsyncFunction: + ctor = AST_Function; + break; + case AST_AsyncGeneratorFunction: + ctor = AST_GeneratorFunction; + break; + } + return make_node(ctor, exp); + } + } + return changed && exp.clone(); + } + + // drop_side_effect_free() + // remove side-effect-free parts which only affects return value + (function(def) { + // Drop side-effect-free elements from an array of expressions. + // Returns an array of expressions with side-effects or null + // if all elements were dropped. Note: original array may be + // returned if nothing changed. + function trim(nodes, compressor, first_in_statement, spread) { + var len = nodes.length; + var ret = [], changed = false; + for (var i = 0; i < len; i++) { + var node = nodes[i]; + var trimmed; + if (spread && node instanceof AST_Spread) { + trimmed = spread(node, compressor, first_in_statement); + } else { + trimmed = node.drop_side_effect_free(compressor, first_in_statement); + } + if (trimmed !== node) changed = true; + if (trimmed) { + ret.push(trimmed); + first_in_statement = false; + } + } + return ret.length ? changed ? ret : nodes : null; + } + function array_spread(node, compressor, first_in_statement) { + var exp = node.expression; + if (!exp.is_string(compressor)) return node; + return exp.drop_side_effect_free(compressor, first_in_statement); + } + function convert_spread(node) { + return node instanceof AST_Spread ? make_node(AST_Array, node, { elements: [ node ] }) : node; + } + def(AST_Node, return_this); + def(AST_Accessor, return_null); + def(AST_Array, function(compressor, first_in_statement) { + var values = trim(this.elements, compressor, first_in_statement, array_spread); + if (!values) return null; + if (values === this.elements && all(values, function(node) { + return node instanceof AST_Spread; + })) return this; + return make_sequence(this, values.map(convert_spread)); + }); + def(AST_Assign, function(compressor) { + var left = this.left; + if (left instanceof AST_PropAccess) { + var expr = left.expression; + if (expr.may_throw_on_access(compressor, true)) return this; + if (compressor.has_directive("use strict") && expr.is_constant()) return this; + } + if (left.has_side_effects(compressor)) return this; + if (lazy_op[this.operator.slice(0, -1)]) return this; + this.write_only = true; + if (!root_expr(left).is_constant_expression(compressor.find_parent(AST_Scope))) return this; + return this.right.drop_side_effect_free(compressor); + }); + def(AST_Await, function(compressor) { + if (!compressor.option("awaits")) return this; + var exp = this.expression; + if (!needs_enqueuing(compressor, exp)) return this; + if (exp instanceof AST_UnaryPrefix && exp.operator == "!") exp = exp.expression; + var dropped = exp.drop_side_effect_free(compressor); + if (dropped === exp) return this; + if (!dropped) { + dropped = make_node(AST_Number, exp, { value: 0 }); + } else if (!needs_enqueuing(compressor, dropped)) { + dropped = dropped.negate(compressor); + } + var node = this.clone(); + node.expression = dropped; + return node; + }); + def(AST_Binary, function(compressor, first_in_statement) { + var left = this.left; + var right = this.right; + var op = this.operator; + if (!can_drop_op(this, compressor)) { + var lhs = left.drop_side_effect_free(compressor, first_in_statement); + if (lhs === left) return this; + var node = this.clone(); + if (lhs) { + node.left = lhs; + } else if (op == "instanceof" && !left.is_constant()) { + node.left = make_node(AST_Array, left, { elements: [] }); + } else { + node.left = make_node(AST_Number, left, { value: 0 }); + } + return node; + } + var rhs = right.drop_side_effect_free(compressor, first_in_statement); + if (!rhs) return left.drop_side_effect_free(compressor, first_in_statement); + if (lazy_op[op] && rhs.has_side_effects(compressor)) { + var node = this; + if (rhs !== right) { + node = node.clone(); + node.right = rhs.drop_side_effect_free(compressor); + } + if (op == "??") return node; + var negated = node.clone(); + negated.operator = op == "&&" ? "||" : "&&"; + negated.left = left.negate(compressor, first_in_statement); + var negated_rhs = negated.right.tail_node(); + if (negated_rhs instanceof AST_Binary && negated.operator == negated_rhs.operator) swap_chain(negated); + var best = first_in_statement ? best_of_statement : best_of_expression; + return op == "&&" ? best(node, negated) : best(negated, node); + } + var lhs = left.drop_side_effect_free(compressor, first_in_statement); + if (!lhs) return rhs; + rhs = rhs.drop_side_effect_free(compressor); + if (!rhs) return lhs; + return make_sequence(this, [ lhs, rhs ]); + }); + function assign_this_only(fn, compressor) { + fn.new = true; + var result = all(fn.body, function(stat) { + return !stat.has_side_effects(compressor); + }) && all(fn.argnames, function(argname) { + return !argname.match_symbol(return_false); + }) && !(fn.rest && fn.rest.match_symbol(return_false)); + fn.new = false; + return result; + } + def(AST_Call, function(compressor, first_in_statement) { + var self = this; + if (self.is_expr_pure(compressor)) { + if (self.pure) AST_Node.warn("Dropping __PURE__ call [{start}]", self); + var args = trim(self.args, compressor, first_in_statement, array_spread); + return args && make_sequence(self, args.map(convert_spread)); + } + var exp = self.expression; + if (self.is_call_pure(compressor)) { + var exprs = self.args.slice(); + exprs.unshift(exp.expression); + exprs = trim(exprs, compressor, first_in_statement, array_spread); + return exprs && make_sequence(self, exprs.map(convert_spread)); + } + if (compressor.option("yields") && is_generator(exp) && fn_name_unused(exp, compressor)) { + var call = self.clone(); + call.expression = make_node(AST_Function, exp); + call.expression.body = []; + return call; + } + var dropped = drop_returns(compressor, exp); + if (dropped) { + // always shallow clone to ensure stripping of negated IIFEs + self = self.clone(); + self.expression = dropped; + // avoid extraneous traversal + if (exp._squeezed) self.expression._squeezed = true; + } + if (self instanceof AST_New) { + var fn = exp; + if (fn instanceof AST_SymbolRef) fn = fn.fixed_value(); + if (fn instanceof AST_Lambda) { + if (assign_this_only(fn, compressor)) { + var exprs = self.args.slice(); + exprs.unshift(exp); + exprs = trim(exprs, compressor, first_in_statement, array_spread); + return exprs && make_sequence(self, exprs.map(convert_spread)); + } + if (!fn.contains_this()) { + self = make_node(AST_Call, self); + self.expression = self.expression.clone(); + self.args = self.args.slice(); + } + } + } + self.call_only = true; + return self; + }); + def(AST_ClassExpression, function(compressor, first_in_statement) { + var self = this; + var exprs = [], values = [], init = 0; + var props = self.properties; + for (var i = 0; i < props.length; i++) { + var prop = props[i]; + if (prop.key instanceof AST_Node) exprs.push(prop.key); + if (!is_static_field_or_init(prop)) continue; + var value = prop.value; + if (!value.has_side_effects(compressor)) continue; + if (value.contains_this()) return self; + if (prop instanceof AST_ClassInit) { + init++; + values.push(prop); + } else { + values.push(value); + } + } + var base = self.extends; + if (base) { + if (base instanceof AST_SymbolRef) base = base.fixed_value(); + base = !safe_for_extends(base); + if (!base) exprs.unshift(self.extends); + } + exprs = trim(exprs, compressor, first_in_statement); + if (exprs) first_in_statement = false; + values = trim(values, compressor, first_in_statement); + if (!exprs) { + if (!base && !values && !self.name) return null; + exprs = []; + } + if (base || self.name || !compressor.has_directive("use strict")) { + var node = to_class_expr(self); + if (!base) node.extends = null; + node.properties = []; + if (values) { + if (values.length == init) { + if (exprs.length) values.unshift(make_node(AST_ClassField, self, { + key: make_sequence(self, exprs), + value: null, + })); + node.properties = values; + } else node.properties.push(make_node(AST_ClassField, self, { + static: true, + key: exprs.length ? make_sequence(self, exprs) : "c", + value: make_value(), + })); + } else if (exprs.length) node.properties.push(make_node(AST_ClassMethod, self, { + key: make_sequence(self, exprs), + value: make_node(AST_Function, self, { + argnames: [], + body: [], + }).init_vars(node), + })); + return node; + } + if (values) exprs.push(make_node(AST_Call, self, { + expression: make_node(AST_Arrow, self, { + argnames: [], + body: [], + value: make_value(), + }).init_vars(self.parent_scope, self), + args: [], + })); + return make_sequence(self, exprs); + + function make_value() { + return make_sequence(self, values.map(function(node) { + if (!(node instanceof AST_ClassInit)) return node; + var fn = make_node(AST_Arrow, node.value); + fn.argnames = []; + return make_node(AST_Call, node, { + expression: fn, + args: [], + }); + })); + } + }); + def(AST_Conditional, function(compressor) { + var consequent = this.consequent.drop_side_effect_free(compressor); + var alternative = this.alternative.drop_side_effect_free(compressor); + if (consequent === this.consequent && alternative === this.alternative) return this; + var exprs; + if (compressor.option("ie")) { + exprs = []; + if (consequent instanceof AST_Function) { + exprs.push(consequent); + consequent = null; + } + if (alternative instanceof AST_Function) { + exprs.push(alternative); + alternative = null; + } + } + var node; + if (!consequent) { + node = alternative ? make_node(AST_Binary, this, { + operator: "||", + left: this.condition, + right: alternative, + }) : this.condition.drop_side_effect_free(compressor); + } else if (!alternative) { + node = make_node(AST_Binary, this, { + operator: "&&", + left: this.condition, + right: consequent, + }); + } else { + node = this.clone(); + node.consequent = consequent; + node.alternative = alternative; + } + if (!exprs) return node; + if (node) exprs.push(node); + return exprs.length == 0 ? null : make_sequence(this, exprs); + }); + def(AST_Constant, return_null); + def(AST_Dot, function(compressor, first_in_statement) { + var expr = this.expression; + if (expr.may_throw_on_access(compressor)) return this; + return expr.drop_side_effect_free(compressor, first_in_statement); + }); + def(AST_Function, function(compressor) { + return fn_name_unused(this, compressor) ? null : this; + }); + def(AST_LambdaExpression, return_null); + def(AST_Object, function(compressor, first_in_statement) { + var exprs = []; + this.properties.forEach(function(prop) { + if (prop instanceof AST_Spread) { + exprs.push(prop); + } else { + if (prop.key instanceof AST_Node) exprs.push(prop.key); + exprs.push(prop.value); + } + }); + var values = trim(exprs, compressor, first_in_statement, function(node, compressor, first_in_statement) { + var exp = node.expression; + return exp.safe_to_spread() ? exp.drop_side_effect_free(compressor, first_in_statement) : node; + }); + if (!values) return null; + if (values === exprs && !all(values, function(node) { + return !(node instanceof AST_Spread); + })) return this; + return make_sequence(this, values.map(function(node) { + return node instanceof AST_Spread ? make_node(AST_Object, node, { properties: [ node ] }) : node; + })); + }); + def(AST_ObjectIdentity, return_null); + def(AST_Sequence, function(compressor, first_in_statement) { + var expressions = trim(this.expressions, compressor, first_in_statement); + if (!expressions) return null; + var end = expressions.length - 1; + var last = expressions[end]; + if (compressor.option("awaits") && end > 0 && last instanceof AST_Await && last.expression.is_constant()) { + expressions = expressions.slice(0, -1); + end--; + var expr = expressions[end]; + last.expression = needs_enqueuing(compressor, expr) ? expr : expr.negate(compressor); + expressions[end] = last; + } + var assign, cond, lhs; + if (compressor.option("conditionals") + && end > 0 + && (assign = expressions[end - 1]) instanceof AST_Assign + && assign.operator == "=" + && (lhs = assign.left) instanceof AST_SymbolRef + && (cond = to_conditional_assignment(compressor, lhs.definition(), assign.right, last))) { + assign = assign.clone(); + assign.right = cond; + expressions = expressions.slice(0, -2); + expressions.push(assign.drop_side_effect_free(compressor, first_in_statement)); + } + return expressions === this.expressions ? this : make_sequence(this, expressions); + }); + def(AST_Sub, function(compressor, first_in_statement) { + var expr = this.expression; + if (expr.may_throw_on_access(compressor)) return this; + var prop = this.property; + expr = expr.drop_side_effect_free(compressor, first_in_statement); + if (!expr) return prop.drop_side_effect_free(compressor, first_in_statement); + prop = prop.drop_side_effect_free(compressor); + if (!prop) return expr; + return make_sequence(this, [ expr, prop ]); + }); + def(AST_SymbolRef, function(compressor) { + return this.is_declared(compressor) && can_drop_symbol(this, compressor) ? null : this; + }); + def(AST_Template, function(compressor, first_in_statement) { + var self = this; + if (self.is_expr_pure(compressor)) { + var expressions = self.expressions; + if (expressions.length == 0) return null; + return make_sequence(self, expressions).drop_side_effect_free(compressor, first_in_statement); + } + var tag = self.tag; + var dropped = drop_returns(compressor, tag); + if (dropped) { + // always shallow clone to signal internal changes + self = self.clone(); + self.tag = dropped; + // avoid extraneous traversal + if (tag._squeezed) self.tag._squeezed = true; + } + return self; + }); + def(AST_Unary, function(compressor, first_in_statement) { + var exp = this.expression; + if (unary_side_effects[this.operator]) { + this.write_only = !exp.has_side_effects(compressor); + return this; + } + if (this.operator == "typeof" && exp instanceof AST_SymbolRef && can_drop_symbol(exp, compressor)) { + return null; + } + var node = exp.drop_side_effect_free(compressor, first_in_statement); + if (first_in_statement && node && is_iife_call(node)) { + if (node === exp && this.operator == "!") return this; + return node.negate(compressor, first_in_statement); + } + return node; + }); + })(function(node, func) { + node.DEFMETHOD("drop_side_effect_free", func); + }); + + OPT(AST_SimpleStatement, function(self, compressor) { + if (compressor.option("side_effects")) { + var body = self.body; + var node = body.drop_side_effect_free(compressor, true); + if (!node) { + AST_Node.warn("Dropping side-effect-free statement [{start}]", self); + return make_node(AST_EmptyStatement, self); + } + if (node !== body) { + return make_node(AST_SimpleStatement, self, { body: node }); + } + } + return self; + }); + + OPT(AST_While, function(self, compressor) { + return compressor.option("loops") ? make_node(AST_For, self).optimize(compressor) : self; + }); + + function has_loop_control(loop, parent, type) { + if (!type) type = AST_LoopControl; + var found = false; + var tw = new TreeWalker(function(node) { + if (found || node instanceof AST_Scope) return true; + if (node instanceof type && tw.loopcontrol_target(node) === loop) { + return found = true; + } + }); + if (parent instanceof AST_LabeledStatement) tw.push(parent); + tw.push(loop); + loop.body.walk(tw); + return found; + } + + OPT(AST_Do, function(self, compressor) { + if (!compressor.option("loops")) return self; + var cond = fuzzy_eval(compressor, self.condition); + if (!(cond instanceof AST_Node)) { + if (cond && !has_loop_control(self, compressor.parent(), AST_Continue)) return make_node(AST_For, self, { + body: make_node(AST_BlockStatement, self.body, { + body: [ + self.body, + make_node(AST_SimpleStatement, self.condition, { body: self.condition }), + ], + }), + }).optimize(compressor); + if (!has_loop_control(self, compressor.parent())) return make_node(AST_BlockStatement, self.body, { + body: [ + self.body, + make_node(AST_SimpleStatement, self.condition, { body: self.condition }), + ], + }).optimize(compressor); + } + if (self.body instanceof AST_BlockStatement && !has_loop_control(self, compressor.parent(), AST_Continue)) { + var body = self.body.body; + for (var i = body.length; --i >= 0;) { + var stat = body[i]; + if (stat instanceof AST_If + && !stat.alternative + && stat.body instanceof AST_Break + && compressor.loopcontrol_target(stat.body) === self) { + if (has_block_scope_refs(stat.condition)) break; + self.condition = make_node(AST_Binary, self, { + operator: "&&", + left: stat.condition.negate(compressor), + right: self.condition, + }); + body.splice(i, 1); + } else if (stat instanceof AST_SimpleStatement) { + if (has_block_scope_refs(stat.body)) break; + self.condition = make_sequence(self, [ + stat.body, + self.condition, + ]); + body.splice(i, 1); + } else if (!is_declaration(stat, true)) { + break; + } + } + self.body = trim_block(self.body, compressor.parent()); + } + if (self.body instanceof AST_EmptyStatement) return make_node(AST_For, self).optimize(compressor); + if (self.body instanceof AST_SimpleStatement) return make_node(AST_For, self, { + condition: make_sequence(self.condition, [ + self.body.body, + self.condition, + ]), + body: make_node(AST_EmptyStatement, self), + }).optimize(compressor); + return self; + + function has_block_scope_refs(node) { + var found = false; + node.walk(new TreeWalker(function(node) { + if (found) return true; + if (node instanceof AST_SymbolRef) { + if (!member(node.definition(), self.enclosed)) found = true; + return true; + } + })); + return found; + } + }); + + function if_break_in_loop(self, compressor) { + var first = first_statement(self.body); + if (compressor.option("dead_code") + && (first instanceof AST_Break + || first instanceof AST_Continue && external_target(first) + || first instanceof AST_Exit)) { + var body = []; + if (is_statement(self.init)) { + body.push(self.init); + } else if (self.init) { + body.push(make_node(AST_SimpleStatement, self.init, { body: self.init })); + } + var retain = external_target(first) || first instanceof AST_Exit; + if (self.condition && retain) { + body.push(make_node(AST_If, self, { + condition: self.condition, + body: first, + alternative: null, + })); + } else if (self.condition) { + body.push(make_node(AST_SimpleStatement, self.condition, { body: self.condition })); + } else if (retain) { + body.push(first); + } + extract_declarations_from_unreachable_code(compressor, self.body, body); + return make_node(AST_BlockStatement, self, { body: body }); + } + if (first instanceof AST_If) { + var ab = first_statement(first.body); + if (ab instanceof AST_Break && !external_target(ab)) { + if (self.condition) { + self.condition = make_node(AST_Binary, self.condition, { + left: self.condition, + operator: "&&", + right: first.condition.negate(compressor), + }); + } else { + self.condition = first.condition.negate(compressor); + } + var body = as_statement_array(first.alternative); + extract_declarations_from_unreachable_code(compressor, first.body, body); + return drop_it(body); + } + ab = first_statement(first.alternative); + if (ab instanceof AST_Break && !external_target(ab)) { + if (self.condition) { + self.condition = make_node(AST_Binary, self.condition, { + left: self.condition, + operator: "&&", + right: first.condition, + }); + } else { + self.condition = first.condition; + } + var body = as_statement_array(first.body); + extract_declarations_from_unreachable_code(compressor, first.alternative, body); + return drop_it(body); + } + } + return self; + + function first_statement(body) { + return body instanceof AST_BlockStatement ? body.body[0] : body; + } + + function external_target(node) { + return compressor.loopcontrol_target(node) !== compressor.self(); + } + + function drop_it(rest) { + if (self.body instanceof AST_BlockStatement) { + self.body = self.body.clone(); + self.body.body = rest.concat(self.body.body.slice(1)); + self.body = self.body.transform(compressor); + } else { + self.body = make_node(AST_BlockStatement, self.body, { body: rest }).transform(compressor); + } + return if_break_in_loop(self, compressor); + } + } + + OPT(AST_For, function(self, compressor) { + if (!compressor.option("loops")) return self; + if (compressor.option("side_effects")) { + if (self.init) self.init = self.init.drop_side_effect_free(compressor); + if (self.step) self.step = self.step.drop_side_effect_free(compressor); + } + if (self.condition) { + var cond = fuzzy_eval(compressor, self.condition); + if (!cond) { + if (compressor.option("dead_code")) { + var body = []; + if (is_statement(self.init)) { + body.push(self.init); + } else if (self.init) { + body.push(make_node(AST_SimpleStatement, self.init, { body: self.init })); + } + body.push(make_node(AST_SimpleStatement, self.condition, { body: self.condition })); + extract_declarations_from_unreachable_code(compressor, self.body, body); + return make_node(AST_BlockStatement, self, { body: body }).optimize(compressor); + } + } else if (!(cond instanceof AST_Node)) { + self.body = make_node(AST_BlockStatement, self.body, { + body: [ + make_node(AST_SimpleStatement, self.condition, { body: self.condition }), + self.body, + ], + }); + self.condition = null; + } + } + return if_break_in_loop(self, compressor); + }); + + OPT(AST_ForEnumeration, function(self, compressor) { + if (compressor.option("varify") && is_lexical_definition(self.init)) { + var name = self.init.definitions[0].name; + if ((compressor.option("module") || name instanceof AST_Destructured || name instanceof AST_SymbolLet) + && !name.match_symbol(function(node) { + if (node instanceof AST_SymbolDeclaration) { + var def = node.definition(); + return !same_scope(def) || may_overlap(compressor, def); + } + }, true)) { + self.init = to_var(self.init, self.resolve()); + } else if (self.init.can_letify(compressor, true)) { + self.init = to_let(self.init, self); + } + } + return self; + }); + + function mark_locally_defined(condition, consequent, alternative) { + if (condition instanceof AST_Sequence) condition = condition.tail_node(); + if (!(condition instanceof AST_Binary)) return; + if (!(condition.left instanceof AST_String)) { + switch (condition.operator) { + case "&&": + mark_locally_defined(condition.left, consequent); + mark_locally_defined(condition.right, consequent); + break; + case "||": + mark_locally_defined(negate(condition.left), alternative); + mark_locally_defined(negate(condition.right), alternative); + break; + } + return; + } + if (!(condition.right instanceof AST_UnaryPrefix)) return; + if (condition.right.operator != "typeof") return; + var sym = condition.right.expression; + if (!is_undeclared_ref(sym)) return; + var body; + var undef = condition.left.value == "undefined"; + switch (condition.operator) { + case "==": + body = undef ? alternative : consequent; + break; + case "!=": + body = undef ? consequent : alternative; + break; + default: + return; + } + if (!body) return; + var abort = false; + var def = sym.definition(); + var fn; + var refs = []; + var scanned = []; + var tw = new TreeWalker(function(node, descend) { + if (abort) return true; + if (node instanceof AST_Assign) { + var ref = node.left; + if (!(ref instanceof AST_SymbolRef && ref.definition() === def)) return; + node.right.walk(tw); + switch (node.operator) { + case "=": + case "&&=": + abort = true; + } + return true; + } + if (node instanceof AST_Call) { + descend(); + fn = node.expression.tail_node(); + var save; + if (fn instanceof AST_SymbolRef) { + fn = fn.fixed_value(); + save = refs.length; + } + if (!(fn instanceof AST_Lambda)) { + abort = true; + } else if (push_uniq(scanned, fn)) { + fn.walk(tw); + } + if (save >= 0) refs.length = save; + return true; + } + if (node instanceof AST_DWLoop) { + var save = refs.length; + descend(); + if (abort) refs.length = save; + return true; + } + if (node instanceof AST_For) { + if (node.init) node.init.walk(tw); + var save = refs.length; + if (node.condition) node.condition.walk(tw); + node.body.walk(tw); + if (node.step) node.step.walk(tw); + if (abort) refs.length = save; + return true; + } + if (node instanceof AST_ForEnumeration) { + node.object.walk(tw); + var save = refs.length; + node.init.walk(tw); + node.body.walk(tw); + if (abort) refs.length = save; + return true; + } + if (node instanceof AST_Scope) { + if (node === fn) return; + return true; + } + if (node instanceof AST_SymbolRef) { + if (node.definition() === def) refs.push(node); + return true; + } + }); + body.walk(tw); + refs.forEach(function(ref) { + ref.defined = true; + }); + + function negate(node) { + if (!(node instanceof AST_Binary)) return; + switch (node.operator) { + case "==": + node = node.clone(); + node.operator = "!="; + return node; + case "!=": + node = node.clone(); + node.operator = "=="; + return node; + } + } + } + + function fuzzy_eval(compressor, node, nullish) { + if (node.truthy) return true; + if (is_undefined(node)) return undefined; + if (node.falsy && !nullish) return false; + if (node.is_truthy()) return true; + return node.evaluate(compressor, true); + } + + function mark_duplicate_condition(compressor, node) { + var child; + var level = 0; + var negated = false; + var parent = compressor.self(); + if (!is_statement(parent)) while (true) { + child = parent; + parent = compressor.parent(level++); + if (parent instanceof AST_Binary) { + switch (child) { + case parent.left: + if (lazy_op[parent.operator]) continue; + break; + case parent.right: + if (match(parent.left)) switch (parent.operator) { + case "&&": + node[negated ? "falsy" : "truthy"] = true; + break; + case "||": + case "??": + node[negated ? "truthy" : "falsy"] = true; + break; + } + break; + } + } else if (parent instanceof AST_Conditional) { + var cond = parent.condition; + if (cond === child) continue; + if (match(cond)) switch (child) { + case parent.consequent: + node[negated ? "falsy" : "truthy"] = true; + break; + case parent.alternative: + node[negated ? "truthy" : "falsy"] = true; + break; + } + } else if (parent instanceof AST_Exit) { + break; + } else if (parent instanceof AST_If) { + break; + } else if (parent instanceof AST_Sequence) { + if (parent.expressions[0] === child) continue; + } else if (parent instanceof AST_SimpleStatement) { + break; + } + return; + } + while (true) { + child = parent; + parent = compressor.parent(level++); + if (parent instanceof AST_BlockStatement) { + if (parent.body[0] === child) continue; + } else if (parent instanceof AST_If) { + if (match(parent.condition)) switch (child) { + case parent.body: + node[negated ? "falsy" : "truthy"] = true; + break; + case parent.alternative: + node[negated ? "truthy" : "falsy"] = true; + break; + } + } + return; + } + + function match(cond) { + if (node.equals(cond)) return true; + if (!(cond instanceof AST_UnaryPrefix)) return false; + if (cond.operator != "!") return false; + if (!node.equals(cond.expression)) return false; + negated = true; + return true; + } + } + + OPT(AST_If, function(self, compressor) { + if (is_empty(self.alternative)) self.alternative = null; + + if (!compressor.option("conditionals")) return self; + if (compressor.option("booleans") && !self.condition.has_side_effects(compressor)) { + mark_duplicate_condition(compressor, self.condition); + } + // if condition can be statically determined, warn and drop + // one of the blocks. note, statically determined implies + // “has no side effects”; also it doesn't work for cases like + // `x && true`, though it probably should. + if (compressor.option("dead_code")) { + var cond = fuzzy_eval(compressor, self.condition); + if (!cond) { + AST_Node.warn("Condition always false [{start}]", self.condition); + var body = [ + make_node(AST_SimpleStatement, self.condition, { body: self.condition }).transform(compressor), + ]; + extract_declarations_from_unreachable_code(compressor, self.body, body); + if (self.alternative) body.push(self.alternative); + return make_node(AST_BlockStatement, self, { body: body }).optimize(compressor); + } else if (!(cond instanceof AST_Node)) { + AST_Node.warn("Condition always true [{start}]", self.condition); + var body = [ + make_node(AST_SimpleStatement, self.condition, { body: self.condition }).transform(compressor), + self.body, + ]; + if (self.alternative) extract_declarations_from_unreachable_code(compressor, self.alternative, body); + return make_node(AST_BlockStatement, self, { body: body }).optimize(compressor); + } + } + var negated = self.condition.negate(compressor); + var self_condition_length = self.condition.print_to_string().length; + var negated_length = negated.print_to_string().length; + var negated_is_best = negated_length < self_condition_length; + if (self.alternative && negated_is_best) { + negated_is_best = false; // because we already do the switch here. + // no need to swap values of self_condition_length and negated_length + // here because they are only used in an equality comparison later on. + self.condition = negated; + var tmp = self.body; + self.body = self.alternative; + self.alternative = is_empty(tmp) ? null : tmp; + } + var body_defuns = []; + var body_var_defs = []; + var body_refs = []; + var body_exprs = sequencesize(self.body, body_defuns, body_var_defs, body_refs); + var alt_defuns = []; + var alt_var_defs = []; + var alt_refs = []; + var alt_exprs = sequencesize(self.alternative, alt_defuns, alt_var_defs, alt_refs); + if (body_exprs instanceof AST_BlockStatement || alt_exprs instanceof AST_BlockStatement) { + var body = [], var_defs = []; + if (body_exprs) { + [].push.apply(body, body_defuns); + [].push.apply(var_defs, body_var_defs); + if (body_exprs instanceof AST_BlockStatement) { + self.body = body_exprs; + } else if (body_exprs.length == 0) { + self.body = make_node(AST_EmptyStatement, self.body); + } else { + self.body = make_node(AST_SimpleStatement, self.body, { + body: make_sequence(self.body, body_exprs), + }); + } + body_refs.forEach(process_to_assign); + } + if (alt_exprs) { + [].push.apply(body, alt_defuns); + [].push.apply(var_defs, alt_var_defs); + if (alt_exprs instanceof AST_BlockStatement) { + self.alternative = alt_exprs; + } else if (alt_exprs.length == 0) { + self.alternative = null; + } else { + self.alternative = make_node(AST_SimpleStatement, self.alternative, { + body: make_sequence(self.alternative, alt_exprs), + }); + } + alt_refs.forEach(process_to_assign); + } + if (var_defs.length > 0) body.push(make_node(AST_Var, self, { definitions: var_defs })); + if (body.length > 0) { + body.push(self.transform(compressor)); + return make_node(AST_BlockStatement, self, { body: body }).optimize(compressor); + } + } else if (body_exprs && alt_exprs) { + var body = body_defuns.concat(alt_defuns); + if (body_var_defs.length > 0 || alt_var_defs.length > 0) body.push(make_node(AST_Var, self, { + definitions: body_var_defs.concat(alt_var_defs), + })); + if (body_exprs.length == 0) { + body.push(make_node(AST_SimpleStatement, self.condition, { + body: alt_exprs.length > 0 ? make_node(AST_Binary, self, { + operator: "||", + left: self.condition, + right: make_sequence(self.alternative, alt_exprs), + }).transform(compressor) : self.condition.clone(), + }).optimize(compressor)); + } else if (alt_exprs.length == 0) { + if (self_condition_length === negated_length && !negated_is_best + && self.condition instanceof AST_Binary && self.condition.operator == "||") { + // although the code length of self.condition and negated are the same, + // negated does not require additional surrounding parentheses. + // see https://github.com/mishoo/UglifyJS/issues/979 + negated_is_best = true; + } + body.push(make_node(AST_SimpleStatement, self, { + body: make_node(AST_Binary, self, { + operator: negated_is_best ? "||" : "&&", + left: negated_is_best ? negated : self.condition, + right: make_sequence(self.body, body_exprs), + }).transform(compressor), + }).optimize(compressor)); + } else { + body.push(make_node(AST_SimpleStatement, self, { + body: make_node(AST_Conditional, self, { + condition: self.condition, + consequent: make_sequence(self.body, body_exprs), + alternative: make_sequence(self.alternative, alt_exprs), + }), + }).optimize(compressor)); + } + body_refs.forEach(process_to_assign); + alt_refs.forEach(process_to_assign); + return make_node(AST_BlockStatement, self, { body: body }).optimize(compressor); + } + if (is_empty(self.body)) self = make_node(AST_If, self, { + condition: negated, + body: self.alternative, + alternative: null, + }); + if (self.alternative instanceof AST_Exit && self.body.TYPE == self.alternative.TYPE) { + var cons_value = self.body.value; + var alt_value = self.alternative.value; + if (!cons_value && !alt_value) return make_node(AST_BlockStatement, self, { + body: [ + make_node(AST_SimpleStatement, self, { body: self.condition }), + self.body, + ], + }).optimize(compressor); + if (cons_value && alt_value || !keep_return_void()) { + var exit = make_node(self.body.CTOR, self, { + value: make_node(AST_Conditional, self, { + condition: self.condition, + consequent: cons_value || make_node(AST_Undefined, self.body).transform(compressor), + alternative: alt_value || make_node(AST_Undefined, self.alternative).transform(compressor), + }), + }); + if (exit instanceof AST_Return) exit.in_bool = self.body.in_bool || self.alternative.in_bool; + return exit; + } + } + if (self.body instanceof AST_If && !self.body.alternative && !self.alternative) { + self = make_node(AST_If, self, { + condition: make_node(AST_Binary, self.condition, { + operator: "&&", + left: self.condition, + right: self.body.condition, + }), + body: self.body.body, + alternative: null, + }); + } + if (aborts(self.body) && self.alternative) { + var alt = self.alternative; + self.alternative = null; + return make_node(AST_BlockStatement, self, { body: [ self, alt ] }).optimize(compressor); + } + if (aborts(self.alternative)) { + var body = self.body; + self.body = self.alternative; + self.condition = negated_is_best ? negated : self.condition.negate(compressor); + self.alternative = null; + return make_node(AST_BlockStatement, self, { body: [ self, body ] }).optimize(compressor); + } + if (self.alternative) { + var body_stats = as_array(self.body); + var body_index = last_index(body_stats); + var alt_stats = as_array(self.alternative); + var alt_index = last_index(alt_stats); + for (var stats = []; body_index >= 0 && alt_index >= 0;) { + var stat = body_stats[body_index]; + var alt_stat = alt_stats[alt_index]; + if (stat.equals(alt_stat)) { + body_stats.splice(body_index--, 1); + alt_stats.splice(alt_index--, 1); + stats.unshift(merge_expression(stat, alt_stat)); + } else { + if (!(stat instanceof AST_SimpleStatement)) break; + if (!(alt_stat instanceof AST_SimpleStatement)) break; + var expr1 = stat.body.tail_node(); + var expr2 = alt_stat.body.tail_node(); + if (!expr1.equals(expr2)) break; + body_index = pop_expr(body_stats, stat.body, body_index); + alt_index = pop_expr(alt_stats, alt_stat.body, alt_index); + stats.unshift(make_node(AST_SimpleStatement, expr1, { body: merge_expression(expr1, expr2) })); + } + } + if (stats.length > 0) { + self.body = body_stats.length > 0 ? make_node(AST_BlockStatement, self, { + body: body_stats, + }) : make_node(AST_EmptyStatement, self); + self.alternative = alt_stats.length > 0 ? make_node(AST_BlockStatement, self, { + body: alt_stats, + }) : null; + stats.unshift(self); + return make_node(AST_BlockStatement, self, { body: stats }).optimize(compressor); + } + } + if (compressor.option("typeofs")) mark_locally_defined(self.condition, self.body, self.alternative); + return self; + + function as_array(node) { + return node instanceof AST_BlockStatement ? node.body : [ node ]; + } + + function keep_return_void() { + var has_finally = false, level = 0, node = compressor.self(); + do { + if (node instanceof AST_Catch) { + if (compressor.parent(level).bfinally) has_finally = true; + level++; + } else if (node instanceof AST_Finally) { + level++; + } else if (node instanceof AST_Scope) { + return has_finally && in_async_generator(node); + } else if (node instanceof AST_Try) { + if (node.bfinally) has_finally = true; + } + } while (node = compressor.parent(level++)); + } + + function last_index(stats) { + for (var index = stats.length; --index >= 0;) { + if (!is_declaration(stats[index], true)) break; + } + return index; + } + + function pop_expr(stats, body, index) { + if (body instanceof AST_Sequence) { + stats[index] = make_node(AST_SimpleStatement, body, { + body: make_sequence(body, body.expressions.slice(0, -1)), + }); + } else { + stats.splice(index--, 1); + } + return index; + } + + function sequencesize(stat, defuns, var_defs, refs) { + if (stat == null) return []; + if (stat instanceof AST_BlockStatement) { + var exprs = []; + for (var i = 0; i < stat.body.length; i++) { + var line = stat.body[i]; + if (line instanceof AST_EmptyStatement) continue; + if (line instanceof AST_Exit) { + if (i == 0) return; + if (exprs.length > 0) { + line = line.clone(); + exprs.push(line.value || make_node(AST_Undefined, line).transform(compressor)); + line.value = make_sequence(stat, exprs); + } + var block = stat.clone(); + block.body = block.body.slice(i + 1); + block.body.unshift(line); + return block; + } + if (line instanceof AST_LambdaDefinition) { + defuns.push(line); + } else if (line instanceof AST_SimpleStatement) { + if (!compressor.option("sequences") && exprs.length > 0) return; + exprs.push(line.body); + } else if (line instanceof AST_Var) { + if (!compressor.option("sequences") && exprs.length > 0) return; + line.remove_initializers(compressor, var_defs); + line.definitions.forEach(process_var_def); + } else { + return; + } + } + return exprs; + } + if (stat instanceof AST_LambdaDefinition) { + defuns.push(stat); + return []; + } + if (stat instanceof AST_EmptyStatement) return []; + if (stat instanceof AST_SimpleStatement) return [ stat.body ]; + if (stat instanceof AST_Var) { + var exprs = []; + stat.remove_initializers(compressor, var_defs); + stat.definitions.forEach(process_var_def); + return exprs; + } + + function process_var_def(var_def) { + if (!var_def.value) return; + exprs.push(make_node(AST_Assign, var_def, { + operator: "=", + left: var_def.name.convert_symbol(AST_SymbolRef, function(ref) { + refs.push(ref); + }), + right: var_def.value, + })); + } + } + }); + + OPT(AST_Switch, function(self, compressor) { + if (!compressor.option("switches")) return self; + if (!compressor.option("dead_code")) return self; + var body = []; + var branch; + var decl = []; + var default_branch; + var exact_match; + var side_effects = []; + for (var i = 0, len = self.body.length; i < len; i++) { + branch = self.body[i]; + if (branch instanceof AST_Default) { + var prev = body[body.length - 1]; + if (default_branch || is_break(branch.body[0], compressor) && (!prev || aborts(prev))) { + eliminate_branch(branch, prev); + continue; + } else { + default_branch = branch; + } + } else { + var exp = branch.expression; + var equals = make_node(AST_Binary, self, { + operator: "===", + left: self.expression, + right: exp, + }).evaluate(compressor, true); + if (!equals) { + if (exp.has_side_effects(compressor)) side_effects.push(exp); + eliminate_branch(branch, body[body.length - 1]); + continue; + } + if (!(equals instanceof AST_Node)) { + if (default_branch) { + var default_index = body.indexOf(default_branch); + body.splice(default_index, 1); + eliminate_branch(default_branch, body[default_index - 1]); + default_branch = null; + } + if (exp.has_side_effects(compressor)) { + exact_match = branch; + } else { + default_branch = branch = make_node(AST_Default, branch); + } + while (++i < len) eliminate_branch(self.body[i], branch); + } + } + if (i + 1 >= len || aborts(branch)) { + var prev = body[body.length - 1]; + var statements = branch.body; + if (aborts(prev)) switch (prev.body.length - statements.length) { + case 1: + var stat = prev.body[prev.body.length - 1]; + if (!is_break(stat, compressor)) break; + statements = statements.concat(stat); + case 0: + var prev_block = make_node(AST_BlockStatement, prev); + var next_block = make_node(AST_BlockStatement, branch, { body: statements }); + if (prev_block.equals(next_block)) prev.body = []; + } + } + if (side_effects.length) { + if (branch instanceof AST_Default) { + body.push(make_node(AST_Case, self, { expression: make_sequence(self, side_effects), body: [] })); + } else { + side_effects.push(branch.expression); + branch.expression = make_sequence(self, side_effects); + } + side_effects = []; + } + body.push(branch); + } + if (side_effects.length && !exact_match) { + body.push(make_node(AST_Case, self, { expression: make_sequence(self, side_effects), body: [] })); + } + while (branch = body[body.length - 1]) { + var stat = branch.body[branch.body.length - 1]; + if (is_break(stat, compressor)) branch.body.pop(); + if (branch === default_branch) { + if (!has_declarations_only(branch)) break; + } else if (branch.expression.has_side_effects(compressor)) { + break; + } else if (default_branch) { + if (!has_declarations_only(default_branch)) break; + if (body[body.length - 2] !== default_branch) break; + default_branch.body = default_branch.body.concat(branch.body); + branch.body = []; + } else if (!has_declarations_only(branch)) break; + eliminate_branch(branch); + if (body.pop() === default_branch) default_branch = null; + } + if (!branch) { + decl.push(make_node(AST_SimpleStatement, self.expression, { body: self.expression })); + if (side_effects.length) decl.push(make_node(AST_SimpleStatement, self, { + body: make_sequence(self, side_effects), + })); + return make_node(AST_BlockStatement, self, { body: decl }).optimize(compressor); + } + if (branch === default_branch) while (branch = body[body.length - 2]) { + if (branch instanceof AST_Default) break; + if (!has_declarations_only(branch)) break; + var exp = branch.expression; + if (exp.has_side_effects(compressor)) { + var prev = body[body.length - 3]; + if (prev && !aborts(prev)) break; + default_branch.body.unshift(make_node(AST_SimpleStatement, self, { body: exp })); + } + eliminate_branch(branch); + body.splice(-2, 1); + } + body[0].body = decl.concat(body[0].body); + self.body = body; + if (compressor.option("conditionals")) switch (body.length) { + case 1: + if (!no_break(body[0])) break; + var exp = body[0].expression; + var statements = body[0].body.slice(); + if (body[0] !== default_branch && body[0] !== exact_match) return make_node(AST_If, self, { + condition: make_node(AST_Binary, self, { + operator: "===", + left: self.expression, + right: exp, + }), + body: make_node(AST_BlockStatement, self, { body: statements }), + alternative: null, + }).optimize(compressor); + if (exp) statements.unshift(make_node(AST_SimpleStatement, exp, { body: exp })); + statements.unshift(make_node(AST_SimpleStatement, self.expression, { body: self.expression })); + return make_node(AST_BlockStatement, self, { body: statements }).optimize(compressor); + case 2: + if (!member(default_branch, body) || !no_break(body[1])) break; + var statements = body[0].body.slice(); + var exclusive = statements.length && is_break(statements[statements.length - 1], compressor); + if (exclusive) statements.pop(); + if (!all(statements, no_break)) break; + var alternative = body[1].body.length && make_node(AST_BlockStatement, body[1]); + var node = make_node(AST_If, self, { + condition: make_node(AST_Binary, self, body[0] === default_branch ? { + operator: "!==", + left: self.expression, + right: body[1].expression, + } : { + operator: "===", + left: self.expression, + right: body[0].expression, + }), + body: make_node(AST_BlockStatement, body[0], { body: statements }), + alternative: exclusive && alternative || null, + }); + if (!exclusive && alternative) node = make_node(AST_BlockStatement, self, { body: [ node, alternative ] }); + return node.optimize(compressor); + } + return self; + + function is_break(node, tw) { + return node instanceof AST_Break && tw.loopcontrol_target(node) === self; + } + + function no_break(node) { + var found = false; + var tw = new TreeWalker(function(node) { + if (found + || node instanceof AST_Lambda + || node instanceof AST_SimpleStatement) return true; + if (is_break(node, tw)) found = true; + }); + tw.push(self); + node.walk(tw); + return !found; + } + + function eliminate_branch(branch, prev) { + if (prev && !aborts(prev)) { + prev.body = prev.body.concat(branch.body); + } else { + extract_declarations_from_unreachable_code(compressor, branch, decl); + } + } + }); + + OPT(AST_Try, function(self, compressor) { + self.body = tighten_body(self.body, compressor); + if (compressor.option("dead_code")) { + if (has_declarations_only(self) + && !(self.bcatch && self.bcatch.argname && self.bcatch.argname.match_symbol(function(node) { + return node instanceof AST_SymbolCatch && !can_drop_symbol(node); + }, true))) { + var body = []; + if (self.bcatch) { + extract_declarations_from_unreachable_code(compressor, self.bcatch, body); + body.forEach(function(stat) { + if (!(stat instanceof AST_Var)) return; + stat.definitions.forEach(function(var_def) { + var def = var_def.name.definition().redefined(); + if (!def) return; + var_def.name = var_def.name.clone(); + var_def.name.thedef = def; + }); + }); + } + body.unshift(make_node(AST_BlockStatement, self).optimize(compressor)); + if (self.bfinally) { + body.push(make_node(AST_BlockStatement, self.bfinally).optimize(compressor)); + } + return make_node(AST_BlockStatement, self, { body: body }).optimize(compressor); + } + if (self.bfinally && has_declarations_only(self.bfinally)) { + var body = make_node(AST_BlockStatement, self.bfinally).optimize(compressor); + body = self.body.concat(body); + if (!self.bcatch) return make_node(AST_BlockStatement, self, { body: body }).optimize(compressor); + self.body = body; + self.bfinally = null; + } + } + return self; + }); + + function remove_initializers(make_value) { + return function(compressor, defns) { + var dropped = false; + this.definitions.forEach(function(defn) { + if (defn.value) dropped = true; + defn.name.match_symbol(function(node) { + if (node instanceof AST_SymbolDeclaration) defns.push(make_node(AST_VarDef, node, { + name: node, + value: make_value(compressor, node), + })); + }, true); + }); + return dropped; + }; + } + + AST_Const.DEFMETHOD("remove_initializers", remove_initializers(function(compressor, node) { + return make_node(AST_Undefined, node).optimize(compressor); + })); + AST_Let.DEFMETHOD("remove_initializers", remove_initializers(return_null)); + AST_Var.DEFMETHOD("remove_initializers", remove_initializers(return_null)); + + AST_Definitions.DEFMETHOD("to_assignments", function() { + var assignments = this.definitions.reduce(function(a, defn) { + var def = defn.name.definition(); + var value = defn.value; + if (value) { + if (value instanceof AST_Sequence) value = value.clone(); + var name = make_node(AST_SymbolRef, defn.name); + var assign = make_node(AST_Assign, defn, { + operator: "=", + left: name, + right: value, + }); + a.push(assign); + var fixed = function() { + return assign.right; + }; + fixed.assigns = [ assign ]; + fixed.direct_access = def.direct_access; + fixed.escaped = def.escaped; + name.fixed = fixed; + def.references.forEach(function(ref) { + if (!ref.fixed) return; + var assigns = ref.fixed.assigns; + if (!assigns) return; + if (assigns[0] !== defn) return; + if (assigns.length > 1 || ref.fixed.to_binary || ref.fixed.to_prefix) { + assigns[0] = assign; + } else { + ref.fixed = fixed; + if (def.fixed === ref.fixed) def.fixed = fixed; + } + }); + def.references.push(name); + } + def.assignments++; + def.eliminated++; + def.single_use = false; + return a; + }, []); + if (assignments.length == 0) return null; + return make_sequence(this, assignments); + }); + + function is_safe_lexical(def) { + return def.name != "arguments" && def.orig.length < (def.orig[0] instanceof AST_SymbolLambda ? 3 : 2); + } + + function may_overlap(compressor, def) { + if (compressor.exposed(def)) return true; + var scope = def.scope.resolve(); + for (var s = def.scope; s !== scope;) { + s = s.parent_scope; + if (s.var_names().has(def.name)) return true; + } + } + + function to_let(stat, scope) { + return make_node(AST_Let, stat, { + definitions: stat.definitions.map(function(defn) { + return make_node(AST_VarDef, defn, { + name: defn.name.convert_symbol(AST_SymbolLet, function(name, node) { + var def = name.definition(); + def.orig[def.orig.indexOf(node)] = name; + for (var s = scope; s !== def.scope && (s = s.parent_scope);) { + remove(s.enclosed, def); + } + def.scope = scope; + scope.variables.set(def.name, def); + }), + value: defn.value, + }); + }), + }); + } + + function to_var(stat, scope) { + return make_node(AST_Var, stat, { + definitions: stat.definitions.map(function(defn) { + return make_node(AST_VarDef, defn, { + name: defn.name.convert_symbol(AST_SymbolVar, function(name, node) { + var def = name.definition(); + def.orig[def.orig.indexOf(node)] = name; + if (def.scope === scope) return; + def.scope = scope; + scope.variables.set(def.name, def); + scope.enclosed.push(def); + scope.var_names().set(def.name, true); + }), + value: defn.value, + }); + }), + }); + } + + (function(def) { + def(AST_Node, return_false); + def(AST_Const, function(compressor, assigned) { + assigned = assigned ? 1 : 0; + var defns = this.definitions; + if (!compressor.option("module") && all(defns, function(defn) { + return defn.name instanceof AST_SymbolConst; + })) return false; + return all(defns, function(defn) { + return !defn.name.match_symbol(function(node) { + if (node instanceof AST_SymbolDeclaration) return node.definition().assignments != assigned; + }, true); + }); + }); + def(AST_Var, function(compressor) { + return all(this.definitions, function(defn) { + return !defn.name.match_symbol(function(node) { + if (!(node instanceof AST_SymbolDeclaration)) return false; + var def = node.definition(); + if (def.first_decl !== node) return true; + if (!safe_from_tdz(compressor, node)) return true; + var defn_scope = node.scope; + if (defn_scope instanceof AST_Scope) return false; + return !all(def.references, function(ref) { + var scope = ref.scope; + do { + if (scope === defn_scope) return true; + } while (scope = scope.parent_scope); + return false; + }); + }, true); + }); + }); + })(function(node, func) { + node.DEFMETHOD("can_letify", func); + }); + + function safe_from_tdz(compressor, sym) { + var def = sym.definition(); + return (def.fixed || def.fixed === 0) + && is_safe_lexical(def) + && same_scope(def) + && !may_overlap(compressor, def); + } + + AST_Definitions.DEFMETHOD("can_varify", function(compressor) { + return all(this.definitions, function(defn) { + return !defn.name.match_symbol(function(node) { + if (node instanceof AST_SymbolDeclaration) return !safe_from_tdz(compressor, node); + }, true); + }); + }); + + OPT(AST_Const, function(self, compressor) { + if (!compressor.option("varify")) return self; + if (self.can_varify(compressor)) return to_var(self, compressor.find_parent(AST_Scope)); + if (self.can_letify(compressor)) return to_let(self, find_scope(compressor)); + return self; + }); + + OPT(AST_Let, function(self, compressor) { + if (!compressor.option("varify")) return self; + if (self.can_varify(compressor)) return to_var(self, compressor.find_parent(AST_Scope)); + return self; + }); + + function trim_optional_chain(node, compressor) { + if (!compressor.option("optional_chains")) return; + if (node.terminal) do { + var expr = node.expression; + if (node.optional) { + var ev = fuzzy_eval(compressor, expr, true); + if (ev == null) return make_node(AST_UnaryPrefix, node, { + operator: "void", + expression: expr, + }).optimize(compressor); + if (!(ev instanceof AST_Node)) node.optional = false; + } + node = expr; + } while ((node.TYPE == "Call" || node instanceof AST_PropAccess) && !node.terminal); + } + + function lift_sequence_in_expression(node, compressor) { + var exp = node.expression; + if (!(exp instanceof AST_Sequence)) return node; + var x = exp.expressions.slice(); + var e = node.clone(); + e.expression = x.pop(); + x.push(e); + return make_sequence(node, x); + } + + function drop_unused_call_args(call, compressor, fns_with_marked_args) { + var exp = call.expression; + var fn = exp instanceof AST_SymbolRef ? exp.fixed_value() : exp; + if (!(fn instanceof AST_Lambda)) return; + if (fn.uses_arguments) return; + if (fn.pinned()) return; + if (fns_with_marked_args && fns_with_marked_args.indexOf(fn) < 0) return; + var args = call.args; + if (!all(args, function(arg) { + return !(arg instanceof AST_Spread); + })) return; + var argnames = fn.argnames; + var is_iife = fn === exp && !fn.name; + if (fn.rest) { + if (!(is_iife && compressor.option("rests"))) return; + var insert = argnames.length; + args = args.slice(0, insert); + while (args.length < insert) args.push(make_node(AST_Undefined, call).optimize(compressor)); + args.push(make_node(AST_Array, call, { elements: call.args.slice(insert) })); + argnames = argnames.concat(fn.rest); + fn.rest = null; + } else { + args = args.slice(); + argnames = argnames.slice(); + } + var pos = 0, last = 0; + var drop_defaults = is_iife && compressor.option("default_values"); + var drop_fargs = is_iife && compressor.drop_fargs(fn, call) ? function(argname, arg) { + if (!argname) return true; + if (argname instanceof AST_DestructuredArray) { + return argname.elements.length == 0 && !argname.rest && arg instanceof AST_Array; + } + if (argname instanceof AST_DestructuredObject) { + return argname.properties.length == 0 && !argname.rest && arg && !arg.may_throw_on_access(compressor); + } + return argname.unused; + } : return_false; + var side_effects = []; + for (var i = 0; i < args.length; i++) { + var argname = argnames[i]; + if (drop_defaults && argname instanceof AST_DefaultValue && args[i].is_defined(compressor)) { + argnames[i] = argname = argname.name; + } + if (!argname || argname.unused !== undefined) { + var node = args[i].drop_side_effect_free(compressor); + if (drop_fargs(argname)) { + if (argname) argnames.splice(i, 1); + args.splice(i, 1); + if (node) side_effects.push(node); + i--; + continue; + } else if (node) { + side_effects.push(node); + args[pos++] = make_sequence(call, side_effects); + side_effects = []; + } else if (argname) { + if (side_effects.length) { + args[pos++] = make_sequence(call, side_effects); + side_effects = []; + } else { + args[pos++] = make_node(AST_Number, args[i], { value: 0 }); + continue; + } + } + } else if (drop_fargs(argname, args[i])) { + var node = args[i].drop_side_effect_free(compressor); + argnames.splice(i, 1); + args.splice(i, 1); + if (node) side_effects.push(node); + i--; + continue; + } else { + side_effects.push(args[i]); + args[pos++] = make_sequence(call, side_effects); + side_effects = []; + } + last = pos; + } + for (; i < argnames.length; i++) { + if (drop_fargs(argnames[i])) argnames.splice(i--, 1); + } + fn.argnames = argnames; + args.length = last; + call.args = args; + if (!side_effects.length) return; + var arg = make_sequence(call, side_effects); + args.push(args.length < argnames.length ? make_node(AST_UnaryPrefix, call, { + operator: "void", + expression: arg, + }) : arg); + } + + function avoid_await_yield(compressor, parent_scope) { + if (!parent_scope) parent_scope = compressor.find_parent(AST_Scope); + var avoid = []; + if (is_async(parent_scope) || parent_scope instanceof AST_Toplevel && compressor.option("module")) { + avoid.push("await"); + } + if (is_generator(parent_scope)) avoid.push("yield"); + return avoid.length && makePredicate(avoid); + } + + function safe_from_await_yield(fn, avoid) { + if (!avoid) return true; + var safe = true; + var tw = new TreeWalker(function(node) { + if (!safe) return true; + if (node instanceof AST_Scope) { + if (node === fn) return; + if (is_arrow(node)) { + for (var i = 0; safe && i < node.argnames.length; i++) node.argnames[i].walk(tw); + } else if (node instanceof AST_LambdaDefinition && avoid[node.name.name]) { + safe = false; + } + return true; + } + if (node instanceof AST_Symbol && avoid[node.name] && node !== fn.name) safe = false; + }); + fn.walk(tw); + return safe; + } + + function safe_from_strict_mode(fn, compressor) { + return fn.in_strict_mode(compressor) || !compressor.has_directive("use strict"); + } + + OPT(AST_Call, function(self, compressor) { + var exp = self.expression; + var terminated = trim_optional_chain(self, compressor); + if (terminated) return terminated; + if (compressor.option("sequences")) { + if (exp instanceof AST_PropAccess) { + var seq = lift_sequence_in_expression(exp, compressor); + if (seq !== exp) { + var call = self.clone(); + call.expression = seq.expressions.pop(); + seq.expressions.push(call); + return seq.optimize(compressor); + } + } else if (!needs_unbinding(exp.tail_node())) { + var seq = lift_sequence_in_expression(self, compressor); + if (seq !== self) return seq.optimize(compressor); + } + } + if (compressor.option("unused")) drop_unused_call_args(self, compressor); + if (compressor.option("unsafe")) { + if (is_undeclared_ref(exp)) switch (exp.name) { + case "Array": + // Array(n) ---> [ , , ... , ] + if (self.args.length == 1) { + var first = self.args[0]; + if (first instanceof AST_Number) try { + var length = first.value; + if (length > 6) break; + var elements = Array(length); + for (var i = 0; i < length; i++) elements[i] = make_node(AST_Hole, self); + return make_node(AST_Array, self, { elements: elements }); + } catch (ex) { + AST_Node.warn("Invalid array length: {length} [{start}]", { + length: length, + start: self.start, + }); + break; + } + if (!first.is_boolean(compressor) && !first.is_string(compressor)) break; + } + // Array(...) ---> [ ... ] + return make_node(AST_Array, self, { elements: self.args }); + case "Object": + // Object() ---> {} + if (self.args.length == 0) return make_node(AST_Object, self, { properties: [] }); + break; + case "String": + // String() ---> "" + if (self.args.length == 0) return make_node(AST_String, self, { value: "" }); + // String(x) ---> "" + x + if (self.args.length == 1) return make_node(AST_Binary, self, { + operator: "+", + left: make_node(AST_String, self, { value: "" }), + right: self.args[0], + }).optimize(compressor); + break; + case "Number": + // Number() ---> 0 + if (self.args.length == 0) return make_node(AST_Number, self, { value: 0 }); + // Number(x) ---> +("" + x) + if (self.args.length == 1) return make_node(AST_UnaryPrefix, self, { + operator: "+", + expression: make_node(AST_Binary, self, { + operator: "+", + left: make_node(AST_String, self, { value: "" }), + right: self.args[0], + }), + }).optimize(compressor); + break; + case "Boolean": + // Boolean() ---> false + if (self.args.length == 0) return make_node(AST_False, self).optimize(compressor); + // Boolean(x) ---> !!x + if (self.args.length == 1) return make_node(AST_UnaryPrefix, self, { + operator: "!", + expression: make_node(AST_UnaryPrefix, self, { + operator: "!", + expression: self.args[0], + }), + }).optimize(compressor); + break; + case "RegExp": + // attempt to convert RegExp(...) to literal + var params = []; + if (all(self.args, function(arg) { + var value = arg.evaluate(compressor); + params.unshift(value); + return arg !== value; + })) try { + return best_of(compressor, self, make_node(AST_RegExp, self, { + value: RegExp.apply(RegExp, params), + })); + } catch (ex) { + AST_Node.warn("Error converting {this} [{start}]", self); + } + break; + } else if (exp instanceof AST_Dot) switch (exp.property) { + case "toString": + // x.toString() ---> "" + x + var expr = exp.expression; + if (self.args.length == 0 && !(expr.may_throw_on_access(compressor) || expr instanceof AST_Super)) { + return make_node(AST_Binary, self, { + operator: "+", + left: make_node(AST_String, self, { value: "" }), + right: expr, + }).optimize(compressor); + } + break; + case "join": + if (exp.expression instanceof AST_Array && self.args.length < 2) EXIT: { + var separator = self.args[0]; + // [].join() ---> "" + // [].join(x) ---> (x, "") + if (exp.expression.elements.length == 0 && !(separator instanceof AST_Spread)) { + return separator ? make_sequence(self, [ + separator, + make_node(AST_String, self, { value: "" }), + ]).optimize(compressor) : make_node(AST_String, self, { value: "" }); + } + if (separator) { + separator = separator.evaluate(compressor); + if (separator instanceof AST_Node) break EXIT; // not a constant + } + var elements = []; + var consts = []; + for (var i = 0; i < exp.expression.elements.length; i++) { + var el = exp.expression.elements[i]; + var value = el.evaluate(compressor); + if (value !== el) { + consts.push(value); + } else if (el instanceof AST_Spread) { + break EXIT; + } else { + if (consts.length > 0) { + elements.push(make_node(AST_String, self, { value: consts.join(separator) })); + consts.length = 0; + } + elements.push(el); + } + } + if (consts.length > 0) elements.push(make_node(AST_String, self, { + value: consts.join(separator), + })); + // [ x ].join() ---> "" + x + // [ x ].join(".") ---> "" + x + // [ 1, 2, 3 ].join() ---> "1,2,3" + // [ 1, 2, 3 ].join(".") ---> "1.2.3" + if (elements.length == 1) { + if (elements[0].is_string(compressor)) return elements[0]; + return make_node(AST_Binary, elements[0], { + operator: "+", + left: make_node(AST_String, self, { value: "" }), + right: elements[0], + }); + } + // [ 1, 2, a, 3 ].join("") ---> "12" + a + "3" + if (separator == "") { + var first; + if (elements[0].is_string(compressor) || elements[1].is_string(compressor)) { + first = elements.shift(); + } else { + first = make_node(AST_String, self, { value: "" }); + } + return elements.reduce(function(prev, el) { + return make_node(AST_Binary, el, { + operator: "+", + left: prev, + right: el, + }); + }, first).optimize(compressor); + } + // [ x, "foo", "bar", y ].join() ---> [ x, "foo,bar", y ].join() + // [ x, "foo", "bar", y ].join("-") ---> [ x, "foo-bar", y ].join("-") + // need this awkward cloning to not affect original element + // best_of will decide which one to get through. + var node = self.clone(); + node.expression = node.expression.clone(); + node.expression.expression = node.expression.expression.clone(); + node.expression.expression.elements = elements; + return best_of(compressor, self, node); + } + break; + case "charAt": + if (self.args.length < 2) { + var node = make_node(AST_Binary, self, { + operator: "||", + left: make_node(AST_Sub, self, { + expression: exp.expression, + property: self.args.length ? make_node(AST_Binary, self.args[0], { + operator: "|", + left: make_node(AST_Number, self, { value: 0 }), + right: self.args[0], + }) : make_node(AST_Number, self, { value: 0 }), + }).optimize(compressor), + right: make_node(AST_String, self, { value: "" }), + }); + node.is_string = return_true; + return node.optimize(compressor); + } + break; + case "apply": + if (self.args.length == 2 && self.args[1] instanceof AST_Array) { + var args = self.args[1].elements.slice(); + args.unshift(self.args[0]); + return make_node(AST_Call, self, { + expression: make_node(AST_Dot, exp, { + expression: exp.expression, + property: "call", + }), + args: args, + }).optimize(compressor); + } + break; + case "call": + var func = exp.expression; + if (func instanceof AST_SymbolRef) { + func = func.fixed_value(); + } + if (func instanceof AST_Lambda && !func.contains_this()) { + return (self.args.length ? make_sequence(self, [ + self.args[0], + make_node(AST_Call, self, { + expression: exp.expression, + args: self.args.slice(1), + }), + ]) : make_node(AST_Call, self, { + expression: exp.expression, + args: [], + })).optimize(compressor); + } + break; + } else if (compressor.option("side_effects") + && exp instanceof AST_Call + && exp.args.length == 1 + && is_undeclared_ref(exp.expression) + && exp.expression.name == "Object") { + var call = self.clone(); + call.expression = maintain_this_binding(self, exp, exp.args[0]); + return call.optimize(compressor); + } + } + if (compressor.option("unsafe_Function") + && is_undeclared_ref(exp) + && exp.name == "Function") { + // new Function() ---> function(){} + if (self.args.length == 0) return make_node(AST_Function, self, { + argnames: [], + body: [], + }).init_vars(exp.scope); + if (all(self.args, function(x) { + return x instanceof AST_String; + })) { + // quite a corner-case, but we can handle it: + // https://github.com/mishoo/UglifyJS/issues/203 + // if the code argument is a constant, then we can minify it. + try { + var code = "n(function(" + self.args.slice(0, -1).map(function(arg) { + return arg.value; + }).join() + "){" + self.args[self.args.length - 1].value + "})"; + var ast = parse(code); + var mangle = { ie: compressor.option("ie") }; + ast.figure_out_scope(mangle); + var comp = new Compressor(compressor.options); + ast = ast.transform(comp); + ast.figure_out_scope(mangle); + ast.compute_char_frequency(mangle); + ast.mangle_names(mangle); + var fun; + ast.walk(new TreeWalker(function(node) { + if (fun) return true; + if (node instanceof AST_Lambda) { + fun = node; + return true; + } + })); + var code = OutputStream(); + AST_BlockStatement.prototype._codegen.call(fun, code); + self.args = [ + make_node(AST_String, self, { + value: fun.argnames.map(function(arg) { + return arg.print_to_string(); + }).join(), + }), + make_node(AST_String, self.args[self.args.length - 1], { + value: code.get().replace(/^\{|\}$/g, "") + }), + ]; + return self; + } catch (ex) { + if (ex instanceof JS_Parse_Error) { + AST_Node.warn("Error parsing code passed to new Function [{start}]", self.args[self.args.length - 1]); + AST_Node.warn(ex.toString()); + } else { + throw ex; + } + } + } + } + var fn = exp instanceof AST_SymbolRef ? exp.fixed_value() : exp; + var parent = compressor.parent(), current = compressor.self(); + var is_func = fn instanceof AST_Lambda + && (!is_async(fn) || compressor.option("awaits") && parent instanceof AST_Await) + && (!is_generator(fn) || compressor.option("yields") && current instanceof AST_Yield && current.nested); + var stat = is_func && fn.first_statement(); + var has_default = 0, has_destructured = false; + var has_spread = !all(self.args, function(arg) { + return !(arg instanceof AST_Spread); + }); + var can_drop = is_func && all(fn.argnames, function(argname, index) { + if (has_default == 1 && self.args[index] instanceof AST_Spread) has_default = 2; + if (argname instanceof AST_DefaultValue) { + if (!has_default) has_default = 1; + var arg = has_default == 1 && self.args[index]; + if (!is_undefined(arg)) has_default = 2; + if (has_arg_refs(fn, argname.value)) return false; + argname = argname.name; + } + if (argname instanceof AST_Destructured) { + has_destructured = true; + if (has_arg_refs(fn, argname)) return false; + } + return true; + }) && !(fn.rest instanceof AST_Destructured && has_arg_refs(fn, fn.rest)); + var can_inline = can_drop + && compressor.option("inline") + && !self.is_expr_pure(compressor) + && (exp === fn || safe_from_strict_mode(fn, compressor)); + if (can_inline && stat instanceof AST_Return) { + var value = stat.value; + if (exp === fn + && !fn.name + && (!value || value.is_constant_expression()) + && safe_from_await_yield(fn, avoid_await_yield(compressor))) { + return make_sequence(self, convert_args(value)).optimize(compressor); + } + } + if (is_func && !fn.contains_this()) { + var def, value, var_assigned = false; + if (can_inline + && !fn.uses_arguments + && !fn.pinned() + && !(fn.name && fn instanceof AST_LambdaExpression) + && (exp === fn || !recursive_ref(compressor, def = exp.definition(), fn) + && fn.is_constant_expression(find_scope(compressor))) + && (value = can_flatten_body(stat))) { + var replacing = exp === fn || def.single_use && def.references.length - def.replaced == 1; + if (can_substitute_directly()) { + var args = self.args.slice(); + var refs = []; + var retValue = value.clone(true).transform(new TreeTransformer(function(node) { + if (node instanceof AST_SymbolRef) { + var def = node.definition(); + if (fn.variables.get(node.name) !== def) { + refs.push(node); + return node; + } + var index = resolve_index(def); + var arg = args[index]; + if (!arg) return make_node(AST_Undefined, self); + args[index] = null; + var parent = this.parent(); + return parent ? maintain_this_binding(parent, node, arg) : arg; + } + })); + var save_inlined = fn.inlined; + if (exp !== fn) fn.inlined = true; + var exprs = []; + args.forEach(function(arg) { + if (!arg) return; + arg = arg.clone(true); + arg.walk(new TreeWalker(function(node) { + if (node instanceof AST_SymbolRef) refs.push(node); + })); + exprs.push(arg); + }, []); + exprs.push(retValue); + var node = make_sequence(self, exprs).optimize(compressor); + fn.inlined = save_inlined; + node = maintain_this_binding(parent, current, node); + if (replacing || best_of_expression(node, self) === node) { + refs.forEach(function(ref) { + ref.scope = exp === fn ? fn.parent_scope : exp.scope; + ref.reference(); + var def = ref.definition(); + if (replacing) def.replaced++; + def.single_use = false; + }); + return node; + } else if (!node.has_side_effects(compressor)) { + self.drop_side_effect_free = function(compressor, first_in_statement) { + var self = this; + var exprs = self.args.slice(); + exprs.unshift(self.expression); + return make_sequence(self, exprs).drop_side_effect_free(compressor, first_in_statement); + }; + } + } + var arg_used, insert, in_loop, scope; + if (replacing && can_inject_symbols()) { + fn._squeezed = true; + if (exp !== fn) fn.parent_scope = exp.scope; + var node = make_sequence(self, flatten_fn()).optimize(compressor); + return maintain_this_binding(parent, current, node); + } + } + if (compressor.option("side_effects") + && can_drop + && all(fn.body, is_empty) + && (fn === exp ? fn_name_unused(fn, compressor) : !has_default && !has_destructured && !fn.rest) + && !(is_arrow(fn) && fn.value) + && safe_from_await_yield(fn, avoid_await_yield(compressor))) { + return make_sequence(self, convert_args()).optimize(compressor); + } + } + if (compressor.option("drop_console")) { + if (exp instanceof AST_PropAccess) { + var name = exp.expression; + while (name.expression) { + name = name.expression; + } + if (is_undeclared_ref(name) && name.name == "console") { + return make_node(AST_Undefined, self).optimize(compressor); + } + } + } + if (compressor.option("negate_iife") && parent instanceof AST_SimpleStatement && is_iife_call(current)) { + return self.negate(compressor, true); + } + return try_evaluate(compressor, self); + + function make_void_lhs(orig) { + return make_node(AST_Sub, orig, { + expression: make_node(AST_Array, orig, { elements: [] }), + property: make_node(AST_Number, orig, { value: 0 }), + }); + } + + function convert_args(value) { + var args = self.args.slice(); + var destructured = has_default > 1 || has_destructured || fn.rest; + if (destructured || has_spread) args = [ make_node(AST_Array, self, { elements: args }) ]; + if (destructured) { + var tt = new TreeTransformer(function(node, descend) { + if (node instanceof AST_DefaultValue) return make_node(AST_DefaultValue, node, { + name: node.name.transform(tt) || make_void_lhs(node), + value: node.value, + }); + if (node instanceof AST_DestructuredArray) { + var elements = []; + node.elements.forEach(function(node, index) { + node = node.transform(tt); + if (node) elements[index] = node; + }); + fill_holes(node, elements); + return make_node(AST_DestructuredArray, node, { elements: elements }); + } + if (node instanceof AST_DestructuredObject) { + var properties = [], side_effects = []; + node.properties.forEach(function(prop) { + var key = prop.key; + var value = prop.value.transform(tt); + if (value) { + if (side_effects.length) { + if (!(key instanceof AST_Node)) key = make_node_from_constant(key, prop); + side_effects.push(key); + key = make_sequence(node, side_effects); + side_effects = []; + } + properties.push(make_node(AST_DestructuredKeyVal, prop, { + key: key, + value: value, + })); + } else if (key instanceof AST_Node) { + side_effects.push(key); + } + }); + if (side_effects.length) properties.push(make_node(AST_DestructuredKeyVal, node, { + key: make_sequence(node, side_effects), + value: make_void_lhs(node), + })); + return make_node(AST_DestructuredObject, node, { properties: properties }); + } + if (node instanceof AST_SymbolFunarg) return null; + }); + var lhs = []; + fn.argnames.forEach(function(argname, index) { + argname = argname.transform(tt); + if (argname) lhs[index] = argname; + }); + var rest = fn.rest && fn.rest.transform(tt); + if (rest) lhs.length = fn.argnames.length; + fill_holes(fn, lhs); + args[0] = make_node(AST_Assign, self, { + operator: "=", + left: make_node(AST_DestructuredArray, fn, { + elements: lhs, + rest: rest, + }), + right: args[0], + }); + } else fn.argnames.forEach(function(argname) { + if (argname instanceof AST_DefaultValue) args.push(argname.value); + }); + args.push(value || make_node(AST_Undefined, self)); + return args; + } + + function noop_value() { + return self.call_only ? make_node(AST_Number, self, { value: 0 }) : make_node(AST_Undefined, self); + } + + function return_value(stat) { + if (!stat) return noop_value(); + if (stat instanceof AST_Return) return stat.value || noop_value(); + if (stat instanceof AST_SimpleStatement) { + return self.call_only ? stat.body : make_node(AST_UnaryPrefix, stat, { + operator: "void", + expression: stat.body, + }); + } + } + + function can_flatten_body(stat) { + var len = fn.body.length; + if (len < 2) { + stat = return_value(stat); + if (stat) return stat; + } + if (compressor.option("inline") < 3) return false; + stat = null; + for (var i = 0; i < len; i++) { + var line = fn.body[i]; + if (line instanceof AST_Var) { + if (var_assigned) { + if (!stat) continue; + if (!(stat instanceof AST_SimpleStatement)) return false; + if (!declarations_only(line)) stat = null; + } else if (!declarations_only(line)) { + if (stat && !(stat instanceof AST_SimpleStatement)) return false; + stat = null; + var_assigned = true; + } + } else if (line instanceof AST_AsyncDefun + || line instanceof AST_Defun + || line instanceof AST_EmptyStatement) { + continue; + } else if (stat) { + return false; + } else { + stat = line; + } + } + return return_value(stat); + } + + function resolve_index(def) { + for (var i = fn.argnames.length; --i >= 0;) { + if (fn.argnames[i].definition() === def) return i; + } + } + + function can_substitute_directly() { + if (has_default || has_destructured || has_spread || var_assigned || fn.rest) return; + if (compressor.option("inline") < 2 && fn.argnames.length) return; + if (!fn.variables.all(function(def) { + return def.references.length - def.replaced < 2 && def.orig[0] instanceof AST_SymbolFunarg; + })) return; + var scope = compressor.find_parent(AST_Scope); + var abort = false; + var avoid = avoid_await_yield(compressor, scope); + var begin; + var in_order = []; + var side_effects = false; + var tw = new TreeWalker(function(node, descend) { + if (abort) return true; + if (node instanceof AST_Binary && lazy_op[node.operator] + || node instanceof AST_Conditional) { + in_order = null; + return; + } + if (node instanceof AST_Class) return abort = true; + if (node instanceof AST_Scope) return abort = true; + if (avoid && node instanceof AST_Symbol && avoid[node.name]) return abort = true; + if (node instanceof AST_SymbolRef) { + var def = node.definition(); + if (fn.variables.get(node.name) !== def) { + in_order = null; + return; + } + if (def.init instanceof AST_LambdaDefinition) return abort = true; + if (is_lhs(node, tw.parent())) return abort = true; + var index = resolve_index(def); + if (!(begin < index)) begin = index; + if (!in_order) return; + if (side_effects) { + in_order = null; + } else { + in_order.push(fn.argnames[index]); + } + return; + } + if (side_effects) return; + if (node instanceof AST_Assign && node.left instanceof AST_PropAccess) { + node.left.expression.walk(tw); + if (node.left instanceof AST_Sub) node.left.property.walk(tw); + node.right.walk(tw); + side_effects = true; + return true; + } + if (node.has_side_effects(compressor)) { + descend(); + side_effects = true; + return true; + } + }); + value.walk(tw); + if (abort) return; + var end = self.args.length; + if (in_order && fn.argnames.length >= end) { + end = fn.argnames.length; + while (end-- > begin && fn.argnames[end] === in_order.pop()); + end++; + } + return end <= begin || all(self.args.slice(begin, end), side_effects && !in_order ? function(funarg) { + return funarg.is_constant_expression(scope); + } : function(funarg) { + return !funarg.has_side_effects(compressor); + }); + } + + function var_exists(defined, name) { + return defined.has(name) || identifier_atom[name] || scope.var_names().has(name); + } + + function can_inject_args(defined, safe_to_inject) { + var abort = false; + fn.each_argname(function(arg) { + if (abort) return; + if (arg.unused) return; + if (!safe_to_inject || var_exists(defined, arg.name)) return abort = true; + arg_used.set(arg.name, true); + if (in_loop) in_loop.push(arg.definition()); + }); + return !abort; + } + + function can_inject_vars(defined, safe_to_inject) { + for (var i = 0; i < fn.body.length; i++) { + var stat = fn.body[i]; + if (stat instanceof AST_LambdaDefinition) { + var name = stat.name; + if (!safe_to_inject) return false; + if (arg_used.has(name.name)) return false; + if (var_exists(defined, name.name)) return false; + if (!all(stat.enclosed, function(def) { + return def.scope === scope || def.scope === stat || !defined.has(def.name); + })) return false; + if (in_loop) in_loop.push(name.definition()); + continue; + } + if (!(stat instanceof AST_Var)) continue; + if (!safe_to_inject) return false; + for (var j = stat.definitions.length; --j >= 0;) { + var name = stat.definitions[j].name; + if (var_exists(defined, name.name)) return false; + if (in_loop) in_loop.push(name.definition()); + } + } + return true; + } + + function can_inject_symbols() { + var defined = new Dictionary(); + var level = 0, child; + scope = current; + do { + if (scope.variables) scope.variables.each(function(def) { + defined.set(def.name, true); + }); + child = scope; + scope = compressor.parent(level++); + if (scope instanceof AST_ClassField) { + if (!scope.static) return false; + } else if (scope instanceof AST_DWLoop) { + in_loop = []; + } else if (scope instanceof AST_For) { + if (scope.init === child) continue; + in_loop = []; + } else if (scope instanceof AST_ForEnumeration) { + if (scope.init === child) continue; + if (scope.object === child) continue; + in_loop = []; + } + } while (!(scope instanceof AST_Scope)); + insert = scope.body.indexOf(child) + 1; + if (!insert) return false; + if (!safe_from_await_yield(fn, avoid_await_yield(compressor, scope))) return false; + var safe_to_inject = (exp !== fn || fn.parent_scope.resolve() === scope) && !scope.pinned(); + if (scope instanceof AST_Toplevel) { + if (compressor.toplevel.vars) { + defined.set("arguments", true); + } else { + safe_to_inject = false; + } + } + arg_used = new Dictionary(); + var inline = compressor.option("inline"); + if (!can_inject_args(defined, inline >= 2 && safe_to_inject)) return false; + if (!can_inject_vars(defined, inline >= 3 && safe_to_inject)) return false; + return !in_loop || in_loop.length == 0 || !is_reachable(fn, in_loop); + } + + function append_var(decls, expressions, name, value) { + var def = name.definition(); + if (!scope.var_names().has(name.name)) { + def.first_decl = null; + scope.var_names().set(name.name, true); + decls.push(make_node(AST_VarDef, name, { + name: name, + value: null, + })); + } + scope.variables.set(name.name, def); + scope.enclosed.push(def); + if (!value) return; + var sym = make_node(AST_SymbolRef, name); + def.assignments++; + def.references.push(sym); + expressions.push(make_node(AST_Assign, self, { + operator: "=", + left: sym, + right: value, + })); + } + + function flatten_args(decls, expressions) { + var len = fn.argnames.length; + for (var i = self.args.length; --i >= len;) { + expressions.push(self.args[i]); + } + var default_args = []; + for (i = len; --i >= 0;) { + var argname = fn.argnames[i]; + var name; + if (argname instanceof AST_DefaultValue) { + default_args.push(argname); + name = argname.name; + } else { + name = argname; + } + var value = self.args[i]; + if (name.unused || scope.var_names().has(name.name)) { + if (value) expressions.push(value); + } else { + var symbol = make_node(AST_SymbolVar, name); + var def = name.definition(); + def.orig.push(symbol); + def.eliminated++; + if (name.unused !== undefined) { + append_var(decls, expressions, symbol); + if (value) expressions.push(value); + } else { + if (!value && argname === name && (in_loop + || name.name == "arguments" && !is_arrow(fn) && is_arrow(scope))) { + value = make_node(AST_Undefined, self); + } + append_var(decls, expressions, symbol, value); + } + } + } + decls.reverse(); + expressions.reverse(); + for (i = default_args.length; --i >= 0;) { + var node = default_args[i]; + if (node.name.unused !== undefined) { + expressions.push(node.value); + } else { + var sym = make_node(AST_SymbolRef, node.name); + node.name.definition().references.push(sym); + expressions.push(make_node(AST_Assign, node, { + operator: "=", + left: sym, + right: node.value, + })); + } + } + } + + function flatten_destructured(decls, expressions) { + expressions.push(make_node(AST_Assign, self, { + operator: "=", + left: make_node(AST_DestructuredArray, self, { + elements: fn.argnames.map(function(argname) { + if (argname.unused) return make_node(AST_Hole, argname); + return argname.convert_symbol(AST_SymbolRef, process); + }), + rest: fn.rest && fn.rest.convert_symbol(AST_SymbolRef, process), + }), + right: make_node(AST_Array, self, { elements: self.args.slice() }), + })); + + function process(ref, name) { + if (name.unused) return make_void_lhs(name); + var def = name.definition(); + def.assignments++; + def.references.push(ref); + var symbol = make_node(AST_SymbolVar, name); + def.orig.push(symbol); + def.eliminated++; + append_var(decls, expressions, symbol); + } + } + + function flatten_vars(decls, expressions) { + var args = [ insert, 0 ]; + var decl_var = [], expr_fn = [], expr_var = [], expr_loop = [], exprs = []; + fn.body.filter(in_loop ? function(stat) { + if (!(stat instanceof AST_LambdaDefinition)) return true; + var name = make_node(AST_SymbolVar, flatten_var(stat.name)); + var def = name.definition(); + def.fixed = false; + def.orig.push(name); + def.eliminated++; + append_var(decls, expr_fn, name, to_func_expr(stat, true)); + return false; + } : function(stat) { + if (!(stat instanceof AST_LambdaDefinition)) return true; + var def = stat.name.definition(); + scope.functions.set(def.name, def); + scope.variables.set(def.name, def); + scope.enclosed.push(def); + scope.var_names().set(def.name, true); + args.push(stat); + return false; + }).forEach(function(stat) { + if (!(stat instanceof AST_Var)) { + if (stat instanceof AST_SimpleStatement) exprs.push(stat.body); + return; + } + for (var j = 0; j < stat.definitions.length; j++) { + var var_def = stat.definitions[j]; + var name = flatten_var(var_def.name); + var value = var_def.value; + if (value && exprs.length > 0) { + exprs.push(value); + value = make_sequence(var_def, exprs); + exprs = []; + } + append_var(decl_var, expr_var, name, value); + if (!in_loop) continue; + if (arg_used.has(name.name)) continue; + if (name.definition().orig.length == 1 && fn.functions.has(name.name)) continue; + expr_loop.push(init_ref(compressor, name)); + } + }); + [].push.apply(decls, decl_var); + [].push.apply(expressions, expr_loop); + [].push.apply(expressions, expr_fn); + [].push.apply(expressions, expr_var); + return args; + } + + function flatten_fn() { + var decls = []; + var expressions = []; + if (has_default > 1 || has_destructured || has_spread || fn.rest) { + flatten_destructured(decls, expressions); + } else { + flatten_args(decls, expressions); + } + var args = flatten_vars(decls, expressions); + expressions.push(value); + if (decls.length) args.push(make_node(AST_Var, fn, { definitions: decls })); + [].splice.apply(scope.body, args); + fn.enclosed.forEach(function(def) { + if (scope.var_names().has(def.name)) return; + scope.enclosed.push(def); + scope.var_names().set(def.name, true); + }); + return expressions; + } + }); + + OPT(AST_New, function(self, compressor) { + if (compressor.option("unsafe")) { + var exp = self.expression; + if (is_undeclared_ref(exp)) switch (exp.name) { + case "Array": + case "Error": + case "Function": + case "Object": + case "RegExp": + return make_node(AST_Call, self).transform(compressor); + } + } + if (compressor.option("sequences")) { + var seq = lift_sequence_in_expression(self, compressor); + if (seq !== self) return seq.optimize(compressor); + } + if (compressor.option("unused")) drop_unused_call_args(self, compressor); + return self; + }); + + // (a = b, x && a = c) ---> a = x ? c : b + // (a = b, x || a = c) ---> a = x ? b : c + function to_conditional_assignment(compressor, def, value, node) { + if (!(node instanceof AST_Binary)) return; + if (!(node.operator == "&&" || node.operator == "||")) return; + if (!(node.right instanceof AST_Assign)) return; + if (node.right.operator != "=") return; + if (!(node.right.left instanceof AST_SymbolRef)) return; + if (node.right.left.definition() !== def) return; + if (value.has_side_effects(compressor)) return; + if (!safe_from_assignment(node.left)) return; + if (!safe_from_assignment(node.right.right)) return; + def.replaced++; + return node.operator == "&&" ? make_node(AST_Conditional, node, { + condition: node.left, + consequent: node.right.right, + alternative: value, + }) : make_node(AST_Conditional, node, { + condition: node.left, + consequent: value, + alternative: node.right.right, + }); + + function safe_from_assignment(node) { + if (node.has_side_effects(compressor)) return; + var hit = false; + node.walk(new TreeWalker(function(node) { + if (hit) return true; + if (node instanceof AST_SymbolRef && node.definition() === def) return hit = true; + })); + return !hit; + } + } + + OPT(AST_Sequence, function(self, compressor) { + var expressions = filter_for_side_effects(); + var end = expressions.length - 1; + merge_assignments(); + trim_right_for_undefined(); + if (end == 0) { + self = maintain_this_binding(compressor.parent(), compressor.self(), expressions[0]); + if (!(self instanceof AST_Sequence)) self = self.optimize(compressor); + return self; + } + self.expressions = expressions; + return self; + + function filter_for_side_effects() { + if (!compressor.option("side_effects")) return self.expressions; + var expressions = []; + var first = first_in_statement(compressor); + var last = self.expressions.length - 1; + self.expressions.forEach(function(expr, index) { + if (index < last) expr = expr.drop_side_effect_free(compressor, first); + if (expr) { + merge_sequence(expressions, expr); + first = false; + } + }); + return expressions; + } + + function trim_right_for_undefined() { + if (!compressor.option("side_effects")) return; + while (end > 0 && is_undefined(expressions[end], compressor)) end--; + if (end < expressions.length - 1) { + expressions[end] = make_node(AST_UnaryPrefix, self, { + operator: "void", + expression: expressions[end], + }); + expressions.length = end + 1; + } + } + + function is_simple_assign(node) { + return node instanceof AST_Assign + && node.operator == "=" + && node.left instanceof AST_SymbolRef + && node.left.definition(); + } + + function merge_assignments() { + for (var i = 1; i < end; i++) { + var prev = expressions[i - 1]; + var def = is_simple_assign(prev); + if (!def) continue; + var expr = expressions[i]; + if (compressor.option("conditionals")) { + var cond = to_conditional_assignment(compressor, def, prev.right, expr); + if (cond) { + prev.right = cond; + expressions.splice(i--, 1); + end--; + continue; + } + } + if (compressor.option("dead_code") + && is_simple_assign(expr) === def + && expr.right.is_constant_expression(def.scope.resolve())) { + expressions[--i] = prev.right; + } + } + } + }); + + OPT(AST_UnaryPostfix, function(self, compressor) { + if (compressor.option("sequences")) { + var seq = lift_sequence_in_expression(self, compressor); + if (seq !== self) return seq.optimize(compressor); + } + return try_evaluate(compressor, self); + }); + + var SIGN_OPS = makePredicate("+ -"); + var MULTIPLICATIVE_OPS = makePredicate("* / %"); + OPT(AST_UnaryPrefix, function(self, compressor) { + var op = self.operator; + var exp = self.expression; + if (compressor.option("sequences") && can_lift()) { + var seq = lift_sequence_in_expression(self, compressor); + if (seq !== self) return seq.optimize(compressor); + } + switch (op) { + case "+": + if (!compressor.option("evaluate")) break; + if (!exp.is_number(compressor, true)) break; + var parent = compressor.parent(); + if (parent instanceof AST_UnaryPrefix && parent.operator == "delete") break; + return exp; + case "-": + if (exp instanceof AST_Infinity) exp = exp.transform(compressor); + // avoids infinite recursion of numerals + if (exp instanceof AST_Number || exp instanceof AST_Infinity) return self; + break; + case "!": + if (!compressor.option("booleans")) break; + if (exp.is_truthy()) return make_sequence(self, [ exp, make_node(AST_False, self) ]).optimize(compressor); + if (compressor.in_boolean_context()) { + // !!foo ---> foo, if we're in boolean context + if (exp instanceof AST_UnaryPrefix && exp.operator == "!") return exp.expression; + if (exp instanceof AST_Binary) { + var first = first_in_statement(compressor); + self = (first ? best_of_statement : best_of_expression)(self, exp.negate(compressor, first)); + } + } + break; + case "delete": + if (!compressor.option("evaluate")) break; + if (may_not_delete(exp)) break; + return make_sequence(self, [ exp, make_node(AST_True, self) ]).optimize(compressor); + case "typeof": + if (!compressor.option("booleans")) break; + if (!compressor.in_boolean_context()) break; + // typeof always returns a non-empty string, thus always truthy + AST_Node.warn("Boolean expression always true [{start}]", self); + var exprs = [ make_node(AST_True, self) ]; + if (!(exp instanceof AST_SymbolRef && can_drop_symbol(exp, compressor))) exprs.unshift(exp); + return make_sequence(self, exprs).optimize(compressor); + case "void": + if (!compressor.option("side_effects")) break; + exp = exp.drop_side_effect_free(compressor); + if (!exp) return make_node(AST_Undefined, self).optimize(compressor); + self.expression = exp; + return self; + } + if (compressor.option("evaluate") + && exp instanceof AST_Binary + && SIGN_OPS[op] + && MULTIPLICATIVE_OPS[exp.operator] + && (exp.left.is_constant() || !exp.right.has_side_effects(compressor))) { + return make_node(AST_Binary, self, { + operator: exp.operator, + left: make_node(AST_UnaryPrefix, exp.left, { + operator: op, + expression: exp.left, + }), + right: exp.right, + }); + } + return try_evaluate(compressor, self); + + function may_not_delete(node) { + return node instanceof AST_Infinity + || node instanceof AST_NaN + || node instanceof AST_NewTarget + || node instanceof AST_PropAccess + || node instanceof AST_SymbolRef + || node instanceof AST_Undefined; + } + + function can_lift() { + switch (op) { + case "delete": + return !may_not_delete(exp.tail_node()); + case "typeof": + return !is_undeclared_ref(exp.tail_node()); + default: + return true; + } + } + }); + + OPT(AST_Await, function(self, compressor) { + if (!compressor.option("awaits")) return self; + if (compressor.option("sequences")) { + var seq = lift_sequence_in_expression(self, compressor); + if (seq !== self) return seq.optimize(compressor); + } + if (compressor.option("side_effects")) { + var exp = self.expression; + if (exp instanceof AST_Await) return exp.optimize(compressor); + if (exp instanceof AST_UnaryPrefix && exp.expression instanceof AST_Await) return exp.optimize(compressor); + for (var level = 0, node = self, parent; parent = compressor.parent(level++); node = parent) { + if (is_arrow(parent)) { + if (parent.value === node) return exp.optimize(compressor); + } else if (parent instanceof AST_Return) { + var drop = true; + do { + node = parent; + parent = compressor.parent(level++); + if (parent instanceof AST_Try && (parent.bfinally || parent.bcatch) !== node) { + drop = false; + break; + } + } while (parent && !(parent instanceof AST_Scope)); + if (drop) return exp.optimize(compressor); + } else if (parent instanceof AST_Sequence) { + if (parent.tail_node() === node) continue; + } + break; + } + } + return self; + }); + + OPT(AST_Yield, function(self, compressor) { + if (!compressor.option("yields")) return self; + if (compressor.option("sequences")) { + var seq = lift_sequence_in_expression(self, compressor); + if (seq !== self) return seq.optimize(compressor); + } + var exp = self.expression; + if (self.nested && exp.TYPE == "Call") { + var inlined = exp.clone().optimize(compressor); + if (inlined.TYPE != "Call") return inlined; + } + return self; + }); + + AST_Binary.DEFMETHOD("lift_sequences", function(compressor) { + if (this.left instanceof AST_PropAccess) { + if (!(this.left.expression instanceof AST_Sequence)) return this; + var x = this.left.expression.expressions.slice(); + var e = this.clone(); + e.left = e.left.clone(); + e.left.expression = x.pop(); + x.push(e); + return make_sequence(this, x); + } + if (this.left instanceof AST_Sequence) { + var x = this.left.expressions.slice(); + var e = this.clone(); + e.left = x.pop(); + x.push(e); + return make_sequence(this, x); + } + if (this.right instanceof AST_Sequence) { + if (this.left.has_side_effects(compressor)) return this; + var assign = this.operator == "=" && this.left instanceof AST_SymbolRef; + var x = this.right.expressions; + var last = x.length - 1; + for (var i = 0; i < last; i++) { + if (!assign && x[i].has_side_effects(compressor)) break; + } + if (i == last) { + x = x.slice(); + var e = this.clone(); + e.right = x.pop(); + x.push(e); + return make_sequence(this, x); + } + if (i > 0) { + var e = this.clone(); + e.right = make_sequence(this.right, x.slice(i)); + x = x.slice(0, i); + x.push(e); + return make_sequence(this, x); + } + } + return this; + }); + + var indexFns = makePredicate("indexOf lastIndexOf"); + var commutativeOperators = makePredicate("== === != !== * & | ^"); + function is_object(node, plain) { + if (node instanceof AST_Assign) return !plain && node.operator == "=" && is_object(node.right); + if (node instanceof AST_New) return !plain; + if (node instanceof AST_Sequence) return is_object(node.tail_node(), plain); + if (node instanceof AST_SymbolRef) return !plain && is_object(node.fixed_value()); + return node instanceof AST_Array + || node instanceof AST_Class + || node instanceof AST_Lambda + || node instanceof AST_Object; + } + + function can_drop_op(node, compressor) { + var rhs = node.right; + switch (node.operator) { + case "in": + return is_object(rhs) || compressor && compressor.option("unsafe_comps"); + case "instanceof": + if (rhs instanceof AST_SymbolRef) rhs = rhs.fixed_value(); + if (rhs instanceof AST_Defun || rhs instanceof AST_Function || is_generator(rhs)) return true; + if (is_lambda(rhs) && node.left.is_constant()) return true; + return compressor && compressor.option("unsafe_comps"); + default: + return true; + } + } + + function needs_enqueuing(compressor, node) { + if (node.is_constant()) return true; + if (node instanceof AST_Assign) return node.operator != "=" || needs_enqueuing(compressor, node.right); + if (node instanceof AST_Binary) { + return !lazy_op[node.operator] + || needs_enqueuing(compressor, node.left) && needs_enqueuing(compressor, node.right); + } + if (node instanceof AST_Call) return is_async(node.expression); + if (node instanceof AST_Conditional) { + return needs_enqueuing(compressor, node.consequent) && needs_enqueuing(compressor, node.alternative); + } + if (node instanceof AST_Sequence) return needs_enqueuing(compressor, node.tail_node()); + if (node instanceof AST_SymbolRef) { + var fixed = node.fixed_value(); + return fixed && needs_enqueuing(compressor, fixed); + } + if (node instanceof AST_Template) return !node.tag || is_raw_tag(compressor, node.tag); + if (node instanceof AST_Unary) return true; + } + + function extract_lhs(node, compressor) { + if (node instanceof AST_Assign) return is_lhs_read_only(node.left, compressor) ? node : node.left; + if (node instanceof AST_Sequence) return extract_lhs(node.tail_node(), compressor); + if (node instanceof AST_UnaryPrefix && UNARY_POSTFIX[node.operator]) { + return is_lhs_read_only(node.expression, compressor) ? node : node.expression; + } + return node; + } + + function repeatable(compressor, node) { + if (node instanceof AST_Dot) return repeatable(compressor, node.expression); + if (node instanceof AST_Sub) { + return repeatable(compressor, node.expression) && repeatable(compressor, node.property); + } + if (node instanceof AST_Symbol) return true; + return !node.has_side_effects(compressor); + } + + function swap_chain(self, compressor) { + var rhs = self.right.tail_node(); + if (rhs !== self.right) { + var exprs = self.right.expressions.slice(0, -1); + exprs.push(rhs.left); + rhs = rhs.clone(); + rhs.left = make_sequence(self.right, exprs); + self.right = rhs; + } + self.left = make_node(AST_Binary, self, { + operator: self.operator, + left: self.left, + right: rhs.left, + start: self.left.start, + end: rhs.left.end, + }); + self.right = rhs.right; + if (compressor) { + var left = self.left.transform(compressor); + if (left !== self.left) { + self = self.clone(); + self.left = left; + } + return self; + } + if (self.operator == rhs.left.operator) swap_chain(self.left); + } + + OPT(AST_Binary, function(self, compressor) { + if (commutativeOperators[self.operator] + && self.right.is_constant() + && !self.left.is_constant() + && !(self.left instanceof AST_Binary + && PRECEDENCE[self.left.operator] >= PRECEDENCE[self.operator])) { + // if right is a constant, whatever side effects the + // left side might have could not influence the + // result. hence, force switch. + reverse(); + } + if (compressor.option("sequences")) { + var seq = self.lift_sequences(compressor); + if (seq !== self) return seq.optimize(compressor); + } + if (compressor.option("assignments") && lazy_op[self.operator]) { + var lhs = extract_lhs(self.left, compressor); + var right = self.right; + // a || (a = x) ---> a = a || x + // (a = x) && (a = y) ---> a = (a = x) && y + if (lhs instanceof AST_SymbolRef + && right instanceof AST_Assign + && right.operator == "=" + && lhs.equals(right.left)) { + lhs = lhs.clone(); + var assign = make_node(AST_Assign, self, { + operator: "=", + left: lhs, + right: make_node(AST_Binary, self, { + operator: self.operator, + left: self.left, + right: right.right, + }), + }); + if (lhs.fixed) { + lhs.fixed = function() { + return assign.right; + }; + lhs.fixed.assigns = [ assign ]; + } + var def = lhs.definition(); + def.references.push(lhs); + def.replaced++; + return assign.optimize(compressor); + } + } + if (compressor.option("comparisons")) switch (self.operator) { + case "===": + case "!==": + if (is_undefined(self.left, compressor) && self.right.is_defined(compressor)) { + AST_Node.warn("Expression always defined [{start}]", self); + return make_sequence(self, [ + self.right, + make_node(self.operator == "===" ? AST_False : AST_True, self), + ]).optimize(compressor); + } + var is_strict_comparison = true; + if ((self.left.is_string(compressor) && self.right.is_string(compressor)) || + (self.left.is_number(compressor) && self.right.is_number(compressor)) || + (self.left.is_boolean(compressor) && self.right.is_boolean(compressor)) || + repeatable(compressor, self.left) && self.left.equals(self.right)) { + self.operator = self.operator.slice(0, 2); + } + // XXX: intentionally falling down to the next case + case "==": + case "!=": + // void 0 == x ---> null == x + if (!is_strict_comparison && is_undefined(self.left, compressor)) { + self.left = make_node(AST_Null, self.left); + } + // "undefined" == typeof x ---> undefined === x + else if (compressor.option("typeofs") + && self.left instanceof AST_String + && self.left.value == "undefined" + && self.right instanceof AST_UnaryPrefix + && self.right.operator == "typeof") { + var expr = self.right.expression; + if (expr instanceof AST_SymbolRef ? expr.is_declared(compressor) + : !(expr instanceof AST_PropAccess && compressor.option("ie"))) { + self.right = expr; + self.left = make_node(AST_Undefined, self.left).optimize(compressor); + if (self.operator.length == 2) self.operator += "="; + } + } + // obj !== obj ---> false + else if (self.left instanceof AST_SymbolRef + && self.right instanceof AST_SymbolRef + && self.left.definition() === self.right.definition() + && is_object(self.left)) { + return make_node(self.operator[0] == "=" ? AST_True : AST_False, self).optimize(compressor); + } + break; + case "&&": + case "||": + // void 0 !== x && null !== x ---> null != x + // void 0 === x || null === x ---> null == x + var left = self.left; + if (!(left instanceof AST_Binary)) break; + if (left.operator != (self.operator == "&&" ? "!==" : "===")) break; + if (!(self.right instanceof AST_Binary)) break; + if (left.operator != self.right.operator) break; + if (is_undefined(left.left, compressor) && self.right.left instanceof AST_Null + || left.left instanceof AST_Null && is_undefined(self.right.left, compressor)) { + var expr = left.right; + if (expr instanceof AST_Assign && expr.operator == "=") expr = expr.left; + if (expr.has_side_effects(compressor)) break; + if (!expr.equals(self.right.right)) break; + left.operator = left.operator.slice(0, -1); + left.left = make_node(AST_Null, self); + return left; + } + break; + } + var in_bool = false; + var parent = compressor.parent(); + if (compressor.option("booleans")) { + var lhs = extract_lhs(self.left, compressor); + if (lazy_op[self.operator] && !lhs.has_side_effects(compressor)) { + // a || a ---> a + // (a = x) && a --> a = x + if (lhs.equals(self.right)) { + return maintain_this_binding(parent, compressor.self(), self.left).optimize(compressor); + } + mark_duplicate_condition(compressor, lhs); + } + in_bool = compressor.in_boolean_context(); + } + if (in_bool) switch (self.operator) { + case "+": + var ev = self.left.evaluate(compressor, true); + if (ev && typeof ev == "string" || (ev = self.right.evaluate(compressor, true)) && typeof ev == "string") { + AST_Node.warn("+ in boolean context always true [{start}]", self); + var exprs = []; + if (self.left.evaluate(compressor) instanceof AST_Node) exprs.push(self.left); + if (self.right.evaluate(compressor) instanceof AST_Node) exprs.push(self.right); + if (exprs.length < 2) { + exprs.push(make_node(AST_True, self)); + return make_sequence(self, exprs).optimize(compressor); + } + self.truthy = true; + } + break; + case "==": + if (self.left instanceof AST_String && self.left.value == "" && self.right.is_string(compressor)) { + return make_node(AST_UnaryPrefix, self, { + operator: "!", + expression: self.right, + }).optimize(compressor); + } + break; + case "!=": + if (self.left instanceof AST_String && self.left.value == "" && self.right.is_string(compressor)) { + return self.right.optimize(compressor); + } + break; + } + if (compressor.option("comparisons") && self.is_boolean(compressor)) { + if (parent.TYPE != "Binary") { + var negated = make_node(AST_UnaryPrefix, self, { + operator: "!", + expression: self.negate(compressor), + }); + if (best_of(compressor, self, negated) === negated) return negated; + } + switch (self.operator) { + case ">": reverse("<"); break; + case ">=": reverse("<="); break; + } + } + if (compressor.option("conditionals") && lazy_op[self.operator]) { + if (self.left instanceof AST_Binary && self.operator == self.left.operator) { + var before = make_node(AST_Binary, self, { + operator: self.operator, + left: self.left.right, + right: self.right, + }); + var after = before.transform(compressor); + if (before !== after) { + self.left = self.left.left; + self.right = after; + } + } + // x && (y && z) ---> x && y && z + // w || (x, y || z) ---> w || (x, y) || z + var rhs = self.right.tail_node(); + if (rhs instanceof AST_Binary && self.operator == rhs.operator) self = swap_chain(self, compressor); + } + if (compressor.option("strings") && self.operator == "+") { + // "foo" + 42 + "" ---> "foo" + 42 + if (self.right instanceof AST_String + && self.right.value == "" + && self.left.is_string(compressor)) { + return self.left.optimize(compressor); + } + // "" + ("foo" + 42) ---> "foo" + 42 + if (self.left instanceof AST_String + && self.left.value == "" + && self.right.is_string(compressor)) { + return self.right.optimize(compressor); + } + // "" + 42 + "foo" ---> 42 + "foo" + if (self.left instanceof AST_Binary + && self.left.operator == "+" + && self.left.left instanceof AST_String + && self.left.left.value == "" + && self.right.is_string(compressor) + && (self.left.right.is_constant() || !self.right.has_side_effects(compressor))) { + self.left = self.left.right; + return self.optimize(compressor); + } + // "x" + (y + "z") ---> "x" + y + "z" + // w + (x, "y" + z) ---> w + (x, "y") + z + var rhs = self.right.tail_node(); + if (rhs instanceof AST_Binary + && self.operator == rhs.operator + && (self.left.is_string(compressor) && rhs.is_string(compressor) + || rhs.left.is_string(compressor) + && (self.left.is_constant() || !rhs.right.has_side_effects(compressor)))) { + self = swap_chain(self, compressor); + } + } + if (compressor.option("evaluate")) { + var associative = true; + switch (self.operator) { + case "&&": + var ll = fuzzy_eval(compressor, self.left); + if (!ll) { + AST_Node.warn("Condition left of && always false [{start}]", self); + return maintain_this_binding(parent, compressor.self(), self.left).optimize(compressor); + } else if (!(ll instanceof AST_Node)) { + AST_Node.warn("Condition left of && always true [{start}]", self); + return make_sequence(self, [ self.left, self.right ]).optimize(compressor); + } + if (!self.right.evaluate(compressor, true)) { + if (in_bool && !(self.right.evaluate(compressor) instanceof AST_Node)) { + AST_Node.warn("Boolean && always false [{start}]", self); + return make_sequence(self, [ self.left, make_node(AST_False, self) ]).optimize(compressor); + } else self.falsy = true; + } else if ((in_bool || parent.operator == "&&" && parent.left === compressor.self()) + && !(self.right.evaluate(compressor) instanceof AST_Node)) { + AST_Node.warn("Dropping side-effect-free && [{start}]", self); + return self.left.optimize(compressor); + } + // (x || false) && y ---> x ? y : false + if (self.left.operator == "||") { + var lr = fuzzy_eval(compressor, self.left.right); + if (!lr) return make_node(AST_Conditional, self, { + condition: self.left.left, + consequent: self.right, + alternative: self.left.right, + }).optimize(compressor); + } + break; + case "??": + var nullish = true; + case "||": + var ll = fuzzy_eval(compressor, self.left, nullish); + if (nullish ? ll == null : !ll) { + AST_Node.warn("Condition left of {operator} always {value} [{start}]", { + operator: self.operator, + value: nullish ? "nullish" : "false", + start: self.start, + }); + return make_sequence(self, [ self.left, self.right ]).optimize(compressor); + } else if (!(ll instanceof AST_Node)) { + AST_Node.warn("Condition left of {operator} always {value} [{start}]", { + operator: self.operator, + value: nullish ? "defined" : "true", + start: self.start, + }); + return maintain_this_binding(parent, compressor.self(), self.left).optimize(compressor); + } + var rr; + if (!nullish && (rr = self.right.evaluate(compressor, true)) && !(rr instanceof AST_Node)) { + if (in_bool && !(self.right.evaluate(compressor) instanceof AST_Node)) { + AST_Node.warn("Boolean || always true [{start}]", self); + return make_sequence(self, [ self.left, make_node(AST_True, self) ]).optimize(compressor); + } else self.truthy = true; + } else if ((in_bool || parent.operator == "||" && parent.left === compressor.self()) + && !self.right.evaluate(compressor)) { + AST_Node.warn("Dropping side-effect-free {operator} [{start}]", self); + return self.left.optimize(compressor); + } + // x && true || y ---> x ? true : y + if (!nullish && self.left.operator == "&&") { + var lr = fuzzy_eval(compressor, self.left.right); + if (lr && !(lr instanceof AST_Node)) return make_node(AST_Conditional, self, { + condition: self.left.left, + consequent: self.left.right, + alternative: self.right, + }).optimize(compressor); + } + break; + case "+": + // "foo" + ("bar" + x) ---> "foobar" + x + if (self.left instanceof AST_Constant + && self.right instanceof AST_Binary + && self.right.operator == "+" + && self.right.left instanceof AST_Constant + && self.right.is_string(compressor)) { + self = make_node(AST_Binary, self, { + operator: "+", + left: make_node(AST_String, self.left, { + value: "" + self.left.value + self.right.left.value, + start: self.left.start, + end: self.right.left.end, + }), + right: self.right.right, + }); + } + // (x + "foo") + "bar" ---> x + "foobar" + if (self.right instanceof AST_Constant + && self.left instanceof AST_Binary + && self.left.operator == "+" + && self.left.right instanceof AST_Constant + && self.left.is_string(compressor)) { + self = make_node(AST_Binary, self, { + operator: "+", + left: self.left.left, + right: make_node(AST_String, self.right, { + value: "" + self.left.right.value + self.right.value, + start: self.left.right.start, + end: self.right.end, + }), + }); + } + // a + -b ---> a - b + if (self.right instanceof AST_UnaryPrefix + && self.right.operator == "-" + && self.left.is_number(compressor)) { + self = make_node(AST_Binary, self, { + operator: "-", + left: self.left, + right: self.right.expression, + }); + break; + } + // -a + b ---> b - a + if (self.left instanceof AST_UnaryPrefix + && self.left.operator == "-" + && reversible() + && self.right.is_number(compressor)) { + self = make_node(AST_Binary, self, { + operator: "-", + left: self.right, + right: self.left.expression, + }); + break; + } + // (a + b) + 3 ---> 3 + (a + b) + if (compressor.option("unsafe_math") + && self.left instanceof AST_Binary + && PRECEDENCE[self.left.operator] == PRECEDENCE[self.operator] + && self.right.is_constant() + && (self.right.is_boolean(compressor) || self.right.is_number(compressor)) + && self.left.is_number(compressor) + && !self.left.right.is_constant() + && (self.left.left.is_boolean(compressor) || self.left.left.is_number(compressor))) { + self = make_node(AST_Binary, self, { + operator: self.left.operator, + left: make_node(AST_Binary, self, { + operator: self.operator, + left: self.right, + right: self.left.left, + }), + right: self.left.right, + }); + break; + } + case "-": + // a - -b ---> a + b + if (self.right instanceof AST_UnaryPrefix + && self.right.operator == "-" + && self.left.is_number(compressor) + && self.right.expression.is_number(compressor)) { + self = make_node(AST_Binary, self, { + operator: "+", + left: self.left, + right: self.right.expression, + }); + break; + } + case "*": + case "/": + associative = compressor.option("unsafe_math"); + // +a - b ---> a - b + // a - +b ---> a - b + if (self.operator != "+") [ "left", "right" ].forEach(function(operand) { + var node = self[operand]; + if (node instanceof AST_UnaryPrefix && node.operator == "+") { + var exp = node.expression; + if (exp.is_boolean(compressor) || exp.is_number(compressor) || exp.is_string(compressor)) { + self[operand] = exp; + } + } + }); + case "&": + case "|": + case "^": + // a + +b ---> +b + a + if (self.operator != "-" + && self.operator != "/" + && (self.left.is_boolean(compressor) || self.left.is_number(compressor)) + && (self.right.is_boolean(compressor) || self.right.is_number(compressor)) + && reversible() + && !(self.left instanceof AST_Binary + && self.left.operator != self.operator + && PRECEDENCE[self.left.operator] >= PRECEDENCE[self.operator])) { + self = best_of(compressor, self, make_node(AST_Binary, self, { + operator: self.operator, + left: self.right, + right: self.left, + }), self.right instanceof AST_Constant && !(self.left instanceof AST_Constant)); + } + if (!associative || !self.is_number(compressor)) break; + // a + (b + c) ---> (a + b) + c + if (self.right instanceof AST_Binary + && self.right.operator != "%" + && PRECEDENCE[self.right.operator] == PRECEDENCE[self.operator] + && self.right.is_number(compressor) + && (self.operator != "+" + || self.right.left.is_boolean(compressor) + || self.right.left.is_number(compressor)) + && (self.operator != "-" || !self.left.is_negative_zero()) + && (self.right.left.is_constant_expression() + || !self.right.right.has_side_effects(compressor)) + && !is_modify_array(self.right.right)) { + self = make_node(AST_Binary, self, { + operator: align(self.operator, self.right.operator), + left: make_node(AST_Binary, self.left, { + operator: self.operator, + left: self.left, + right: self.right.left, + start: self.left.start, + end: self.right.left.end, + }), + right: self.right.right, + }); + if (self.operator == "+" + && !self.right.is_boolean(compressor) + && !self.right.is_number(compressor)) { + self.right = make_node(AST_UnaryPrefix, self.right, { + operator: "+", + expression: self.right, + }); + } + } + // (2 * n) * 3 ---> 6 * n + // (n + 2) + 3 ---> n + 5 + if (self.right instanceof AST_Constant + && self.left instanceof AST_Binary + && self.left.operator != "%" + && PRECEDENCE[self.left.operator] == PRECEDENCE[self.operator] + && self.left.is_number(compressor)) { + if (self.left.left instanceof AST_Constant) { + var lhs = make_binary(self.operator, self.left.left, self.right, { + start: self.left.left.start, + end: self.right.end, + }); + self = make_binary(self.left.operator, try_evaluate(compressor, lhs), self.left.right, self); + } else if (self.left.right instanceof AST_Constant) { + var op = align(self.left.operator, self.operator); + var rhs = try_evaluate(compressor, make_binary(op, self.left.right, self.right, self.left)); + if (rhs.is_constant() + && !(self.left.operator == "-" + && self.right.value != 0 + && +rhs.value == 0 + && self.left.left.is_negative_zero())) { + self = make_binary(self.left.operator, self.left.left, rhs, self); + } + } + } + break; + case "instanceof": + if (!can_drop_op(self, compressor)) break; + if (is_lambda(self.right)) return make_sequence(self, [ + self.left, + self.right, + make_node(AST_False, self), + ]).optimize(compressor); + break; + } + if (!(parent instanceof AST_UnaryPrefix && parent.operator == "delete")) { + if (self.left instanceof AST_Number && !self.right.is_constant()) switch (self.operator) { + // 0 + n ---> n + case "+": + if (self.left.value == 0) { + if (self.right.is_boolean(compressor)) return make_node(AST_UnaryPrefix, self, { + operator: "+", + expression: self.right, + }).optimize(compressor); + if (self.right.is_number(compressor) && !self.right.is_negative_zero()) return self.right; + } + break; + // 1 * n ---> n + case "*": + if (self.left.value == 1) return make_node(AST_UnaryPrefix, self, { + operator: "+", + expression: self.right, + }).optimize(compressor); + break; + } + if (self.right instanceof AST_Number && !self.left.is_constant()) switch (self.operator) { + // n + 0 ---> n + case "+": + if (self.right.value == 0) { + if (self.left.is_boolean(compressor)) return make_node(AST_UnaryPrefix, self, { + operator: "+", + expression: self.left, + }).optimize(compressor); + if (self.left.is_number(compressor) && !self.left.is_negative_zero()) return self.left; + } + break; + // n - 0 ---> n + case "-": + if (self.right.value == 0) return make_node(AST_UnaryPrefix, self, { + operator: "+", + expression: self.left, + }).optimize(compressor); + break; + // n / 1 ---> n + case "/": + if (self.right.value == 1) return make_node(AST_UnaryPrefix, self, { + operator: "+", + expression: self.left, + }).optimize(compressor); + break; + } + } + } + if (compressor.option("typeofs")) switch (self.operator) { + case "&&": + mark_locally_defined(self.left, self.right, null); + break; + case "||": + mark_locally_defined(self.left, null, self.right); + break; + } + if (compressor.option("unsafe")) { + var indexRight = is_indexFn(self.right); + if (in_bool + && indexRight + && (self.operator == "==" || self.operator == "!=") + && self.left instanceof AST_Number + && self.left.value == 0) { + return (self.operator == "==" ? make_node(AST_UnaryPrefix, self, { + operator: "!", + expression: self.right, + }) : self.right).optimize(compressor); + } + var indexLeft = is_indexFn(self.left); + if (compressor.option("comparisons") && is_indexOf_match_pattern()) { + var node = make_node(AST_UnaryPrefix, self, { + operator: "!", + expression: make_node(AST_UnaryPrefix, self, { + operator: "~", + expression: indexLeft ? self.left : self.right, + }), + }); + switch (self.operator) { + case "<": + if (indexLeft) break; + case "<=": + case "!=": + node = make_node(AST_UnaryPrefix, self, { + operator: "!", + expression: node, + }); + break; + } + return node.optimize(compressor); + } + } + return try_evaluate(compressor, self); + + function is_modify_array(node) { + var found = false; + node.walk(new TreeWalker(function(node) { + if (found) return true; + if (node instanceof AST_Assign) { + if (node.left instanceof AST_PropAccess) return found = true; + } else if (node instanceof AST_Unary) { + if (unary_side_effects[node.operator] && node.expression instanceof AST_PropAccess) { + return found = true; + } + } + })); + return found; + } + + function align(ref, op) { + switch (ref) { + case "-": + return op == "+" ? "-" : "+"; + case "/": + return op == "*" ? "/" : "*"; + default: + return op; + } + } - // tell me if a statement aborts - function aborts(thing) { - return thing && thing.aborts(); - }; - (function(def){ - def(AST_Statement, function(){ return null }); - def(AST_Jump, function(){ return this }); - function block_aborts(){ - var n = this.body.length; - return n > 0 && aborts(this.body[n - 1]); - }; - def(AST_BlockStatement, block_aborts); - def(AST_SwitchBranch, block_aborts); - def(AST_If, function(){ - return this.alternative && aborts(this.body) && aborts(this.alternative) && this; - }); - })(function(node, func){ - node.DEFMETHOD("aborts", func); - }); + function make_binary(op, left, right, orig) { + if (op == "+") { + if (!left.is_boolean(compressor) && !left.is_number(compressor)) { + left = make_node(AST_UnaryPrefix, left, { + operator: "+", + expression: left, + }); + } + if (!right.is_boolean(compressor) && !right.is_number(compressor)) { + right = make_node(AST_UnaryPrefix, right, { + operator: "+", + expression: right, + }); + } + } + return make_node(AST_Binary, orig, { + operator: op, + left: left, + right: right, + }); + } - /* -----[ optimizers ]----- */ + function is_indexFn(node) { + return node.TYPE == "Call" + && node.expression instanceof AST_Dot + && indexFns[node.expression.property]; + } - OPT(AST_Directive, function(self, compressor){ - if (self.scope.has_directive(self.value) !== self.scope) { - return make_node(AST_EmptyStatement, self); + function is_indexOf_match_pattern() { + switch (self.operator) { + case "<=": + // 0 <= array.indexOf(string) ---> !!~array.indexOf(string) + return indexRight && self.left instanceof AST_Number && self.left.value == 0; + case "<": + // array.indexOf(string) < 0 ---> !~array.indexOf(string) + if (indexLeft && self.right instanceof AST_Number && self.right.value == 0) return true; + // -1 < array.indexOf(string) ---> !!~array.indexOf(string) + case "==": + case "!=": + // -1 == array.indexOf(string) ---> !~array.indexOf(string) + // -1 != array.indexOf(string) ---> !!~array.indexOf(string) + if (!indexRight) return false; + return self.left instanceof AST_Number && self.left.value == -1 + || self.left instanceof AST_UnaryPrefix && self.left.operator == "-" + && self.left.expression instanceof AST_Number && self.left.expression.value == 1; + } } - return self; - }); - OPT(AST_Debugger, function(self, compressor){ - if (compressor.option("drop_debugger")) - return make_node(AST_EmptyStatement, self); - return self; - }); + function reversible() { + return self.left.is_constant() + || self.right.is_constant() + || !self.left.has_side_effects(compressor) + && !self.right.has_side_effects(compressor); + } - OPT(AST_LabeledStatement, function(self, compressor){ - if (self.body instanceof AST_Break - && compressor.loopcontrol_target(self.body.label) === self.body) { - return make_node(AST_EmptyStatement, self); + function reverse(op) { + if (reversible()) { + if (op) self.operator = op; + var tmp = self.left; + self.left = self.right; + self.right = tmp; + } } - return self.label.references.length == 0 ? self.body : self; }); - OPT(AST_Block, function(self, compressor){ - self.body = tighten_body(self.body, compressor); + OPT(AST_SymbolExport, function(self) { return self; }); - OPT(AST_BlockStatement, function(self, compressor){ - self.body = tighten_body(self.body, compressor); - switch (self.body.length) { - case 1: return self.body[0]; - case 0: return make_node(AST_EmptyStatement, self); - } - return self; - }); + function recursive_ref(compressor, def, fn) { + var level = 0, node = compressor.self(); + do { + if (node === fn) return node; + if (is_lambda(node) && node.name && node.name.definition() === def) return node; + } while (node = compressor.parent(level++)); + } - AST_Scope.DEFMETHOD("drop_unused", function(compressor){ - var self = this; - if (compressor.option("unused") - && !(self instanceof AST_Toplevel) - && !self.uses_eval - ) { - var in_use = []; - var initializations = new Dictionary(); - // pass 1: find out which symbols are directly used in - // this scope (not in nested scopes). - var scope = this; - var tw = new TreeWalker(function(node, descend){ - if (node !== self) { - if (node instanceof AST_Defun) { - initializations.add(node.name.name, node); - return true; // don't go in nested scopes - } - if (node instanceof AST_Definitions && scope === self) { - node.definitions.forEach(function(def){ - if (def.value) { - initializations.add(def.name.name, def.value); - if (def.value.has_side_effects(compressor)) { - def.value.walk(tw); + function same_scope(def) { + var scope = def.scope.resolve(); + return all(def.references, function(ref) { + return scope === ref.scope.resolve(); + }); + } + + OPT(AST_SymbolRef, function(self, compressor) { + if (!compressor.option("ie") + && is_undeclared_ref(self) + // testing against `self.scope.uses_with` is an optimization + && !(self.scope.resolve().uses_with && compressor.find_parent(AST_With))) { + switch (self.name) { + case "undefined": + return make_node(AST_Undefined, self).optimize(compressor); + case "NaN": + return make_node(AST_NaN, self).optimize(compressor); + case "Infinity": + return make_node(AST_Infinity, self).optimize(compressor); + } + } + var parent = compressor.parent(); + if (compressor.option("reduce_vars") && is_lhs(compressor.self(), parent) !== compressor.self()) { + var def = self.definition(); + var fixed = self.fixed_value(); + var single_use = def.single_use && !(parent instanceof AST_Call && parent.is_expr_pure(compressor)); + if (single_use) { + if (is_lambda(fixed)) { + if ((def.scope !== self.scope.resolve(true) || def.in_loop) + && (!compressor.option("reduce_funcs") || def.escaped.depth == 1 || fixed.inlined)) { + single_use = false; + } else if (def.redefined()) { + single_use = false; + } else if (recursive_ref(compressor, def, fixed)) { + single_use = false; + } else if (fixed.name && fixed.name.definition() !== def) { + single_use = false; + } else if (fixed.parent_scope !== self.scope || is_funarg(def)) { + if (!safe_from_strict_mode(fixed, compressor)) { + single_use = false; + } else if ((single_use = fixed.is_constant_expression(self.scope)) == "f") { + var scope = self.scope; + do { + if (scope instanceof AST_LambdaDefinition || scope instanceof AST_LambdaExpression) { + scope.inlined = true; } - } - }); - return true; - } - if (node instanceof AST_SymbolRef) { - push_uniq(in_use, node.definition()); - return true; - } - if (node instanceof AST_Scope) { - var save_scope = scope; - scope = node; - descend(); - scope = save_scope; - return true; + } while (scope = scope.parent_scope); + } + } else if (fixed.name && (fixed.name.name == "await" && is_async(fixed) + || fixed.name.name == "yield" && is_generator(fixed))) { + single_use = false; + } else if (fixed.has_side_effects(compressor)) { + single_use = false; + } else if (compressor.option("ie") && fixed instanceof AST_Class) { + single_use = false; } + if (single_use) fixed.parent_scope = self.scope; + } else if (!fixed + || def.recursive_refs > 0 + || !fixed.is_constant_expression() + || fixed.drop_side_effect_free(compressor)) { + single_use = false; } - }); - self.walk(tw); - // pass 2: for every used symbol we need to walk its - // initialization code to figure out if it uses other - // symbols (that may not be in_use). - for (var i = 0; i < in_use.length; ++i) { - in_use[i].orig.forEach(function(decl){ - // undeclared globals will be instanceof AST_SymbolRef - var init = initializations.get(decl.name); - if (init) init.forEach(function(init){ - var tw = new TreeWalker(function(node){ - if (node instanceof AST_SymbolRef) { - push_uniq(in_use, node.definition()); - } - }); - init.walk(tw); - }); - }); } - // pass 3: we should drop declarations not in_use - var tt = new TreeTransformer( - function before(node, descend, in_list) { - if (node instanceof AST_Lambda && !(node instanceof AST_Accessor)) { - if (!compressor.option("keep_fargs")) { - for (var a = node.argnames, i = a.length; --i >= 0;) { - var sym = a[i]; - if (sym.unreferenced()) { - a.pop(); - compressor.warn("Dropping unused function argument {name} [{file}:{line},{col}]", { - name : sym.name, - file : sym.start.file, - line : sym.start.line, - col : sym.start.col - }); - } - else break; - } + if (single_use) { + def.single_use = false; + fixed._squeezed = true; + fixed.single_use = true; + if (fixed instanceof AST_DefClass) fixed = to_class_expr(fixed); + if (fixed instanceof AST_LambdaDefinition) fixed = to_func_expr(fixed); + if (is_lambda(fixed)) { + var scopes = []; + var scope = self.scope; + do { + scopes.push(scope); + if (scope === def.scope) break; + } while (scope = scope.parent_scope); + fixed.enclosed.forEach(function(def) { + if (fixed.variables.has(def.name)) return; + for (var i = 0; i < scopes.length; i++) { + var scope = scopes[i]; + if (!push_uniq(scope.enclosed, def)) return; + scope.var_names().set(def.name, true); } + }); + } + var value; + if (def.recursive_refs > 0) { + value = fixed.clone(true); + var defun_def = value.name.definition(); + var lambda_def = value.variables.get(value.name.name); + var name = lambda_def && lambda_def.orig[0]; + var def_fn_name, symbol_type; + if (value instanceof AST_Class) { + def_fn_name = "def_function"; + symbol_type = AST_SymbolClass; + } else { + def_fn_name = "def_variable"; + symbol_type = AST_SymbolLambda; } - if (node instanceof AST_Defun && node !== self) { - if (!member(node.name.definition(), in_use)) { - compressor.warn("Dropping unused function {name} [{file}:{line},{col}]", { - name : node.name.name, - file : node.name.start.file, - line : node.name.start.line, - col : node.name.start.col - }); - return make_node(AST_EmptyStatement, node); - } - return node; + if (!(name instanceof symbol_type)) { + name = make_node(symbol_type, value.name); + name.scope = value; + value.name = name; + lambda_def = value[def_fn_name](name); + lambda_def.recursive_refs = def.recursive_refs; } - if (node instanceof AST_Definitions && !(tt.parent() instanceof AST_ForIn)) { - var def = node.definitions.filter(function(def){ - if (member(def.name.definition(), in_use)) return true; - var w = { - name : def.name.name, - file : def.name.start.file, - line : def.name.start.line, - col : def.name.start.col - }; - if (def.value && def.value.has_side_effects(compressor)) { - def._unused_side_effects = true; - compressor.warn("Side effects in initialization of unused variable {name} [{file}:{line},{col}]", w); - return true; - } - compressor.warn("Dropping unused variable {name} [{file}:{line},{col}]", w); - return false; - }); - // place uninitialized names at the start - def = mergeSort(def, function(a, b){ - if (!a.value && b.value) return -1; - if (!b.value && a.value) return 1; - return 0; - }); - // for unused names whose initialization has - // side effects, we can cascade the init. code - // into the next one, or next statement. - var side_effects = []; - for (var i = 0; i < def.length;) { - var x = def[i]; - if (x._unused_side_effects) { - side_effects.push(x.value); - def.splice(i, 1); - } else { - if (side_effects.length > 0) { - side_effects.push(x.value); - x.value = AST_Seq.from_array(side_effects); - side_effects = []; - } - ++i; + value.walk(new TreeWalker(function(node) { + if (node instanceof AST_SymbolDeclaration) { + if (node !== name) { + var def = node.definition(); + def.orig.push(node); + def.eliminated++; } + return; } - if (side_effects.length > 0) { - side_effects = make_node(AST_BlockStatement, node, { - body: [ make_node(AST_SimpleStatement, node, { - body: AST_Seq.from_array(side_effects) - }) ] - }); + if (!(node instanceof AST_SymbolRef)) return; + var def = node.definition(); + if (def === defun_def) { + node.thedef = def = lambda_def; } else { - side_effects = null; - } - if (def.length == 0 && !side_effects) { - return make_node(AST_EmptyStatement, node); - } - if (def.length == 0) { - return side_effects; - } - node.definitions = def; - if (side_effects) { - side_effects.body.unshift(node); - node = side_effects; + def.single_use = false; + var fn = node.fixed_value(); + if (is_lambda(fn) + && fn.name + && fn.name.definition() === def + && def.scope === fn.name.scope + && fixed.variables.get(fn.name.name) === def) { + fn.name = fn.name.clone(); + node.thedef = def = value.variables.get(fn.name.name) || value[def_fn_name](fn.name); + } } - return node; + def.references.push(node); + })); + } else { + if (fixed instanceof AST_Scope) { + compressor.push(fixed); + value = fixed.optimize(compressor); + compressor.pop(); + } else { + value = fixed.optimize(compressor); } - if (node instanceof AST_For) { + value = value.transform(new TreeTransformer(function(node, descend) { + if (node instanceof AST_Scope) return node; + node = node.clone(); descend(node, this); - - if (node.init instanceof AST_BlockStatement) { - // certain combination of unused name + side effect leads to: - // https://github.com/mishoo/UglifyJS2/issues/44 - // that's an invalid AST. - // We fix it at this stage by moving the `var` outside the `for`. - - var body = node.init.body.slice(0, -1); - node.init = node.init.body.slice(-1)[0].body; - body.push(node); - - return in_list ? MAP.splice(body) : make_node(AST_BlockStatement, node, { - body: body - }); - } - } - if (node instanceof AST_Scope && node !== self) return node; + })); } - ); - self.transform(tt); - } - }); - - AST_Scope.DEFMETHOD("hoist_declarations", function(compressor){ - var hoist_funs = compressor.option("hoist_funs"); - var hoist_vars = compressor.option("hoist_vars"); - var self = this; - if (hoist_funs || hoist_vars) { - var dirs = []; - var hoisted = []; - var vars = new Dictionary(), vars_found = 0, var_decl = 0; - // let's count var_decl first, we seem to waste a lot of - // space if we hoist `var` when there's only one. - self.walk(new TreeWalker(function(node){ - if (node instanceof AST_Scope && node !== self) - return true; - if (node instanceof AST_Var) { - ++var_decl; - return true; - } - })); - hoist_vars = hoist_vars && var_decl > 1; - var tt = new TreeTransformer( - function before(node) { - if (node !== self) { - if (node instanceof AST_Directive) { - dirs.push(node); - return make_node(AST_EmptyStatement, node); - } - if (node instanceof AST_Defun && hoist_funs) { - hoisted.push(node); - return make_node(AST_EmptyStatement, node); - } - if (node instanceof AST_Var && hoist_vars) { - node.definitions.forEach(function(def){ - vars.set(def.name.name, def); - ++vars_found; - }); - var seq = node.to_assignments(); - var p = tt.parent(); - if (p instanceof AST_ForIn && p.init === node) { - if (seq == null) return node.definitions[0].name; - return seq; - } - if (p instanceof AST_For && p.init === node) { - return seq; - } - if (!seq) return make_node(AST_EmptyStatement, node); - return make_node(AST_SimpleStatement, node, { - body: seq - }); - } - if (node instanceof AST_Scope) - return node; // to avoid descending in nested scopes - } + def.replaced++; + return value; + } + var state; + if (fixed && (state = self.fixed || def.fixed).should_replace !== false) { + var ev, init; + if (fixed instanceof AST_This) { + if (!is_funarg(def) && same_scope(def) && !cross_class(def)) init = fixed; + } else if ((ev = fixed.evaluate(compressor, true)) !== fixed + && typeof ev != "function" + && (ev === null + || typeof ev != "object" + || compressor.option("unsafe_regexp") + && ev instanceof RegExp && !def.cross_loop && same_scope(def))) { + init = make_node_from_constant(ev, fixed); } - ); - self = self.transform(tt); - if (vars_found > 0) { - // collect only vars which don't show up in self's arguments list - var defs = []; - vars.each(function(def, name){ - if (self instanceof AST_Lambda - && find_if(function(x){ return x.name == def.name.name }, - self.argnames)) { - vars.del(name); - } else { - def = def.clone(); - def.value = null; - defs.push(def); - vars.set(name, def); - } - }); - if (defs.length > 0) { - // try to merge in assignments - for (var i = 0; i < self.body.length;) { - if (self.body[i] instanceof AST_SimpleStatement) { - var expr = self.body[i].body, sym, assign; - if (expr instanceof AST_Assign - && expr.operator == "=" - && (sym = expr.left) instanceof AST_Symbol - && vars.has(sym.name)) - { - var def = vars.get(sym.name); - if (def.value) break; - def.value = expr.right; - remove(defs, def); - defs.push(def); - self.body.splice(i, 1); - continue; - } - if (expr instanceof AST_Seq - && (assign = expr.car) instanceof AST_Assign - && assign.operator == "=" - && (sym = assign.left) instanceof AST_Symbol - && vars.has(sym.name)) - { - var def = vars.get(sym.name); - if (def.value) break; - def.value = assign.right; - remove(defs, def); - defs.push(def); - self.body[i].body = expr.cdr; - continue; - } + if (init) { + if (state.should_replace === undefined) { + var value_length = init.optimize(compressor).print_to_string().length; + if (!has_symbol_ref(fixed)) { + value_length = Math.min(value_length, fixed.print_to_string().length); } - if (self.body[i] instanceof AST_EmptyStatement) { - self.body.splice(i, 1); - continue; + var name_length = def.name.length; + if (compressor.option("unused") && !compressor.exposed(def)) { + var refs = def.references.length - def.replaced - def.assignments; + refs = Math.min(refs, def.references.filter(function(ref) { + return ref.fixed === state; + }).length); + name_length += (name_length + 2 + value_length) / Math.max(1, refs); } - if (self.body[i] instanceof AST_BlockStatement) { - var tmp = [ i, 1 ].concat(self.body[i].body); - self.body.splice.apply(self.body, tmp); - continue; + state.should_replace = value_length - Math.floor(name_length) < compressor.eval_threshold; + } + if (state.should_replace) { + var value; + if (has_symbol_ref(fixed)) { + value = init.optimize(compressor); + if (value === init) value = value.clone(true); + } else { + value = best_of_expression(init.optimize(compressor), fixed); + if (value === init || value === fixed) value = value.clone(true); } - break; + def.replaced++; + return value; } - defs = make_node(AST_Var, self, { - definitions: defs - }); - hoisted.push(defs); - }; + } } - self.body = dirs.concat(hoisted, self.body); } return self; - }); - OPT(AST_SimpleStatement, function(self, compressor){ - if (compressor.option("side_effects")) { - if (!self.body.has_side_effects(compressor)) { - compressor.warn("Dropping side-effect-free statement [{file}:{line},{col}]", self.start); - return make_node(AST_EmptyStatement, self); + function cross_class(def) { + var scope = self.scope; + while (scope !== def.scope) { + if (scope instanceof AST_Class) return true; + scope = scope.parent_scope; } } - return self; - }); - OPT(AST_DWLoop, function(self, compressor){ - var cond = self.condition.evaluate(compressor); - self.condition = cond[0]; - if (!compressor.option("loops")) return self; - if (cond.length > 1) { - if (cond[1]) { - return make_node(AST_For, self, { - body: self.body - }); - } else if (self instanceof AST_While) { - if (compressor.option("dead_code")) { - var a = []; - extract_declarations_from_unreachable_code(compressor, self.body, a); - return make_node(AST_BlockStatement, self, { body: a }); - } - } + function has_symbol_ref(value) { + var found; + value.walk(new TreeWalker(function(node) { + if (node instanceof AST_SymbolRef) found = true; + if (found) return true; + })); + return found; } - return self; }); - function if_break_in_loop(self, compressor) { - function drop_it(rest) { - rest = as_statement_array(rest); - if (self.body instanceof AST_BlockStatement) { - self.body = self.body.clone(); - self.body.body = rest.concat(self.body.body.slice(1)); - self.body = self.body.transform(compressor); - } else { - self.body = make_node(AST_BlockStatement, self.body, { - body: rest - }).transform(compressor); - } - if_break_in_loop(self, compressor); - } - var first = self.body instanceof AST_BlockStatement ? self.body.body[0] : self.body; - if (first instanceof AST_If) { - if (first.body instanceof AST_Break - && compressor.loopcontrol_target(first.body.label) === self) { - if (self.condition) { - self.condition = make_node(AST_Binary, self.condition, { - left: self.condition, - operator: "&&", - right: first.condition.negate(compressor), - }); - } else { - self.condition = first.condition.negate(compressor); - } - drop_it(first.alternative); - } - else if (first.alternative instanceof AST_Break - && compressor.loopcontrol_target(first.alternative.label) === self) { - if (self.condition) { - self.condition = make_node(AST_Binary, self.condition, { - left: self.condition, - operator: "&&", - right: first.condition, - }); - } else { - self.condition = first.condition; - } - drop_it(first.body); - } - } - }; + function is_raw_tag(compressor, tag) { + return compressor.option("unsafe") + && tag instanceof AST_Dot + && tag.property == "raw" + && is_undeclared_ref(tag.expression) + && tag.expression.name == "String"; + } - OPT(AST_While, function(self, compressor) { - if (!compressor.option("loops")) return self; - self = AST_DWLoop.prototype.optimize.call(self, compressor); - if (self instanceof AST_While) { - if_break_in_loop(self, compressor); - self = make_node(AST_For, self, self).transform(compressor); - } - return self; - }); + function decode_template(str) { + var malformed = false; + str = str.replace(/\\(u\{[^{}]*\}?|u[\s\S]{0,4}|x[\s\S]{0,2}|[0-9]+|[\s\S])/g, function(match, seq) { + var ch = decode_escape_sequence(seq); + if (typeof ch == "string") return ch; + malformed = true; + }); + if (!malformed) return str; + } - OPT(AST_For, function(self, compressor){ - var cond = self.condition; - if (cond) { - cond = cond.evaluate(compressor); - self.condition = cond[0]; - } - if (!compressor.option("loops")) return self; - if (cond) { - if (cond.length > 1 && !cond[1]) { - if (compressor.option("dead_code")) { - var a = []; - if (self.init instanceof AST_Statement) { - a.push(self.init); - } - else if (self.init) { - a.push(make_node(AST_SimpleStatement, self.init, { - body: self.init - })); - } - extract_declarations_from_unreachable_code(compressor, self.body, a); - return make_node(AST_BlockStatement, self, { body: a }); + OPT(AST_Template, function(self, compressor) { + if (!compressor.option("templates")) return self; + var tag = self.tag; + if (!tag || is_raw_tag(compressor, tag)) { + var exprs = []; + var strs = []; + for (var i = 0, status; i < self.strings.length; i++) { + var str = self.strings[i]; + if (!tag) { + var trimmed = decode_template(str); + if (trimmed) str = escape_literal(trimmed); } - } - } - if_break_in_loop(self, compressor); - return self; - }); - - OPT(AST_If, function(self, compressor){ - if (!compressor.option("conditionals")) return self; - // if condition can be statically determined, warn and drop - // one of the blocks. note, statically determined implies - // “has no side effects”; also it doesn't work for cases like - // `x && true`, though it probably should. - var cond = self.condition.evaluate(compressor); - self.condition = cond[0]; - if (cond.length > 1) { - if (cond[1]) { - compressor.warn("Condition always true [{file}:{line},{col}]", self.condition.start); - if (compressor.option("dead_code")) { - var a = []; - if (self.alternative) { - extract_declarations_from_unreachable_code(compressor, self.alternative, a); + if (i > 0) { + var node = self.expressions[i - 1]; + var value = should_join(node); + if (value) { + var prev = strs[strs.length - 1]; + var joined = prev + value + str; + var decoded; + if (tag || typeof (decoded = decode_template(joined)) == status) { + strs[strs.length - 1] = decoded ? escape_literal(decoded) : joined; + continue; + } } - a.push(self.body); - return make_node(AST_BlockStatement, self, { body: a }).transform(compressor); - } - } else { - compressor.warn("Condition always false [{file}:{line},{col}]", self.condition.start); - if (compressor.option("dead_code")) { - var a = []; - extract_declarations_from_unreachable_code(compressor, self.body, a); - if (self.alternative) a.push(self.alternative); - return make_node(AST_BlockStatement, self, { body: a }).transform(compressor); - } - } - } - if (is_empty(self.alternative)) self.alternative = null; - var negated = self.condition.negate(compressor); - var negated_is_best = best_of(self.condition, negated) === negated; - if (self.alternative && negated_is_best) { - negated_is_best = false; // because we already do the switch here. - self.condition = negated; - var tmp = self.body; - self.body = self.alternative || make_node(AST_EmptyStatement); - self.alternative = tmp; - } - if (is_empty(self.body) && is_empty(self.alternative)) { - return make_node(AST_SimpleStatement, self.condition, { - body: self.condition - }).transform(compressor); - } - if (self.body instanceof AST_SimpleStatement - && self.alternative instanceof AST_SimpleStatement) { - return make_node(AST_SimpleStatement, self, { - body: make_node(AST_Conditional, self, { - condition : self.condition, - consequent : self.body.body, - alternative : self.alternative.body - }) - }).transform(compressor); - } - if (is_empty(self.alternative) && self.body instanceof AST_SimpleStatement) { - if (negated_is_best) return make_node(AST_SimpleStatement, self, { - body: make_node(AST_Binary, self, { - operator : "||", - left : negated, - right : self.body.body - }) - }).transform(compressor); - return make_node(AST_SimpleStatement, self, { - body: make_node(AST_Binary, self, { - operator : "&&", - left : self.condition, - right : self.body.body - }) - }).transform(compressor); - } - if (self.body instanceof AST_EmptyStatement - && self.alternative - && self.alternative instanceof AST_SimpleStatement) { - return make_node(AST_SimpleStatement, self, { - body: make_node(AST_Binary, self, { - operator : "||", - left : self.condition, - right : self.alternative.body - }) - }).transform(compressor); - } - if (self.body instanceof AST_Exit - && self.alternative instanceof AST_Exit - && self.body.TYPE == self.alternative.TYPE) { - return make_node(self.body.CTOR, self, { - value: make_node(AST_Conditional, self, { - condition : self.condition, - consequent : self.body.value || make_node(AST_Undefined, self.body).optimize(compressor), - alternative : self.alternative.value || make_node(AST_Undefined, self.alternative).optimize(compressor) - }) - }).transform(compressor); - } - if (self.body instanceof AST_If - && !self.body.alternative - && !self.alternative) { - self.condition = make_node(AST_Binary, self.condition, { - operator: "&&", - left: self.condition, - right: self.body.condition - }).transform(compressor); - self.body = self.body.body; - } - if (aborts(self.body)) { - if (self.alternative) { - var alt = self.alternative; - self.alternative = null; - return make_node(AST_BlockStatement, self, { - body: [ self, alt ] - }).transform(compressor); - } - } - if (aborts(self.alternative)) { - var body = self.body; - self.body = self.alternative; - self.condition = negated_is_best ? negated : self.condition.negate(compressor); - self.alternative = null; - return make_node(AST_BlockStatement, self, { - body: [ self, body ] - }).transform(compressor); - } - return self; - }); - - OPT(AST_Switch, function(self, compressor){ - if (self.body.length == 0 && compressor.option("conditionals")) { - return make_node(AST_SimpleStatement, self, { - body: self.expression - }).transform(compressor); - } - for(;;) { - var last_branch = self.body[self.body.length - 1]; - if (last_branch) { - var stat = last_branch.body[last_branch.body.length - 1]; // last statement - if (stat instanceof AST_Break && loop_body(compressor.loopcontrol_target(stat.label)) === self) - last_branch.body.pop(); - if (last_branch instanceof AST_Default && last_branch.body.length == 0) { - self.body.pop(); - continue; + exprs.push(node); } + strs.push(str); + if (!tag) status = typeof trimmed; } - break; - } - var exp = self.expression.evaluate(compressor); - out: if (exp.length == 2) try { - // constant expression - self.expression = exp[0]; - if (!compressor.option("dead_code")) break out; - var value = exp[1]; - var in_if = false; - var in_block = false; - var started = false; - var stopped = false; - var ruined = false; - var tt = new TreeTransformer(function(node, descend, in_list){ - if (node instanceof AST_Lambda || node instanceof AST_SimpleStatement) { - // no need to descend these node types - return node; - } - else if (node instanceof AST_Switch && node === self) { - node = node.clone(); - descend(node, this); - return ruined ? node : make_node(AST_BlockStatement, node, { - body: node.body.reduce(function(a, branch){ - return a.concat(branch.body); - }, []) - }).transform(compressor); - } - else if (node instanceof AST_If || node instanceof AST_Try) { - var save = in_if; - in_if = !in_block; - descend(node, this); - in_if = save; - return node; - } - else if (node instanceof AST_StatementWithBody || node instanceof AST_Switch) { - var save = in_block; - in_block = true; - descend(node, this); - in_block = save; - return node; - } - else if (node instanceof AST_Break && this.loopcontrol_target(node.label) === self) { - if (in_if) { - ruined = true; - return node; - } - if (in_block) return node; - stopped = true; - return in_list ? MAP.skip : make_node(AST_EmptyStatement, node); - } - else if (node instanceof AST_SwitchBranch && this.parent() === self) { - if (stopped) return MAP.skip; - if (node instanceof AST_Case) { - var exp = node.expression.evaluate(compressor); - if (exp.length < 2) { - // got a case with non-constant expression, baling out - throw self; - } - if (exp[1] === value || started) { - started = true; - if (aborts(node)) stopped = true; - descend(node, this); - return node; - } - return MAP.skip; + if (!tag && strs.length > 1) { + if (strs[strs.length - 1] == "") return make_node(AST_Binary, self, { + operator: "+", + left: make_node(AST_Template, self, { + expressions: exprs.slice(0, -1), + strings: strs.slice(0, -1), + }).transform(compressor), + right: exprs[exprs.length - 1], + }).optimize(compressor); + if (strs[0] == "") { + var left = make_node(AST_Binary, self, { + operator: "+", + left: make_node(AST_String, self, { value: "" }), + right: exprs[0], + }); + for (var i = 1; strs[i] == "" && i < exprs.length; i++) { + left = make_node(AST_Binary, self, { + operator: "+", + left: left, + right: exprs[i], + }); } - descend(node, this); - return node; + return best_of(compressor, self, make_node(AST_Binary, self, { + operator: "+", + left: left.transform(compressor), + right: make_node(AST_Template, self, { + expressions: exprs.slice(i), + strings: strs.slice(i), + }).transform(compressor), + }).optimize(compressor)); } + } + self.expressions = exprs; + self.strings = strs; + } + return try_evaluate(compressor, self); + + function escape_literal(str) { + return str.replace(/\r|\\|`|\${/g, function(s) { + return "\\" + (s == "\r" ? "r" : s); }); - tt.stack = compressor.stack.slice(); // so that's able to see parent nodes - self = self.transform(tt); - } catch(ex) { - if (ex !== self) throw ex; } - return self; - }); - OPT(AST_Case, function(self, compressor){ - self.body = tighten_body(self.body, compressor); - return self; + function should_join(node) { + var ev = node.evaluate(compressor); + if (ev === node) return; + if (tag && /\r|\\|`/.test(ev)) return; + ev = escape_literal("" + ev); + if (ev.length > node.print_to_string().length + "${}".length) return; + return ev; + } }); - OPT(AST_Try, function(self, compressor){ - self.body = tighten_body(self.body, compressor); - return self; - }); + function is_atomic(lhs, self) { + return lhs instanceof AST_SymbolRef || lhs.TYPE === self.TYPE; + } - AST_Definitions.DEFMETHOD("remove_initializers", function(){ - this.definitions.forEach(function(def){ def.value = null }); + OPT(AST_Undefined, function(self, compressor) { + if (compressor.option("unsafe_undefined")) { + var undef = find_scope(compressor).find_variable("undefined"); + if (undef) { + var ref = make_node(AST_SymbolRef, self, { + name: "undefined", + scope: undef.scope, + thedef: undef, + }); + ref.is_undefined = true; + return ref; + } + } + var lhs = is_lhs(compressor.self(), compressor.parent()); + if (lhs && is_atomic(lhs, self)) return self; + return make_node(AST_UnaryPrefix, self, { + operator: "void", + expression: make_node(AST_Number, self, { value: 0 }), + }); }); - AST_Definitions.DEFMETHOD("to_assignments", function(){ - var assignments = this.definitions.reduce(function(a, def){ - if (def.value) { - var name = make_node(AST_SymbolRef, def.name, def.name); - a.push(make_node(AST_Assign, def, { - operator : "=", - left : name, - right : def.value - })); - } - return a; - }, []); - if (assignments.length == 0) return null; - return AST_Seq.from_array(assignments); + OPT(AST_Infinity, function(self, compressor) { + var lhs = is_lhs(compressor.self(), compressor.parent()); + if (lhs && is_atomic(lhs, self)) return self; + if (compressor.option("keep_infinity") && !lhs && !find_scope(compressor).find_variable("Infinity")) { + return self; + } + return make_node(AST_Binary, self, { + operator: "/", + left: make_node(AST_Number, self, { value: 1 }), + right: make_node(AST_Number, self, { value: 0 }), + }); }); - OPT(AST_Definitions, function(self, compressor){ - if (self.definitions.length == 0) - return make_node(AST_EmptyStatement, self); - return self; + OPT(AST_NaN, function(self, compressor) { + var lhs = is_lhs(compressor.self(), compressor.parent()); + if (lhs && is_atomic(lhs, self)) return self; + if (!lhs && !find_scope(compressor).find_variable("NaN")) return self; + return make_node(AST_Binary, self, { + operator: "/", + left: make_node(AST_Number, self, { value: 0 }), + right: make_node(AST_Number, self, { value: 0 }), + }); }); - OPT(AST_Function, function(self, compressor){ - self = AST_Lambda.prototype.optimize.call(self, compressor); - if (compressor.option("unused") && !compressor.option("keep_fnames")) { - if (self.name && self.name.unreferenced()) { - self.name = null; + function is_reachable(self, defs) { + var reachable = false; + var find_ref = new TreeWalker(function(node) { + if (reachable) return true; + if (node instanceof AST_SymbolRef && member(node.definition(), defs)) return reachable = true; + }); + var scan_scope = new TreeWalker(function(node) { + if (reachable) return true; + if (node instanceof AST_Lambda && node !== self) { + if (!(node.name || is_async(node) || is_generator(node))) { + var parent = scan_scope.parent(); + if (parent instanceof AST_Call && parent.expression === node) return; + } + node.walk(find_ref); + return true; } - } - return self; - }); + }); + self.walk(scan_scope); + return reachable; + } - OPT(AST_Call, function(self, compressor){ - if (compressor.option("unsafe")) { - var exp = self.expression; - if (exp instanceof AST_SymbolRef && exp.undeclared()) { - switch (exp.name) { - case "Array": - if (self.args.length != 1) { - return make_node(AST_Array, self, { - elements: self.args - }).transform(compressor); + var ASSIGN_OPS = makePredicate("+ - * / % >> << >>> | ^ &"); + var ASSIGN_OPS_COMMUTATIVE = makePredicate("* | ^ &"); + OPT(AST_Assign, function(self, compressor) { + if (compressor.option("dead_code")) { + if (self.left instanceof AST_PropAccess) { + if (self.operator == "=") { + if (self.redundant) { + var exprs = [ self.left.expression ]; + if (self.left instanceof AST_Sub) exprs.push(self.left.property); + exprs.push(self.right); + return make_sequence(self, exprs).optimize(compressor); } - break; - case "Object": - if (self.args.length == 0) { - return make_node(AST_Object, self, { - properties: [] - }); + if (self.left.equals(self.right) && !self.left.has_side_effects(compressor)) { + return self.right; } - break; - case "String": - if (self.args.length == 0) return make_node(AST_String, self, { - value: "" - }); - if (self.args.length <= 1) return make_node(AST_Binary, self, { - left: self.args[0], - operator: "+", - right: make_node(AST_String, self, { value: "" }) - }).transform(compressor); - break; - case "Number": - if (self.args.length == 0) return make_node(AST_Number, self, { - value: 0 - }); - if (self.args.length == 1) return make_node(AST_UnaryPrefix, self, { - expression: self.args[0], - operator: "+" - }).transform(compressor); - case "Boolean": - if (self.args.length == 0) return make_node(AST_False, self); - if (self.args.length == 1) return make_node(AST_UnaryPrefix, self, { - expression: make_node(AST_UnaryPrefix, null, { - expression: self.args[0], - operator: "!" - }), - operator: "!" - }).transform(compressor); - break; - case "Function": - // new Function() => function(){} - if (self.args.length == 0) return make_node(AST_Function, self, { - argnames: [], - body: [] - }); - if (all(self.args, function(x){ return x instanceof AST_String })) { - // quite a corner-case, but we can handle it: - // https://github.com/mishoo/UglifyJS2/issues/203 - // if the code argument is a constant, then we can minify it. - try { - var code = "(function(" + self.args.slice(0, -1).map(function(arg){ - return arg.value; - }).join(",") + "){" + self.args[self.args.length - 1].value + "})()"; - var ast = parse(code); - ast.figure_out_scope({ screw_ie8: compressor.option("screw_ie8") }); - var comp = new Compressor(compressor.options); - ast = ast.transform(comp); - ast.figure_out_scope({ screw_ie8: compressor.option("screw_ie8") }); - ast.mangle_names(); - var fun; - try { - ast.walk(new TreeWalker(function(node){ - if (node instanceof AST_Lambda) { - fun = node; - throw ast; - } - })); - } catch(ex) { - if (ex !== ast) throw ex; - }; - if (!fun) return self; - var args = fun.argnames.map(function(arg, i){ - return make_node(AST_String, self.args[i], { - value: arg.print_to_string() - }); - }); - var code = OutputStream(); - AST_BlockStatement.prototype._codegen.call(fun, fun, code); - code = code.toString().replace(/^\{|\}$/g, ""); - args.push(make_node(AST_String, self.args[self.args.length - 1], { - value: code - })); - self.args = args; - return self; - } catch(ex) { - if (ex instanceof JS_Parse_Error) { - compressor.warn("Error parsing code passed to new Function [{file}:{line},{col}]", self.args[self.args.length - 1].start); - compressor.warn(ex.toString()); - } else { - console.log(ex); - throw ex; - } - } + var exp = self.left.expression; + if (exp instanceof AST_Lambda + || !compressor.has_directive("use strict") + && exp instanceof AST_Constant + && !exp.may_throw_on_access(compressor)) { + return self.left instanceof AST_Dot ? self.right : make_sequence(self, [ + self.left.property, + self.right + ]).optimize(compressor); } - break; } - } - else if (exp instanceof AST_Dot && exp.property == "toString" && self.args.length == 0) { - return make_node(AST_Binary, self, { - left: make_node(AST_String, self, { value: "" }), - operator: "+", - right: exp.expression - }).transform(compressor); - } - else if (exp instanceof AST_Dot && exp.expression instanceof AST_Array && exp.property == "join") EXIT: { - var separator = self.args.length == 0 ? "," : self.args[0].evaluate(compressor)[1]; - if (separator == null) break EXIT; // not a constant - var elements = exp.expression.elements.reduce(function(a, el){ - el = el.evaluate(compressor); - if (a.length == 0 || el.length == 1) { - a.push(el); - } else { - var last = a[a.length - 1]; - if (last.length == 2) { - // it's a constant - var val = "" + last[1] + separator + el[1]; - a[a.length - 1] = [ make_node_from_constant(compressor, val, last[0]), val ]; - } else { - a.push(el); - } - } - return a; - }, []); - if (elements.length == 0) return make_node(AST_String, self, { value: "" }); - if (elements.length == 1) return elements[0][0]; - if (separator == "") { - var first; - if (elements[0][0] instanceof AST_String - || elements[1][0] instanceof AST_String) { - first = elements.shift()[0]; - } else { - first = make_node(AST_String, self, { value: "" }); + } else if (self.left instanceof AST_SymbolRef && can_drop_symbol(self.left, compressor)) { + var parent; + if (self.operator == "=" && self.left.equals(self.right) + && !((parent = compressor.parent()) instanceof AST_UnaryPrefix && parent.operator == "delete")) { + return self.right; + } + if (self.left.is_immutable()) return strip_assignment(); + var def = self.left.definition(); + var scope = def.scope.resolve(); + var local = scope === compressor.find_parent(AST_Lambda); + var level = 0, node; + parent = compressor.self(); + if (!(scope.uses_arguments && is_funarg(def)) || compressor.has_directive("use strict")) do { + node = parent; + parent = compressor.parent(level++); + if (parent instanceof AST_Assign) { + if (parent.left instanceof AST_SymbolRef && parent.left.definition() === def) { + if (in_try(level, parent, !local)) break; + return strip_assignment(def); + } + if (parent.left.match_symbol(function(node) { + if (node instanceof AST_PropAccess) return true; + })) break; + continue; } - return elements.reduce(function(prev, el){ - return make_node(AST_Binary, el[0], { - operator : "+", - left : prev, - right : el[0], - }); - }, first).transform(compressor); + if (parent instanceof AST_Exit) { + if (!local) break; + if (in_try(level, parent)) break; + if (is_reachable(scope, [ def ])) break; + return strip_assignment(def); + } + if (parent instanceof AST_SimpleStatement) { + if (!local) break; + if (is_reachable(scope, [ def ])) break; + var stat; + do { + stat = parent; + parent = compressor.parent(level++); + if (parent === scope && is_last_statement(parent.body, stat)) return strip_assignment(def); + } while (is_tail_block(stat, parent)); + break; + } + if (parent instanceof AST_VarDef) { + if (!(parent.name instanceof AST_SymbolDeclaration)) continue; + if (parent.name.definition() !== def) continue; + if (in_try(level, parent)) break; + return strip_assignment(def); + } + } while (is_tail(node, parent)); + } + } + if (compressor.option("sequences")) { + var seq = self.lift_sequences(compressor); + if (seq !== self) return seq.optimize(compressor); + } + if (compressor.option("assignments")) { + if (self.operator == "=" && self.left instanceof AST_SymbolRef && self.right instanceof AST_Binary) { + // x = expr1 OP expr2 + if (self.right.left instanceof AST_SymbolRef + && self.right.left.name == self.left.name + && ASSIGN_OPS[self.right.operator]) { + // x = x - 2 ---> x -= 2 + return make_compound(self.right.right); + } + if (self.right.right instanceof AST_SymbolRef + && self.right.right.name == self.left.name + && ASSIGN_OPS_COMMUTATIVE[self.right.operator] + && !self.right.left.has_side_effects(compressor)) { + // x = 2 & x ---> x &= 2 + return make_compound(self.right.left); } - // need this awkward cloning to not affect original element - // best_of will decide which one to get through. - var node = self.clone(); - node.expression = node.expression.clone(); - node.expression.expression = node.expression.expression.clone(); - node.expression.expression.elements = elements.map(function(el){ - return el[0]; + } + if ((self.operator == "-=" || self.operator == "+=" + && (self.left.is_boolean(compressor) || self.left.is_number(compressor))) + && self.right instanceof AST_Number + && self.right.value == 1) { + var op = self.operator.slice(0, -1); + return make_node(AST_UnaryPrefix, self, { + operator: op + op, + expression: self.left, }); - return best_of(self, node); } } - if (compressor.option("side_effects")) { - if (self.expression instanceof AST_Function - && self.args.length == 0 - && !AST_Block.prototype.has_side_effects.call(self.expression, compressor)) { - return make_node(AST_Undefined, self).transform(compressor); + return try_evaluate(compressor, self); + + function is_tail(node, parent) { + if (parent instanceof AST_Binary) switch (node) { + case parent.left: + return parent.right.is_constant_expression(scope); + case parent.right: + return true; + default: + return false; } - } - if (compressor.option("drop_console")) { - if (self.expression instanceof AST_PropAccess && - self.expression.expression instanceof AST_SymbolRef && - self.expression.expression.name == "console" && - self.expression.expression.undeclared()) { - return make_node(AST_Undefined, self).transform(compressor); + if (parent instanceof AST_Conditional) switch (node) { + case parent.condition: + return parent.consequent.is_constant_expression(scope) + && parent.alternative.is_constant_expression(scope); + case parent.consequent: + case parent.alternative: + return true; + default: + return false; + } + if (parent instanceof AST_Sequence) { + var exprs = parent.expressions; + var stop = exprs.indexOf(node); + if (stop < 0) return false; + for (var i = exprs.length; --i > stop;) { + if (!exprs[i].is_constant_expression(scope)) return false; + } + return true; } + return parent instanceof AST_UnaryPrefix; + } + + function is_tail_block(stat, parent) { + if (parent instanceof AST_BlockStatement) return is_last_statement(parent.body, stat); + if (parent instanceof AST_Catch) return is_last_statement(parent.body, stat); + if (parent instanceof AST_Finally) return is_last_statement(parent.body, stat); + if (parent instanceof AST_If) return parent.body === stat || parent.alternative === stat; + if (parent instanceof AST_Try) return parent.bfinally ? parent.bfinally === stat : parent.bcatch === stat; + } + + function in_try(level, node, sync) { + var right = self.right; + self.right = make_node(AST_Null, right); + var may_throw = node.may_throw(compressor); + self.right = right; + return find_try(compressor, level, node, scope, may_throw, sync); + } + + function make_compound(rhs) { + var fixed = self.left.fixed; + if (fixed) fixed.to_binary = replace_ref(function(node) { + return node.left; + }, fixed); + return make_node(AST_Assign, self, { + operator: self.right.operator + "=", + left: self.left, + right: rhs, + }); + } + + function strip_assignment(def) { + if (def) def.fixed = false; + return (self.operator != "=" ? make_node(AST_Binary, self, { + operator: self.operator.slice(0, -1), + left: self.left, + right: self.right, + }) : maintain_this_binding(compressor.parent(), self, self.right)).optimize(compressor); } - return self.evaluate(compressor)[0]; }); - OPT(AST_New, function(self, compressor){ - if (compressor.option("unsafe")) { - var exp = self.expression; - if (exp instanceof AST_SymbolRef && exp.undeclared()) { - switch (exp.name) { - case "Object": - case "RegExp": - case "Function": - case "Error": - case "Array": - return make_node(AST_Call, self, self).transform(compressor); - } + OPT(AST_Conditional, function(self, compressor) { + if (compressor.option("sequences") && self.condition instanceof AST_Sequence) { + var expressions = self.condition.expressions.slice(); + var node = self.clone(); + node.condition = expressions.pop(); + expressions.push(node); + return make_sequence(self, expressions).optimize(compressor); + } + if (!compressor.option("conditionals")) return self; + var condition = self.condition; + if (compressor.option("booleans") && !condition.has_side_effects(compressor)) { + mark_duplicate_condition(compressor, condition); + } + condition = fuzzy_eval(compressor, condition); + if (!condition) { + AST_Node.warn("Condition always false [{start}]", self); + return make_sequence(self, [ self.condition, self.alternative ]).optimize(compressor); + } else if (!(condition instanceof AST_Node)) { + AST_Node.warn("Condition always true [{start}]", self); + return make_sequence(self, [ self.condition, self.consequent ]).optimize(compressor); + } + var first = first_in_statement(compressor); + var negated = condition.negate(compressor, first); + if ((first ? best_of_statement : best_of_expression)(condition, negated) === negated) { + self = make_node(AST_Conditional, self, { + condition: negated, + consequent: self.alternative, + alternative: self.consequent, + }); + negated = condition; + condition = self.condition; + } + var consequent = self.consequent; + var alternative = self.alternative; + var cond_lhs = extract_lhs(condition, compressor); + if (repeatable(compressor, cond_lhs)) { + // x ? x : y ---> x || y + if (cond_lhs.equals(consequent)) return make_node(AST_Binary, self, { + operator: "||", + left: condition, + right: alternative, + }).optimize(compressor); + // x ? y : x ---> x && y + if (cond_lhs.equals(alternative)) return make_node(AST_Binary, self, { + operator: "&&", + left: condition, + right: consequent, + }).optimize(compressor); + } + // if (foo) exp = something; else exp = something_else; + // | + // v + // exp = foo ? something : something_else; + var seq_tail = consequent.tail_node(); + if (seq_tail instanceof AST_Assign) { + var is_eq = seq_tail.operator == "="; + var alt_tail = is_eq ? alternative.tail_node() : alternative; + if ((is_eq || consequent === seq_tail) + && alt_tail instanceof AST_Assign + && seq_tail.operator == alt_tail.operator + && seq_tail.left.equals(alt_tail.left) + && (is_eq && seq_tail.left instanceof AST_SymbolRef + || !condition.has_side_effects(compressor) + && can_shift_lhs_of_tail(consequent) + && can_shift_lhs_of_tail(alternative))) { + return make_node(AST_Assign, self, { + operator: seq_tail.operator, + left: seq_tail.left, + right: make_node(AST_Conditional, self, { + condition: condition, + consequent: pop_lhs(consequent), + alternative: pop_lhs(alternative), + }), + }); } } + var alt_tail = alternative.tail_node(); + // x ? y : y ---> x, y + // x ? (a, c) : (b, c) ---> x ? a : b, c + if (seq_tail.equals(alt_tail)) return make_sequence(self, consequent.equals(alternative) ? [ + condition, + consequent, + ] : [ + make_node(AST_Conditional, self, { + condition: condition, + consequent: pop_seq(consequent), + alternative: pop_seq(alternative), + }), + alt_tail, + ]).optimize(compressor); + // x ? y.p : z.p ---> (x ? y : z).p + // x ? y(a) : z(a) ---> (x ? y : z)(a) + // x ? y.f(a) : z.f(a) ---> (x ? y : z).f(a) + var combined = combine_tail(consequent, alternative, true); + if (combined) return combined; + // x ? y(a) : y(b) ---> y(x ? a : b) + var arg_index; + if (consequent instanceof AST_Call + && alternative.TYPE == consequent.TYPE + && (arg_index = arg_diff(consequent, alternative)) >= 0 + && consequent.expression.equals(alternative.expression) + && !condition.has_side_effects(compressor) + && !consequent.expression.has_side_effects(compressor)) { + var node = consequent.clone(); + var arg = consequent.args[arg_index]; + node.args[arg_index] = arg instanceof AST_Spread ? make_node(AST_Spread, self, { + expression: make_node(AST_Conditional, self, { + condition: condition, + consequent: arg.expression, + alternative: alternative.args[arg_index].expression, + }), + }) : make_node(AST_Conditional, self, { + condition: condition, + consequent: arg, + alternative: alternative.args[arg_index], + }); + return node; + } + // x ? (y ? a : b) : b ---> x && y ? a : b + if (seq_tail instanceof AST_Conditional + && seq_tail.alternative.equals(alternative)) { + return make_node(AST_Conditional, self, { + condition: make_node(AST_Binary, self, { + left: condition, + operator: "&&", + right: fuse(consequent, seq_tail, "condition"), + }), + consequent: seq_tail.consequent, + alternative: merge_expression(seq_tail.alternative, alternative), + }); + } + // x ? (y ? a : b) : a ---> !x || y ? a : b + if (seq_tail instanceof AST_Conditional + && seq_tail.consequent.equals(alternative)) { + return make_node(AST_Conditional, self, { + condition: make_node(AST_Binary, self, { + left: negated, + operator: "||", + right: fuse(consequent, seq_tail, "condition"), + }), + consequent: merge_expression(seq_tail.consequent, alternative), + alternative: seq_tail.alternative, + }); + } + // x ? a : (y ? a : b) ---> x || y ? a : b + if (alt_tail instanceof AST_Conditional + && consequent.equals(alt_tail.consequent)) { + return make_node(AST_Conditional, self, { + condition: make_node(AST_Binary, self, { + left: condition, + operator: "||", + right: fuse(alternative, alt_tail, "condition"), + }), + consequent: merge_expression(consequent, alt_tail.consequent), + alternative: alt_tail.alternative, + }); + } + // x ? b : (y ? a : b) ---> !x && y ? a : b + if (alt_tail instanceof AST_Conditional + && consequent.equals(alt_tail.alternative)) { + return make_node(AST_Conditional, self, { + condition: make_node(AST_Binary, self, { + left: negated, + operator: "&&", + right: fuse(alternative, alt_tail, "condition"), + }), + consequent: alt_tail.consequent, + alternative: merge_expression(consequent, alt_tail.alternative), + }); + } + // x ? y && a : a ---> (!x || y) && a + if (seq_tail instanceof AST_Binary + && seq_tail.operator == "&&" + && seq_tail.right.equals(alternative)) { + return make_node(AST_Binary, self, { + operator: "&&", + left: make_node(AST_Binary, self, { + operator: "||", + left: negated, + right: fuse(consequent, seq_tail, "left"), + }), + right: merge_expression(seq_tail.right, alternative), + }).optimize(compressor); + } + // x ? y || a : a ---> x && y || a + if (seq_tail instanceof AST_Binary + && seq_tail.operator == "||" + && seq_tail.right.equals(alternative)) { + return make_node(AST_Binary, self, { + operator: "||", + left: make_node(AST_Binary, self, { + operator: "&&", + left: condition, + right: fuse(consequent, seq_tail, "left"), + }), + right: merge_expression(seq_tail.right, alternative), + }).optimize(compressor); + } + // x ? a : y && a ---> (x || y) && a + if (alt_tail instanceof AST_Binary + && alt_tail.operator == "&&" + && alt_tail.right.equals(consequent)) { + return make_node(AST_Binary, self, { + operator: "&&", + left: make_node(AST_Binary, self, { + operator: "||", + left: condition, + right: fuse(alternative, alt_tail, "left"), + }), + right: merge_expression(consequent, alt_tail.right), + }).optimize(compressor); + } + // x ? a : y || a ---> !x && y || a + if (alt_tail instanceof AST_Binary + && alt_tail.operator == "||" + && alt_tail.right.equals(consequent)) { + return make_node(AST_Binary, self, { + operator: "||", + left: make_node(AST_Binary, self, { + operator: "&&", + left: negated, + right: fuse(alternative, alt_tail, "left"), + }), + right: merge_expression(consequent, alt_tail.right), + }).optimize(compressor); + } + var in_bool = compressor.option("booleans") && compressor.in_boolean_context(); + if (is_true(consequent)) { + // c ? true : false ---> !!c + if (is_false(alternative)) return booleanize(condition); + // c ? true : x ---> !!c || x + return make_node(AST_Binary, self, { + operator: "||", + left: booleanize(condition), + right: alternative, + }).optimize(compressor); + } + if (is_false(consequent)) { + // c ? false : true ---> !c + if (is_true(alternative)) return booleanize(condition.negate(compressor)); + // c ? false : x ---> !c && x + return make_node(AST_Binary, self, { + operator: "&&", + left: booleanize(condition.negate(compressor)), + right: alternative, + }).optimize(compressor); + } + // c ? x : true ---> !c || x + if (is_true(alternative)) return make_node(AST_Binary, self, { + operator: "||", + left: booleanize(condition.negate(compressor)), + right: consequent, + }).optimize(compressor); + // c ? x : false ---> !!c && x + if (is_false(alternative)) return make_node(AST_Binary, self, { + operator: "&&", + left: booleanize(condition), + right: consequent, + }).optimize(compressor); + if (compressor.option("typeofs")) mark_locally_defined(condition, consequent, alternative); return self; - }); - OPT(AST_Seq, function(self, compressor){ - if (!compressor.option("side_effects")) - return self; - if (!self.car.has_side_effects(compressor)) { - // we shouldn't compress (1,eval)(something) to - // eval(something) because that changes the meaning of - // eval (becomes lexical instead of global). - var p; - if (!(self.cdr instanceof AST_SymbolRef - && self.cdr.name == "eval" - && self.cdr.undeclared() - && (p = compressor.parent()) instanceof AST_Call - && p.expression === self)) { - return self.cdr; - } - } - if (compressor.option("cascade")) { - if (self.car instanceof AST_Assign - && !self.car.left.has_side_effects(compressor)) { - if (self.car.left.equivalent_to(self.cdr)) { - return self.car; - } - if (self.cdr instanceof AST_Call - && self.cdr.expression.equivalent_to(self.car.left)) { - self.cdr.expression = self.car; - return self.cdr; - } - } - if (!self.car.has_side_effects(compressor) - && !self.cdr.has_side_effects(compressor) - && self.car.equivalent_to(self.cdr)) { - return self.car; - } - } - if (self.cdr instanceof AST_UnaryPrefix - && self.cdr.operator == "void" - && !self.cdr.expression.has_side_effects(compressor)) { - self.cdr.expression = self.car; - return self.cdr; - } - if (self.cdr instanceof AST_Undefined) { - return make_node(AST_UnaryPrefix, self, { - operator : "void", - expression : self.car + function booleanize(node) { + if (node.is_boolean(compressor)) return node; + // !!expression + return make_node(AST_UnaryPrefix, node, { + operator: "!", + expression: node.negate(compressor), }); } - return self; - }); - AST_Unary.DEFMETHOD("lift_sequences", function(compressor){ - if (compressor.option("sequences")) { - if (this.expression instanceof AST_Seq) { - var seq = this.expression; - var x = seq.to_array(); - this.expression = x.pop(); - x.push(this); - seq = AST_Seq.from_array(x).transform(compressor); - return seq; + // AST_True or !0 + function is_true(node) { + return node instanceof AST_True + || in_bool + && node instanceof AST_Constant + && node.value + || (node instanceof AST_UnaryPrefix + && node.operator == "!" + && node.expression instanceof AST_Constant + && !node.expression.value); + } + // AST_False or !1 or void 0 + function is_false(node) { + return node instanceof AST_False + || in_bool + && (node instanceof AST_Constant + && !node.value + || node instanceof AST_UnaryPrefix + && node.operator == "void" + && !node.expression.has_side_effects(compressor)) + || (node instanceof AST_UnaryPrefix + && node.operator == "!" + && node.expression instanceof AST_Constant + && node.expression.value); + } + + function arg_diff(consequent, alternative) { + var a = consequent.args; + var b = alternative.args; + var len = a.length; + if (len != b.length) return -2; + for (var i = 0; i < len; i++) { + if (!a[i].equals(b[i])) { + if (a[i] instanceof AST_Spread !== b[i] instanceof AST_Spread) return -3; + for (var j = i + 1; j < len; j++) { + if (!a[j].equals(b[j])) return -2; + } + return i; + } } + return -1; } - return this; - }); - OPT(AST_UnaryPostfix, function(self, compressor){ - return self.lift_sequences(compressor); - }); + function fuse(node, tail, prop) { + if (node === tail) return tail[prop]; + var exprs = node.expressions.slice(0, -1); + exprs.push(tail[prop]); + return make_sequence(node, exprs); + } - OPT(AST_UnaryPrefix, function(self, compressor){ - self = self.lift_sequences(compressor); - var e = self.expression; - if (compressor.option("booleans") && compressor.in_boolean_context()) { - switch (self.operator) { - case "!": - if (e instanceof AST_UnaryPrefix && e.operator == "!") { - // !!foo ==> foo, if we're in boolean context - return e.expression; - } - break; - case "typeof": - // typeof always returns a non-empty string, thus it's - // always true in booleans - compressor.warn("Boolean expression always true [{file}:{line},{col}]", self.start); - return make_node(AST_True, self); + function is_tail_equivalent(consequent, alternative) { + if (consequent.TYPE != alternative.TYPE) return; + if (consequent.optional != alternative.optional) return; + if (consequent instanceof AST_Call) { + if (arg_diff(consequent, alternative) != -1) return; + return consequent.TYPE != "Call" + || !(consequent.expression instanceof AST_PropAccess + || alternative.expression instanceof AST_PropAccess) + || is_tail_equivalent(consequent.expression, alternative.expression); } - if (e instanceof AST_Binary && self.operator == "!") { - self = best_of(self, e.negate(compressor)); + if (!(consequent instanceof AST_PropAccess)) return; + var p = consequent.property; + var q = alternative.property; + return (p instanceof AST_Node ? p.equals(q) : p == q) + && !(consequent.expression instanceof AST_Super || alternative.expression instanceof AST_Super); + } + + function combine_tail(consequent, alternative, top) { + var seq_tail = consequent.tail_node(); + var alt_tail = alternative.tail_node(); + if (!is_tail_equivalent(seq_tail, alt_tail)) return !top && make_node(AST_Conditional, self, { + condition: condition, + consequent: consequent, + alternative: alternative, + }); + var node = seq_tail.clone(); + var seq_expr = fuse(consequent, seq_tail, "expression"); + var alt_expr = fuse(alternative, alt_tail, "expression"); + var combined = combine_tail(seq_expr, alt_expr); + if (seq_tail.expression instanceof AST_Sequence) { + combined = maintain_this_binding(seq_tail, seq_tail.expression, combined); } + node.expression = combined; + return node; } - return self.evaluate(compressor)[0]; - }); - function has_side_effects_or_prop_access(node, compressor) { - var save_pure_getters = compressor.option("pure_getters"); - compressor.options.pure_getters = false; - var ret = node.has_side_effects(compressor); - compressor.options.pure_getters = save_pure_getters; - return ret; - } + function can_shift_lhs_of_tail(node) { + return node === node.tail_node() || all(node.expressions.slice(0, -1), function(expr) { + return !expr.has_side_effects(compressor); + }); + } - AST_Binary.DEFMETHOD("lift_sequences", function(compressor){ - if (compressor.option("sequences")) { - if (this.left instanceof AST_Seq) { - var seq = this.left; - var x = seq.to_array(); - this.left = x.pop(); - x.push(this); - seq = AST_Seq.from_array(x).transform(compressor); - return seq; - } - if (this.right instanceof AST_Seq - && this instanceof AST_Assign - && !has_side_effects_or_prop_access(this.left, compressor)) { - var seq = this.right; - var x = seq.to_array(); - this.right = x.pop(); - x.push(this); - seq = AST_Seq.from_array(x).transform(compressor); - return seq; - } + function pop_lhs(node) { + if (!(node instanceof AST_Sequence)) return node.right; + var exprs = node.expressions.slice(); + exprs.push(exprs.pop().right); + return make_sequence(node, exprs); + } + + function pop_seq(node) { + if (!(node instanceof AST_Sequence)) return make_node(AST_Number, node, { value: 0 }); + return make_sequence(node, node.expressions.slice(0, -1)); } - return this; }); - var commutativeOperators = makePredicate("== === != !== * & | ^"); + OPT(AST_Boolean, function(self, compressor) { + if (!compressor.option("booleans")) return self; + if (compressor.in_boolean_context()) return make_node(AST_Number, self, { value: +self.value }); + var p = compressor.parent(); + if (p instanceof AST_Binary && (p.operator == "==" || p.operator == "!=")) { + AST_Node.warn("Non-strict equality against boolean: {operator} {value} [{start}]", { + operator: p.operator, + value: self.value, + start: p.start, + }); + return make_node(AST_Number, self, { value: +self.value }); + } + return make_node(AST_UnaryPrefix, self, { + operator: "!", + expression: make_node(AST_Number, self, { value: 1 - self.value }), + }); + }); - OPT(AST_Binary, function(self, compressor){ - var reverse = compressor.has_directive("use asm") ? noop - : function(op, force) { - if (force || !(self.left.has_side_effects(compressor) || self.right.has_side_effects(compressor))) { - if (op) self.operator = op; - var tmp = self.left; - self.left = self.right; - self.right = tmp; - } - }; - if (commutativeOperators(self.operator)) { - if (self.right instanceof AST_Constant - && !(self.left instanceof AST_Constant)) { - // if right is a constant, whatever side effects the - // left side might have could not influence the - // result. hence, force switch. + OPT(AST_Spread, function(self, compressor) { + var exp = self.expression; + if (compressor.option("spreads") && exp instanceof AST_Array && !(compressor.parent() instanceof AST_Object)) { + return List.splice(exp.elements.map(function(node) { + return node instanceof AST_Hole ? make_node(AST_Undefined, node).optimize(compressor) : node; + })); + } + return self; + }); - if (!(self.left instanceof AST_Binary - && PRECEDENCE[self.left.operator] >= PRECEDENCE[self.operator])) { - reverse(null, true); - } - } - if (/^[!=]==?$/.test(self.operator)) { - if (self.left instanceof AST_SymbolRef && self.right instanceof AST_Conditional) { - if (self.right.consequent instanceof AST_SymbolRef - && self.right.consequent.definition() === self.left.definition()) { - if (/^==/.test(self.operator)) return self.right.condition; - if (/^!=/.test(self.operator)) return self.right.condition.negate(compressor); - } - if (self.right.alternative instanceof AST_SymbolRef - && self.right.alternative.definition() === self.left.definition()) { - if (/^==/.test(self.operator)) return self.right.condition.negate(compressor); - if (/^!=/.test(self.operator)) return self.right.condition; - } - } - if (self.right instanceof AST_SymbolRef && self.left instanceof AST_Conditional) { - if (self.left.consequent instanceof AST_SymbolRef - && self.left.consequent.definition() === self.right.definition()) { - if (/^==/.test(self.operator)) return self.left.condition; - if (/^!=/.test(self.operator)) return self.left.condition.negate(compressor); - } - if (self.left.alternative instanceof AST_SymbolRef - && self.left.alternative.definition() === self.right.definition()) { - if (/^==/.test(self.operator)) return self.left.condition.negate(compressor); - if (/^!=/.test(self.operator)) return self.left.condition; + function safe_to_flatten(value, compressor) { + if (!value) return false; + var parent = compressor.parent(); + if (parent.TYPE != "Call") return true; + if (parent.expression !== compressor.self()) return true; + if (value instanceof AST_SymbolRef) { + value = value.fixed_value(); + if (!value) return false; + } + return value instanceof AST_Lambda && !value.contains_this(); + } + + OPT(AST_Sub, function(self, compressor) { + var expr = self.expression; + var prop = self.property; + var terminated = trim_optional_chain(self, compressor); + if (terminated) return terminated; + if (compressor.option("properties")) { + var key = prop.evaluate(compressor); + if (key !== prop) { + if (typeof key == "string") { + if (key == "undefined") { + key = undefined; + } else { + var value = parseFloat(key); + if (value.toString() == key) { + key = value; + } } } - } - } - self = self.lift_sequences(compressor); - if (compressor.option("comparisons")) switch (self.operator) { - case "===": - case "!==": - if ((self.left.is_string(compressor) && self.right.is_string(compressor)) || - (self.left.is_boolean() && self.right.is_boolean())) { - self.operator = self.operator.substr(0, 2); - } - // XXX: intentionally falling down to the next case - case "==": - case "!=": - if (self.left instanceof AST_String - && self.left.value == "undefined" - && self.right instanceof AST_UnaryPrefix - && self.right.operator == "typeof" - && compressor.option("unsafe")) { - if (!(self.right.expression instanceof AST_SymbolRef) - || !self.right.expression.undeclared()) { - self.right = self.right.expression; - self.left = make_node(AST_Undefined, self.left).optimize(compressor); - if (self.operator.length == 2) self.operator += "="; + prop = self.property = best_of_expression(prop, make_node_from_constant(key, prop).transform(compressor)); + var property = "" + key; + if (is_identifier_string(property) + && property.length <= prop.print_to_string().length + 1) { + return make_node(AST_Dot, self, { + optional: self.optional, + expression: expr, + property: property, + quoted: true, + }).optimize(compressor); } } - break; - } - if (compressor.option("booleans") && compressor.in_boolean_context()) switch (self.operator) { - case "&&": - var ll = self.left.evaluate(compressor); - var rr = self.right.evaluate(compressor); - if ((ll.length > 1 && !ll[1]) || (rr.length > 1 && !rr[1])) { - compressor.warn("Boolean && always false [{file}:{line},{col}]", self.start); - return make_node(AST_False, self); - } - if (ll.length > 1 && ll[1]) { - return rr[0]; - } - if (rr.length > 1 && rr[1]) { - return ll[0]; - } - break; - case "||": - var ll = self.left.evaluate(compressor); - var rr = self.right.evaluate(compressor); - if ((ll.length > 1 && ll[1]) || (rr.length > 1 && rr[1])) { - compressor.warn("Boolean || always true [{file}:{line},{col}]", self.start); - return make_node(AST_True, self); - } - if (ll.length > 1 && !ll[1]) { - return rr[0]; - } - if (rr.length > 1 && !rr[1]) { - return ll[0]; - } - break; - case "+": - var ll = self.left.evaluate(compressor); - var rr = self.right.evaluate(compressor); - if ((ll.length > 1 && ll[0] instanceof AST_String && ll[1]) || - (rr.length > 1 && rr[0] instanceof AST_String && rr[1])) { - compressor.warn("+ in boolean context always true [{file}:{line},{col}]", self.start); - return make_node(AST_True, self); - } - break; } - if (compressor.option("comparisons")) { - if (!(compressor.parent() instanceof AST_Binary) - || compressor.parent() instanceof AST_Assign) { - var negated = make_node(AST_UnaryPrefix, self, { - operator: "!", - expression: self.negate(compressor) - }); - self = best_of(self, negated); + var parent = compressor.parent(); + var assigned = is_lhs(compressor.self(), parent); + var def, fn, fn_parent, index; + if (compressor.option("arguments") + && expr instanceof AST_SymbolRef + && is_arguments(def = expr.definition()) + && !expr.in_arg + && prop instanceof AST_Number + && Math.floor(index = prop.value) == index + && (fn = def.scope) === find_lambda() + && fn.uses_arguments < (assigned ? 2 : 3)) { + if (parent instanceof AST_UnaryPrefix && parent.operator == "delete") { + if (!def.deleted) def.deleted = []; + def.deleted[index] = true; } - switch (self.operator) { - case "<": reverse(">"); break; - case "<=": reverse(">="); break; + var argname = fn.argnames[index]; + if (def.deleted && def.deleted[index]) { + argname = null; + } else if (argname) { + var arg_def; + if (!(argname instanceof AST_SymbolFunarg) + || argname.name == "await" + || expr.scope.find_variable(argname.name) !== (arg_def = argname.definition())) { + argname = null; + } else if (compressor.has_directive("use strict") + || fn.name + || fn.rest + || !(fn_parent instanceof AST_Call + && index < fn_parent.args.length + && all(fn_parent.args.slice(0, index + 1), function(arg) { + return !(arg instanceof AST_Spread); + })) + || !all(fn.argnames, function(argname) { + return argname instanceof AST_SymbolFunarg; + })) { + if (has_reassigned() || arg_def.assignments || arg_def.orig.length > 1) argname = null; + } + } else if ((assigned || !has_reassigned()) + && index < fn.argnames.length + 5 + && compressor.drop_fargs(fn, fn_parent) + && !fn.rest) { + while (index >= fn.argnames.length) { + argname = fn.make_var(AST_SymbolFunarg, fn, "argument_" + fn.argnames.length); + fn.argnames.push(argname); + } + } + if (argname && find_if(function(node) { + return node.name === argname.name; + }, fn.argnames) === argname) { + if (assigned) def.reassigned--; + var sym = make_node(AST_SymbolRef, argname); + sym.reference(); + argname.unused = undefined; + return sym; } } - if (self.operator == "+" && self.right instanceof AST_String - && self.right.getValue() === "" && self.left instanceof AST_Binary - && self.left.operator == "+" && self.left.is_string(compressor)) { - return self.left; + if (assigned) return self; + if (compressor.option("sequences") + && parent.TYPE != "Call" + && !(parent instanceof AST_ForEnumeration && parent.init === self)) { + var seq = lift_sequence_in_expression(self, compressor); + if (seq !== self) return seq.optimize(compressor); } - if (compressor.option("evaluate")) { - if (self.operator == "+") { - if (self.left instanceof AST_Constant - && self.right instanceof AST_Binary - && self.right.operator == "+" - && self.right.left instanceof AST_Constant - && self.right.is_string(compressor)) { - self = make_node(AST_Binary, self, { - operator: "+", - left: make_node(AST_String, null, { - value: "" + self.left.getValue() + self.right.left.getValue(), - start: self.left.start, - end: self.right.left.end - }), - right: self.right.right - }); + if (key !== prop) { + var sub = self.flatten_object(property, compressor); + if (sub) { + expr = self.expression = sub.expression; + prop = self.property = sub.property; + } + } + var elements; + if (compressor.option("properties") + && compressor.option("side_effects") + && prop instanceof AST_Number + && expr instanceof AST_Array + && all(elements = expr.elements, function(value) { + return !(value instanceof AST_Spread); + })) { + var index = prop.value; + var retValue = elements[index]; + if (safe_to_flatten(retValue, compressor)) { + var is_hole = retValue instanceof AST_Hole; + var flatten = !is_hole; + var values = []; + for (var i = elements.length; --i > index;) { + var value = elements[i].drop_side_effect_free(compressor); + if (value) { + values.unshift(value); + if (flatten && value.has_side_effects(compressor)) flatten = false; + } } - if (self.right instanceof AST_Constant - && self.left instanceof AST_Binary - && self.left.operator == "+" - && self.left.right instanceof AST_Constant - && self.left.is_string(compressor)) { - self = make_node(AST_Binary, self, { - operator: "+", - left: self.left.left, - right: make_node(AST_String, null, { - value: "" + self.left.right.getValue() + self.right.getValue(), - start: self.left.right.start, - end: self.right.end - }) - }); + if (!flatten) values.unshift(retValue); + while (--i >= 0) { + var value = elements[i].drop_side_effect_free(compressor); + if (value) { + values.unshift(value); + } else if (is_hole) { + values.unshift(make_node(AST_Hole, elements[i])); + } else { + index--; + } } - if (self.left instanceof AST_Binary - && self.left.operator == "+" - && self.left.is_string(compressor) - && self.left.right instanceof AST_Constant - && self.right instanceof AST_Binary - && self.right.operator == "+" - && self.right.left instanceof AST_Constant - && self.right.is_string(compressor)) { - self = make_node(AST_Binary, self, { - operator: "+", - left: make_node(AST_Binary, self.left, { - operator: "+", - left: self.left.left, - right: make_node(AST_String, null, { - value: "" + self.left.right.getValue() + self.right.left.getValue(), - start: self.left.right.start, - end: self.right.left.end - }) - }), - right: self.right.right - }); + if (flatten) { + values.push(retValue); + return make_sequence(self, values).optimize(compressor); } + return make_node(AST_Sub, self, { + expression: make_node(AST_Array, expr, { elements: values }), + property: make_node(AST_Number, prop, { value: index }), + }); } } - // x * (y * z) ==> x * y * z - if (self.right instanceof AST_Binary - && self.right.operator == self.operator - && (self.operator == "*" || self.operator == "&&" || self.operator == "||")) - { - self.left = make_node(AST_Binary, self.left, { - operator : self.operator, - left : self.left, - right : self.right.left - }); - self.right = self.right.right; - return self.transform(compressor); - } - return self.evaluate(compressor)[0]; - }); + return try_evaluate(compressor, self); - OPT(AST_SymbolRef, function(self, compressor){ - if (self.undeclared()) { - var defines = compressor.option("global_defs"); - if (defines && defines.hasOwnProperty(self.name)) { - return make_node_from_constant(compressor, defines[self.name], self); - } - switch (self.name) { - case "undefined": - return make_node(AST_Undefined, self); - case "NaN": - return make_node(AST_NaN, self).transform(compressor); - case "Infinity": - return make_node(AST_Infinity, self).transform(compressor); + function find_lambda() { + var i = 0, p; + while (p = compressor.parent(i++)) { + if (p instanceof AST_Lambda) { + if (p instanceof AST_Accessor) return; + if (is_arrow(p)) continue; + fn_parent = compressor.parent(i); + return p; + } } } - return self; + + function has_reassigned() { + return !compressor.option("reduce_vars") || def.reassigned; + } }); - OPT(AST_Infinity, function (self, compressor) { - return make_node(AST_Binary, self, { - operator : '/', - left : make_node(AST_Number, null, {value: 1}), - right : make_node(AST_Number, null, {value: 0}) - }); + AST_LambdaExpression.DEFMETHOD("contains_super", function() { + var result = false; + var self = this; + self.walk(new TreeWalker(function(node) { + if (result) return true; + if (node instanceof AST_Super) return result = true; + if (node !== self && node instanceof AST_Scope && !is_arrow(node)) return true; + })); + return result; }); - OPT(AST_NaN, function (self, compressor) { - return make_node(AST_Binary, self, { - operator : '/', - left : make_node(AST_Number, null, {value: 0}), - right : make_node(AST_Number, null, {value: 0}) + // contains_this() + // returns false only if context bound by the specified scope (or scope + // containing the specified expression) is not referenced by `this` + (function(def) { + // scope of arrow function cannot bind to any context + def(AST_Arrow, return_false); + def(AST_AsyncArrow, return_false); + def(AST_Node, function() { + var result = false; + var self = this; + self.walk(new TreeWalker(function(node) { + if (result) return true; + if (node instanceof AST_This) return result = true; + if (node !== self && node instanceof AST_Scope && !is_arrow(node)) return true; + })); + return result; }); + })(function(node, func) { + node.DEFMETHOD("contains_this", func); }); - OPT(AST_Undefined, function(self, compressor){ - if (compressor.option("unsafe")) { - var scope = compressor.find_parent(AST_Scope); - var undef = scope.find_variable("undefined"); - if (undef) { - var ref = make_node(AST_SymbolRef, self, { - name : "undefined", - scope : scope, - thedef : undef - }); - ref.reference(); - return ref; + function can_hoist_property(prop) { + return prop instanceof AST_ObjectKeyVal + && typeof prop.key == "string" + && !(prop instanceof AST_ObjectMethod && prop.value.contains_super()); + } + + AST_PropAccess.DEFMETHOD("flatten_object", function(key, compressor) { + if (!compressor.option("properties")) return; + if (key === "__proto__") return; + var self = this; + var expr = self.expression; + if (!(expr instanceof AST_Object)) return; + var props = expr.properties; + for (var i = props.length; --i >= 0;) { + var prop = props[i]; + if (prop.key !== key) continue; + if (!all(props, can_hoist_property)) return; + if (!safe_to_flatten(prop.value, compressor)) return; + var call, scope, values = []; + for (var j = 0; j < props.length; j++) { + var value = props[j].value; + if (props[j] instanceof AST_ObjectMethod) { + var arrow = !(value.uses_arguments || is_generator(value) || value.contains_this()); + if (arrow) { + if (!scope) scope = compressor.find_parent(AST_Scope); + var avoid = avoid_await_yield(compressor, scope); + value.each_argname(function(argname) { + if (avoid[argname.name]) arrow = false; + }); + } + var ctor; + if (arrow) { + ctor = is_async(value) ? AST_AsyncArrow : AST_Arrow; + } else if (i != j + || (call = compressor.parent()) instanceof AST_Call && call.expression === self) { + ctor = value.CTOR; + } else { + return; + } + value = make_node(ctor, value); + } + values.push(value); + } + return make_node(AST_Sub, self, { + expression: make_node(AST_Array, expr, { elements: values }), + property: make_node(AST_Number, self, { value: i }), + }); + } + }); + + OPT(AST_Dot, function(self, compressor) { + if (self.property == "arguments" || self.property == "caller") { + AST_Node.warn("Function.prototype.{property} not supported [{start}]", self); + } + var parent = compressor.parent(); + if (is_lhs(compressor.self(), parent)) return self; + var terminated = trim_optional_chain(self, compressor); + if (terminated) return terminated; + if (compressor.option("sequences") + && parent.TYPE != "Call" + && !(parent instanceof AST_ForEnumeration && parent.init === self)) { + var seq = lift_sequence_in_expression(self, compressor); + if (seq !== self) return seq.optimize(compressor); + } + if (compressor.option("unsafe_proto") + && self.expression instanceof AST_Dot + && self.expression.property == "prototype") { + var exp = self.expression.expression; + if (is_undeclared_ref(exp)) switch (exp.name) { + case "Array": + self.expression = make_node(AST_Array, self.expression, { elements: [] }); + break; + case "Function": + self.expression = make_node(AST_Function, self.expression, { + argnames: [], + body: [], + }).init_vars(exp.scope); + break; + case "Number": + self.expression = make_node(AST_Number, self.expression, { value: 0 }); + break; + case "Object": + self.expression = make_node(AST_Object, self.expression, { properties: [] }); + break; + case "RegExp": + self.expression = make_node(AST_RegExp, self.expression, { value: /t/ }); + break; + case "String": + self.expression = make_node(AST_String, self.expression, { value: "" }); + break; } } + var sub = self.flatten_object(self.property, compressor); + if (sub) return sub.optimize(compressor); + return try_evaluate(compressor, self); + }); + + OPT(AST_DestructuredArray, function(self, compressor) { + if (compressor.option("rests") && self.rest instanceof AST_DestructuredArray) { + return make_node(AST_DestructuredArray, self, { + elements: self.elements.concat(self.rest.elements), + rest: self.rest.rest, + }); + } return self; }); - var ASSIGN_OPS = [ '+', '-', '/', '*', '%', '>>', '<<', '>>>', '|', '^', '&' ]; - OPT(AST_Assign, function(self, compressor){ - self = self.lift_sequences(compressor); - if (self.operator == "=" - && self.left instanceof AST_SymbolRef - && self.right instanceof AST_Binary - && self.right.left instanceof AST_SymbolRef - && self.right.left.name == self.left.name - && member(self.right.operator, ASSIGN_OPS)) { - self.operator = self.right.operator + "="; - self.right = self.right.right; + OPT(AST_DestructuredKeyVal, function(self, compressor) { + if (compressor.option("objects")) { + var key = self.key; + if (key instanceof AST_Node) { + key = key.evaluate(compressor); + if (key !== self.key) self.key = "" + key; + } } return self; }); - OPT(AST_Conditional, function(self, compressor){ - if (!compressor.option("conditionals")) return self; - if (self.condition instanceof AST_Seq) { - var car = self.condition.car; - self.condition = self.condition.cdr; - return AST_Seq.cons(car, self); - } - var cond = self.condition.evaluate(compressor); - if (cond.length > 1) { - if (cond[1]) { - compressor.warn("Condition always true [{file}:{line},{col}]", self.start); - return self.consequent; + OPT(AST_Object, function(self, compressor) { + if (!compressor.option("objects")) return self; + var changed = false; + var found = false; + var generated = false; + var keep_duplicate = compressor.has_directive("use strict"); + var keys = []; + var map = new Dictionary(); + var values = []; + self.properties.forEach(function(prop) { + if (!(prop instanceof AST_Spread)) return process(prop); + found = true; + var exp = prop.expression; + if (compressor.option("spreads") && exp instanceof AST_Object && all(exp.properties, function(prop) { + if (prop instanceof AST_ObjectGetter) return false; + if (prop instanceof AST_Spread) return false; + if (prop.key !== "__proto__") return true; + if (prop instanceof AST_ObjectSetter) return true; + return !prop.value.has_side_effects(compressor); + })) { + changed = true; + exp.properties.forEach(function(prop) { + var key = prop.key; + var setter = prop instanceof AST_ObjectSetter; + if (key === "__proto__") { + if (!setter) return; + key = make_node_from_constant(key, prop); + } + process(setter ? make_node(AST_ObjectKeyVal, prop, { + key: key, + value: make_node(AST_Undefined, prop).optimize(compressor), + }) : prop); + }); } else { - compressor.warn("Condition always false [{file}:{line},{col}]", self.start); - return self.alternative; + generated = true; + flush(); + values.push(prop); + } + }); + flush(); + if (!changed) return self; + if (found && generated && values.length == 1) { + var value = values[0]; + if (value instanceof AST_ObjectProperty && value.key instanceof AST_Number) { + value.key = "" + value.key.value; } } - var negated = cond[0].negate(compressor); - if (best_of(cond[0], negated) === negated) { - self = make_node(AST_Conditional, self, { - condition: negated, - consequent: self.alternative, - alternative: self.consequent - }); - } - var consequent = self.consequent; - var alternative = self.alternative; - if (consequent instanceof AST_Assign - && alternative instanceof AST_Assign - && consequent.operator == alternative.operator - && consequent.left.equivalent_to(alternative.left) - ) { - /* - * Stuff like this: - * if (foo) exp = something; else exp = something_else; - * ==> - * exp = foo ? something : something_else; - */ - return make_node(AST_Assign, self, { - operator: consequent.operator, - left: consequent.left, - right: make_node(AST_Conditional, self, { - condition: self.condition, - consequent: consequent.right, - alternative: alternative.right - }) + return make_node(AST_Object, self, { properties: values }); + + function flush() { + keys.forEach(function(key) { + var props = map.get(key); + switch (props.length) { + case 0: + return; + case 1: + return values.push(props[0]); + } + changed = true; + var tail = keep_duplicate && !generated && props.pop(); + values.push(props.length == 1 ? props[0] : make_node(AST_ObjectKeyVal, self, { + key: props[0].key, + value: make_sequence(self, props.map(function(prop) { + return prop.value; + })), + })); + if (tail) values.push(tail); + props.length = 0; }); + keys = []; + map = new Dictionary(); } - if (consequent instanceof AST_Call - && alternative.TYPE === consequent.TYPE - && consequent.args.length == alternative.args.length - && consequent.expression.equivalent_to(alternative.expression)) { - if (consequent.args.length == 0) { - return make_node(AST_Seq, self, { - car: self.condition, - cdr: consequent - }); + + function process(prop) { + var key = prop.key; + if (key instanceof AST_Node) { + found = true; + key = key.evaluate(compressor); + if (key === prop.key || key === "__proto__") { + generated = true; + } else { + key = prop.key = "" + key; + } } - if (consequent.args.length == 1) { - consequent.args[0] = make_node(AST_Conditional, self, { - condition: self.condition, - consequent: consequent.args[0], - alternative: alternative.args[0] - }); - return consequent; + if (can_hoist_property(prop)) { + if (prop.value.has_side_effects(compressor)) flush(); + keys.push(key); + map.add(key, prop); + } else { + flush(); + values.push(prop); + } + if (found && !generated && typeof key == "string" && RE_POSITIVE_INTEGER.test(key)) { + generated = true; + if (map.has(key)) prop = map.get(key)[0]; + prop.key = make_node(AST_Number, prop, { value: +key }); } } - // x?y?z:a:a --> x&&y?z:a - if (consequent instanceof AST_Conditional - && consequent.alternative.equivalent_to(alternative)) { - return make_node(AST_Conditional, self, { - condition: make_node(AST_Binary, self, { - left: self.condition, - operator: "&&", - right: consequent.condition - }), - consequent: consequent.consequent, - alternative: alternative - }); + }); + + function flatten_var(name) { + var redef = name.definition().redefined(); + if (redef) { + name = name.clone(); + name.thedef = redef; } - // x=y?1:1 --> x=1 - if (consequent instanceof AST_Constant - && alternative instanceof AST_Constant - && consequent.equivalent_to(alternative)) { - if (self.condition.has_side_effects(compressor)) { - return AST_Seq.from_array([self.condition, make_node_from_constant(compressor, consequent.value, self)]); - } else { - return make_node_from_constant(compressor, consequent.value, self); + return name; + } + + function has_arg_refs(fn, node) { + var found = false; + node.walk(new TreeWalker(function(node) { + if (found) return true; + if (node instanceof AST_SymbolRef && fn.variables.get(node.name) === node.definition()) { + return found = true; + } + })); + return found; + } + function insert_assign(def, assign) { + var visited = []; + def.references.forEach(function(ref) { + var fixed = ref.fixed; + if (!fixed || !push_uniq(visited, fixed)) return; + if (fixed.assigns) { + fixed.assigns.unshift(assign); + } else { + fixed.assigns = [ assign ]; } + }); + } + + function init_ref(compressor, name) { + var sym = make_node(AST_SymbolRef, name); + var assign = make_node(AST_Assign, name, { + operator: "=", + left: sym, + right: make_node(AST_Undefined, name).transform(compressor), + }); + var def = name.definition(); + if (def.fixed) { + sym.fixed = function() { + return assign.right; + }; + sym.fixed.assigns = [ assign ]; + insert_assign(def, assign); } - // x=y?true:false --> x=!!y - if (consequent instanceof AST_True - && alternative instanceof AST_False) { - self.condition = self.condition.negate(compressor); - return make_node(AST_UnaryPrefix, self.condition, { - operator: "!", - expression: self.condition + def.assignments++; + def.references.push(sym); + return assign; + } + + (function(def) { + def(AST_Node, noop); + def(AST_Assign, noop); + def(AST_Await, function(compressor, scope, no_return, in_loop) { + if (!compressor.option("awaits")) return; + var self = this; + var inlined = self.expression.try_inline(compressor, scope, no_return, in_loop, true); + if (!inlined) return; + if (!no_return) scan_local_returns(inlined, function(node) { + node.in_bool = false; + var value = node.value; + if (value instanceof AST_Await) return; + node.value = make_node(AST_Await, self, { + expression: value || make_node(AST_Undefined, node).transform(compressor), + }); + }); + return aborts(inlined) ? inlined : make_node(AST_BlockStatement, self, { + body: [ inlined, make_node(AST_SimpleStatement, self, { + body: make_node(AST_Await, self, { expression: make_node(AST_Number, self, { value: 0 })}), + }) ], + }); + }); + def(AST_Binary, function(compressor, scope, no_return, in_loop, in_await) { + if (no_return === undefined) return; + var self = this; + var op = self.operator; + if (!lazy_op[op]) return; + var inlined = self.right.try_inline(compressor, scope, no_return, in_loop, in_await); + if (!inlined) return; + return make_node(AST_If, self, { + condition: make_condition(self.left), + body: inlined, + alternative: no_return ? null : make_node(AST_Return, self, { + value: make_node(AST_Undefined, self).transform(compressor), + }), }); - } - // x=y?false:true --> x=!y - if (consequent instanceof AST_False - && alternative instanceof AST_True) { - return self.condition.negate(compressor) - } - return self; - }); - OPT(AST_Boolean, function(self, compressor){ - if (compressor.option("booleans")) { - var p = compressor.parent(); - if (p instanceof AST_Binary && (p.operator == "==" - || p.operator == "!=")) { - compressor.warn("Non-strict equality against boolean: {operator} {value} [{file}:{line},{col}]", { - operator : p.operator, - value : self.value, - file : p.start.file, - line : p.start.line, - col : p.start.col, + function make_condition(cond) { + switch (op) { + case "&&": + return cond; + case "||": + return cond.negate(compressor); + case "??": + return make_node(AST_Binary, self, { + operator: "==", + left: make_node(AST_Null, self), + right: cond, + }); + } + } + }); + def(AST_BlockStatement, function(compressor, scope, no_return, in_loop) { + if (no_return) return; + if (!this.variables) return; + var body = this.body; + var last = body.length - 1; + if (last < 0) return; + var inlined = body[last].try_inline(compressor, this, no_return, in_loop); + if (!inlined) return; + body[last] = inlined; + return this; + }); + def(AST_Call, function(compressor, scope, no_return, in_loop, in_await) { + if (compressor.option("inline") < 4) return; + var call = this; + if (call.is_expr_pure(compressor)) return; + var fn = call.expression; + if (!(fn instanceof AST_LambdaExpression)) return; + if (fn.name) return; + if (fn.uses_arguments) return; + if (fn.pinned()) return; + if (is_generator(fn)) return; + var arrow = is_arrow(fn); + if (arrow && fn.value) return; + if (fn.body[0] instanceof AST_Directive) return; + if (fn.contains_this()) return; + if (!scope) scope = find_scope(compressor); + var defined = new Dictionary(); + defined.set("NaN", true); + while (!(scope instanceof AST_Scope)) { + scope.variables.each(function(def) { + defined.set(def.name, true); }); - return make_node(AST_Number, self, { - value: +self.value + scope = scope.parent_scope; + } + if (!member(scope, compressor.stack)) return; + if (scope.pinned() && fn.variables.size() > (arrow ? 0 : 1)) return; + if (scope instanceof AST_Toplevel) { + if (fn.variables.size() > (arrow ? 0 : 1)) { + if (!compressor.toplevel.vars) return; + if (fn.functions.size() > 0 && !compressor.toplevel.funcs) return; + } + defined.set("arguments", true); + } + var async = !in_await && is_async(fn); + if (async) { + if (!compressor.option("awaits")) return; + if (!is_async(scope)) return; + if (call.may_throw(compressor)) return; + } + var names = scope.var_names(); + if (in_loop) in_loop = []; + if (!fn.variables.all(function(def, name) { + if (in_loop) in_loop.push(def); + if (!defined.has(name) && !names.has(name)) return true; + return !arrow && name == "arguments" && def.orig.length == 1; + })) return; + if (in_loop && in_loop.length > 0 && is_reachable(fn, in_loop)) return; + var simple_argnames = true; + if (!all(fn.argnames, function(argname) { + var abort = false; + var tw = new TreeWalker(function(node) { + if (abort) return true; + if (node instanceof AST_DefaultValue) { + if (has_arg_refs(fn, node.value)) return abort = true; + node.name.walk(tw); + return true; + } + if (node instanceof AST_DestructuredKeyVal) { + if (node.key instanceof AST_Node && has_arg_refs(fn, node.key)) return abort = true; + node.value.walk(tw); + return true; + } + if (node instanceof AST_SymbolFunarg && !all(node.definition().orig, function(sym) { + return !(sym instanceof AST_SymbolDefun); + })) return abort = true; }); + argname.walk(tw); + if (abort) return false; + if (!(argname instanceof AST_SymbolFunarg)) simple_argnames = false; + return true; + })) return; + if (fn.rest) { + if (has_arg_refs(fn, fn.rest)) return; + simple_argnames = false; } - return make_node(AST_UnaryPrefix, self, { - operator: "!", - expression: make_node(AST_Number, self, { - value: 1 - self.value - }) + var verify_body; + if (no_return) { + verify_body = function(stat) { + var abort = false; + stat.walk(new TreeWalker(function(node) { + if (abort) return true; + if (async && (node instanceof AST_Await || node instanceof AST_ForAwaitOf) + || node instanceof AST_Return) { + return abort = true; + } + if (node instanceof AST_Scope) return true; + })); + return !abort; + }; + } else if (in_await || is_async(fn) || in_async_generator(scope)) { + verify_body = function(stat) { + var abort = false; + var find_return = new TreeWalker(function(node) { + if (abort) return true; + if (node instanceof AST_Return) return abort = true; + if (node instanceof AST_Scope) return true; + }); + stat.walk(new TreeWalker(function(node) { + if (abort) return true; + if (node instanceof AST_Try) { + if (node.bfinally && all(node.body, function(stat) { + stat.walk(find_return); + return !abort; + }) && node.bcatch) node.bcatch.walk(find_return); + return true; + } + if (node instanceof AST_Scope) return true; + })); + return !abort; + }; + } + if (verify_body && !all(fn.body, verify_body)) return; + if (!safe_from_await_yield(fn, avoid_await_yield(compressor, scope))) return; + fn.functions.each(function(def, name) { + scope.functions.set(name, def); }); - } - return self; - }); - - OPT(AST_Sub, function(self, compressor){ - var prop = self.property; - if (prop instanceof AST_String && compressor.option("properties")) { - prop = prop.getValue(); - if (RESERVED_WORDS(prop) ? compressor.option("screw_ie8") : is_identifier_string(prop)) { - return make_node(AST_Dot, self, { - expression : self.expression, - property : prop - }).optimize(compressor); + var body = []; + fn.variables.each(function(def, name) { + if (!arrow && name == "arguments" && def.orig.length == 1) return; + names.set(name, true); + scope.enclosed.push(def); + scope.variables.set(name, def); + def.single_use = false; + if (!in_loop) return; + if (def.references.length == def.replaced) return; + switch (def.orig.length - def.eliminated) { + case 0: + return; + case 1: + if (fn.functions.has(name)) return; + } + if (!all(def.orig, function(sym) { + if (sym instanceof AST_SymbolConst) return false; + if (sym instanceof AST_SymbolFunarg) return !sym.unused && def.scope.resolve() !== fn; + if (sym instanceof AST_SymbolLet) return false; + return true; + })) return; + var sym = def.orig[0]; + if (sym instanceof AST_SymbolCatch) return; + body.push(make_node(AST_SimpleStatement, sym, { body: init_ref(compressor, flatten_var(sym)) })); + def.first_decl = null; + }); + var defs = Object.create(null), syms = new Dictionary(); + if (simple_argnames && all(call.args, function(arg) { + return !(arg instanceof AST_Spread); + })) { + var values = call.args.slice(); + fn.argnames.forEach(function(argname) { + var value = values.shift(); + if (argname.unused) { + if (value) body.push(make_node(AST_SimpleStatement, call, { body: value })); + return; + } + var defn = make_node(AST_VarDef, call, { + name: argname.convert_symbol(AST_SymbolVar, process), + value: value || make_node(AST_Undefined, call).transform(compressor), + }); + if (argname instanceof AST_SymbolFunarg) insert_assign(argname.definition(), defn); + body.push(make_node(AST_Var, call, { definitions: [ defn ] })); + }); + if (values.length) body.push(make_node(AST_SimpleStatement, call, { + body: make_sequence(call, values), + })); + } else { + body.push(make_node(AST_Var, call, { + definitions: [ make_node(AST_VarDef, call, { + name: make_node(AST_DestructuredArray, call, { + elements: fn.argnames.map(function(argname) { + if (argname.unused) return make_node(AST_Hole, argname); + return argname.convert_symbol(AST_SymbolVar, process); + }), + rest: fn.rest && fn.rest.convert_symbol(AST_SymbolVar, process), + }), + value: make_node(AST_Array, call, { elements: call.args.slice() }), + }) ], + })); } - var v = parseFloat(prop); - if (!isNaN(v) && v.toString() == prop) { - self.property = make_node(AST_Number, self.property, { - value: v + syms.each(function(orig, id) { + var def = defs[id]; + [].unshift.apply(def.orig, orig); + def.eliminated += orig.length; + }); + [].push.apply(body, in_loop ? fn.body.filter(function(stat) { + if (!(stat instanceof AST_LambdaDefinition)) return true; + var name = make_node(AST_SymbolVar, flatten_var(stat.name)); + var def = name.definition(); + def.fixed = false; + def.orig.push(name); + def.eliminated++; + body.push(make_node(AST_Var, stat, { + definitions: [ make_node(AST_VarDef, stat, { + name: name, + value: to_func_expr(stat, true), + }) ], + })); + return false; + }) : fn.body); + var inlined = make_node(AST_BlockStatement, call, { body: body }); + if (!no_return) { + if (async) scan_local_returns(inlined, function(node) { + var value = node.value; + if (is_undefined(value)) return; + node.value = make_node(AST_Await, call, { expression: value }); }); + body.push(make_node(AST_Return, call, { + value: in_async_generator(scope) ? make_node(AST_Undefined, call).transform(compressor) : null, + })); } - } - return self; - }); + return inlined; - OPT(AST_Dot, function(self, compressor){ - var prop = self.property; - if (RESERVED_WORDS(prop) && !compressor.option("screw_ie8")) { - return make_node(AST_Sub, self, { - expression : self.expression, - property : make_node(AST_String, self, { - value: prop - }) - }).optimize(compressor); + function process(sym, argname) { + var def = argname.definition(); + defs[def.id] = def; + syms.add(def.id, sym); + } + }); + def(AST_Conditional, function(compressor, scope, no_return, in_loop, in_await) { + var self = this; + var body = self.consequent.try_inline(compressor, scope, no_return, in_loop, in_await); + var alt = self.alternative.try_inline(compressor, scope, no_return, in_loop, in_await); + if (!body && !alt) return; + return make_node(AST_If, self, { + condition: self.condition, + body: body || make_body(self.consequent), + alternative: alt || make_body(self.alternative), + }); + + function make_body(value) { + if (no_return) return make_node(AST_SimpleStatement, value, { body: value }); + return make_node(AST_Return, value, { value: value }); + } + }); + def(AST_For, function(compressor, scope, no_return, in_loop) { + var body = this.body.try_inline(compressor, scope, true, true); + if (body) this.body = body; + var inlined = this.init; + if (inlined) { + inlined = inlined.try_inline(compressor, scope, true, in_loop); + if (inlined) { + this.init = null; + if (inlined instanceof AST_BlockStatement) { + inlined.body.push(this); + return inlined; + } + return make_node(AST_BlockStatement, inlined, { body: [ inlined, this ] }); + } + } + return body && this; + }); + def(AST_ForEnumeration, function(compressor, scope, no_return, in_loop) { + var body = this.body.try_inline(compressor, scope, true, true); + if (body) this.body = body; + var obj = this.object; + if (obj instanceof AST_Sequence) { + var inlined = inline_sequence(compressor, scope, true, in_loop, false, obj, 1); + if (inlined) { + this.object = obj.tail_node(); + inlined.body.push(this); + return inlined; + } + } + return body && this; + }); + def(AST_If, function(compressor, scope, no_return, in_loop) { + var body = this.body.try_inline(compressor, scope, no_return, in_loop); + if (body) this.body = body; + var alt = this.alternative; + if (alt) { + alt = alt.try_inline(compressor, scope, no_return, in_loop); + if (alt) this.alternative = alt; + } + var cond = this.condition; + if (cond instanceof AST_Sequence) { + var inlined = inline_sequence(compressor, scope, true, in_loop, false, cond, 1); + if (inlined) { + this.condition = cond.tail_node(); + inlined.body.push(this); + return inlined; + } + } + return (body || alt) && this; + }); + def(AST_IterationStatement, function(compressor, scope, no_return, in_loop) { + var body = this.body.try_inline(compressor, scope, true, true); + if (!body) return; + this.body = body; + return this; + }); + def(AST_LabeledStatement, function(compressor, scope, no_return, in_loop) { + var body = this.body.try_inline(compressor, scope, no_return, in_loop); + if (!body) return; + if (this.body instanceof AST_IterationStatement && body instanceof AST_BlockStatement) { + var loop = body.body.pop(); + this.body = loop; + body.body.push(this); + return body; + } + this.body = body; + return this; + }); + def(AST_New, noop); + def(AST_Return, function(compressor, scope, no_return, in_loop) { + var value = this.value; + return value && value.try_inline(compressor, scope, undefined, in_loop === "try"); + }); + function inline_sequence(compressor, scope, no_return, in_loop, in_await, node, skip) { + var body = [], exprs = node.expressions, no_ret = no_return; + for (var i = exprs.length - (skip || 0), j = i; --i >= 0; no_ret = true, in_await = false) { + var inlined = exprs[i].try_inline(compressor, scope, no_ret, in_loop, in_await); + if (!inlined) continue; + flush(); + body.push(inlined); + } + if (body.length == 0) return; + flush(); + if (!no_return && body[0] instanceof AST_SimpleStatement) { + body[0] = make_node(AST_Return, node, { value: body[0].body }); + } + return make_node(AST_BlockStatement, node, { body: body.reverse() }); + + function flush() { + if (j > i + 1) body.push(make_node(AST_SimpleStatement, node, { + body: make_sequence(node, exprs.slice(i + 1, j)), + })); + j = i; + } } - return self.evaluate(compressor)[0]; + def(AST_Sequence, function(compressor, scope, no_return, in_loop, in_await) { + return inline_sequence(compressor, scope, no_return, in_loop, in_await, this); + }); + def(AST_SimpleStatement, function(compressor, scope, no_return, in_loop) { + var body = this.body; + while (body instanceof AST_UnaryPrefix) { + var op = body.operator; + if (unary_side_effects[op]) break; + if (op == "void") break; + body = body.expression; + } + if (!no_return && !is_undefined(body)) body = make_node(AST_UnaryPrefix, this, { + operator: "void", + expression: body, + }); + return body.try_inline(compressor, scope, no_return || false, in_loop); + }); + def(AST_UnaryPrefix, function(compressor, scope, no_return, in_loop, in_await) { + var self = this; + var op = self.operator; + if (unary_side_effects[op]) return; + if (!no_return && op == "void") no_return = false; + var inlined = self.expression.try_inline(compressor, scope, no_return, in_loop, in_await); + if (!inlined) return; + if (!no_return) scan_local_returns(inlined, function(node) { + node.in_bool = false; + var value = node.value; + if (op == "void" && is_undefined(value)) return; + node.value = make_node(AST_UnaryPrefix, self, { + operator: op, + expression: value || make_node(AST_Undefined, node).transform(compressor), + }); + }); + return inlined; + }); + def(AST_With, function(compressor, scope, no_return, in_loop) { + var body = this.body.try_inline(compressor, scope, no_return, in_loop); + if (body) this.body = body; + var exp = this.expression; + if (exp instanceof AST_Sequence) { + var inlined = inline_sequence(compressor, scope, true, in_loop, false, exp, 1); + if (inlined) { + this.expression = exp.tail_node(); + inlined.body.push(this); + return inlined; + } + } + return body && this; + }); + def(AST_Yield, function(compressor, scope, no_return, in_loop) { + if (!compressor.option("yields")) return; + if (!this.nested) return; + var call = this.expression; + if (call.TYPE != "Call") return; + var fn = call.expression; + switch (fn.CTOR) { + case AST_AsyncGeneratorFunction: + fn = make_node(AST_AsyncFunction, fn); + break; + case AST_GeneratorFunction: + fn = make_node(AST_Function, fn); + break; + default: + return; + } + call = call.clone(); + call.expression = fn; + return call.try_inline(compressor, scope, no_return, in_loop); + }); + })(function(node, func) { + node.DEFMETHOD("try_inline", func); }); - function literals_in_boolean_context(self, compressor) { - if (compressor.option("booleans") && compressor.in_boolean_context()) { - return make_node(AST_True, self); + OPT(AST_Return, function(self, compressor) { + var value = self.value; + if (value && compressor.option("side_effects") + && is_undefined(value, compressor) + && !in_async_generator(compressor.find_parent(AST_Scope))) { + self.value = null; } return self; - }; - OPT(AST_Array, literals_in_boolean_context); - OPT(AST_Object, literals_in_boolean_context); - OPT(AST_RegExp, literals_in_boolean_context); - -})(); + }); +})(function(node, optimizer) { + node.DEFMETHOD("optimize", function(compressor) { + var self = this; + if (self._optimized) return self; + if (compressor.has_directive("use asm")) return self; + var opt = optimizer(self, compressor); + opt._optimized = true; + return opt; + }); +}); diff --git a/lib/feature_extractor.js b/lib/feature_extractor.js index 6651443d4b3..1a458c493bb 100644 --- a/lib/feature_extractor.js +++ b/lib/feature_extractor.js @@ -154,7 +154,7 @@ function nodeToProperty(node, parent) { return new Property(false, node.name, ""); } // AST_Symbol::unmangleable() returns true if this symbol cannot be renamed (it's either global, undeclared, or defined in scope where eval or with are in use. - if (node.unmangleable()){ + if (node.unmangleable({})){ //return GIVEN + node.name; return new Property(false, node.name, ""); } @@ -222,7 +222,7 @@ function nodeToProperty(node, parent) { function nodeType(node) { if (node instanceof AST_Binary || node instanceof AST_Unary) { - return node.__proto__.TYPE + node.operator; + return Object.getPrototypeOf(node).TYPE + node.operator; } else if (node instanceof AST_Boolean) { return "Bool"; } else if (node instanceof AST_Atom && !(node instanceof AST_Constant)) { @@ -230,7 +230,7 @@ function nodeType(node) { return "Atom"; } - return node.__proto__.TYPE; + return Object.getPrototypeOf(node).TYPE; } function pathToStringFw(path, start){ @@ -338,6 +338,7 @@ function addScopeConstraints(node, toplevel, feature_outputter){ } node.variables.each(function(symbol){ + if (symbol.name === "arguments" && !symbol.scope.uses_arguments) return; feature_outputter.addToScope(nodeToProperty(symbol.orig[0])); }); @@ -655,11 +656,11 @@ function extendAst(root){ var walker = new TreeWalker(function(node){ - if (!node.hasOwnProperty("id")){ + if (!Object.prototype.hasOwnProperty.call(node, "id")){ node.id = current_id; current_id += 1; } - if (!node.hasOwnProperty("parent")){ + if (!Object.prototype.hasOwnProperty.call(node, "parent")){ node.parent = walker.parent(); } node.num_childs = 0; diff --git a/lib/minify.js b/lib/minify.js new file mode 100644 index 00000000000..1771338c1ff --- /dev/null +++ b/lib/minify.js @@ -0,0 +1,277 @@ +"use strict"; + +var to_ascii, to_base64; +if (typeof Buffer == "undefined") { + to_ascii = atob; + to_base64 = btoa; +} else if (typeof Buffer.alloc == "undefined") { + to_ascii = function(b64) { + return new Buffer(b64, "base64").toString(); + }; + to_base64 = function(str) { + return new Buffer(str).toString("base64"); + }; +} else { + to_ascii = function(b64) { + return Buffer.from(b64, "base64").toString(); + }; + to_base64 = function(str) { + return Buffer.from(str).toString("base64"); + }; +} + +function read_source_map(name, toplevel) { + var comments = toplevel.end.comments_after; + for (var i = comments.length; --i >= 0;) { + var comment = comments[i]; + if (comment.type != "comment1") break; + var match = /^# ([^\s=]+)=(\S+)\s*$/.exec(comment.value); + if (!match) break; + if (match[1] == "sourceMappingURL") { + match = /^data:application\/json(;.*?)?;base64,([^,]+)$/.exec(match[2]); + if (!match) break; + return to_ascii(match[2]); + } + } + AST_Node.warn("inline source map not found: {name}", { + name: name, + }); +} + +function parse_source_map(content) { + try { + return JSON.parse(content); + } catch (ex) { + throw new Error("invalid input source map: " + content); + } +} + +function set_shorthand(name, options, keys) { + keys.forEach(function(key) { + if (options[key]) { + if (typeof options[key] != "object") options[key] = {}; + if (!(name in options[key])) options[key][name] = options[name]; + } + }); +} + +function init_cache(cache) { + if (!cache) return; + if (!("props" in cache)) { + cache.props = new Dictionary(); + } else if (!(cache.props instanceof Dictionary)) { + cache.props = Dictionary.fromObject(cache.props); + } +} + +function to_json(cache) { + return { + props: cache.props.toObject() + }; +} + +function minify(files, options) { + try { + options = defaults(options, { + annotations: undefined, + compress: {}, + enclose: false, + expression: false, + ie: false, + ie8: false, + keep_fargs: false, + keep_fnames: false, + mangle: {}, + module: undefined, + nameCache: null, + output: {}, + parse: {}, + rename: undefined, + sourceMap: false, + timings: false, + toplevel: options && !options["expression"] && options["module"] ? true : undefined, + v8: false, + validate: false, + warnings: false, + webkit: false, + wrap: false, + }, true); + if (options.validate) AST_Node.enable_validation(); + var timings = options.timings && { start: Date.now() }; + if (options.annotations !== undefined) set_shorthand("annotations", options, [ "compress", "output" ]); + if (options.expression) set_shorthand("expression", options, [ "compress", "parse" ]); + if (options.ie8) options.ie = options.ie || options.ie8; + if (options.ie) set_shorthand("ie", options, [ "compress", "mangle", "output", "rename" ]); + if (options.keep_fargs) set_shorthand("keep_fargs", options, [ "compress", "mangle", "rename" ]); + if (options.keep_fnames) set_shorthand("keep_fnames", options, [ "compress", "mangle", "rename" ]); + if (options.module === undefined && !options.ie) options.module = true; + if (options.module) set_shorthand("module", options, [ "compress", "parse" ]); + if (options.toplevel !== undefined) set_shorthand("toplevel", options, [ "compress", "mangle", "rename" ]); + if (options.v8) set_shorthand("v8", options, [ "mangle", "output", "rename" ]); + if (options.webkit) set_shorthand("webkit", options, [ "compress", "mangle", "output", "rename" ]); + var quoted_props; + if (options.mangle) { + options.mangle = defaults(options.mangle, { + cache: options.nameCache && (options.nameCache.vars || {}), + eval: false, + ie: false, + keep_fargs: false, + keep_fnames: false, + properties: false, + reserved: [], + toplevel: false, + v8: false, + webkit: false, + }, true); + if (options.mangle.properties) { + if (typeof options.mangle.properties != "object") { + options.mangle.properties = {}; + } + if (options.mangle.properties.keep_quoted) { + quoted_props = options.mangle.properties.reserved; + if (!Array.isArray(quoted_props)) quoted_props = []; + options.mangle.properties.reserved = quoted_props; + } + if (options.nameCache && !("cache" in options.mangle.properties)) { + options.mangle.properties.cache = options.nameCache.props || {}; + } + } + init_cache(options.mangle.cache); + init_cache(options.mangle.properties.cache); + } + if (options.rename === undefined) options.rename = options.compress && options.mangle; + if (options.sourceMap) { + options.sourceMap = defaults(options.sourceMap, { + content: null, + filename: null, + includeSources: false, + names: true, + root: null, + url: null, + }, true); + } + var warnings = []; + if (options.warnings) AST_Node.log_function(function(warning) { + warnings.push(warning); + }, options.warnings == "verbose"); + if (timings) timings.parse = Date.now(); + var toplevel; + options.parse = options.parse || {}; + if (files instanceof AST_Node) { + toplevel = files; + } else { + if (typeof files == "string") files = [ files ]; + options.parse.toplevel = null; + var source_map_content = options.sourceMap && options.sourceMap.content; + if (typeof source_map_content == "string" && source_map_content != "inline") { + source_map_content = parse_source_map(source_map_content); + } + if (source_map_content) options.sourceMap.orig = Object.create(null); + for (var name in files) if (HOP(files, name)) { + options.parse.filename = name; + options.parse.toplevel = toplevel = parse(files[name], options.parse); + if (source_map_content == "inline") { + var inlined_content = read_source_map(name, toplevel); + if (inlined_content) options.sourceMap.orig[name] = parse_source_map(inlined_content); + } else if (source_map_content) { + options.sourceMap.orig[name] = source_map_content; + } + } + } + if (options.parse.expression) toplevel = toplevel.wrap_expression(); + if (quoted_props) reserve_quoted_keys(toplevel, quoted_props); + [ "enclose", "wrap" ].forEach(function(action) { + var option = options[action]; + if (!option) return; + var orig = toplevel.print_to_string().slice(0, -1); + toplevel = toplevel[action](option); + files[toplevel.start.file] = toplevel.print_to_string().replace(orig, ""); + }); + if (options.validate) toplevel.validate_ast(); + if (timings) timings.rename = Date.now(); + if (options.rename) { + toplevel.figure_out_scope(options.rename); + toplevel.expand_names(options.rename); + } + if (timings) timings.compress = Date.now(); + if (options.compress) { + toplevel = new Compressor(options.compress).compress(toplevel); + if (options.validate) toplevel.validate_ast(); + } + if (timings) timings.scope = Date.now(); + if (options.mangle) toplevel.figure_out_scope(options.mangle); + if (timings) timings.mangle = Date.now(); + if (options.mangle) { + toplevel.compute_char_frequency(options.mangle); + toplevel.mangle_names(options.mangle); + } + if (timings) timings.properties = Date.now(); + if (quoted_props) reserve_quoted_keys(toplevel, quoted_props); + if (options.mangle && options.mangle.properties) mangle_properties(toplevel, options.mangle.properties); + if (options.parse.expression) toplevel = toplevel.unwrap_expression(); + if (timings) timings.output = Date.now(); + var result = {}; + var output = defaults(options.output, { + ast: false, + code: true, + }); + if (output.ast) result.ast = toplevel; + if (output.code) { + if (options.sourceMap) { + output.source_map = SourceMap(options.sourceMap); + if (options.sourceMap.includeSources) { + if (files instanceof AST_Toplevel) { + throw new Error("original source content unavailable"); + } else for (var name in files) if (HOP(files, name)) { + output.source_map.setSourceContent(name, files[name]); + } + } + } + delete output.ast; + delete output.code; + var stream = OutputStream(output); + toplevel.print(stream); + result.code = stream.get(); + if (options.sourceMap) { + result.map = output.source_map.toString(); + var url = options.sourceMap.url; + if (url) { + result.code = result.code.replace(/\n\/\/# sourceMappingURL=\S+\s*$/, ""); + if (url == "inline") { + result.code += "\n//# sourceMappingURL=data:application/json;charset=utf-8;base64," + to_base64(result.map); + } else { + result.code += "\n//# sourceMappingURL=" + url; + } + } + } + } + if (options.nameCache && options.mangle) { + if (options.mangle.cache) options.nameCache.vars = to_json(options.mangle.cache); + if (options.mangle.properties && options.mangle.properties.cache) { + options.nameCache.props = to_json(options.mangle.properties.cache); + } + } + if (timings) { + timings.end = Date.now(); + result.timings = { + parse: 1e-3 * (timings.rename - timings.parse), + rename: 1e-3 * (timings.compress - timings.rename), + compress: 1e-3 * (timings.scope - timings.compress), + scope: 1e-3 * (timings.mangle - timings.scope), + mangle: 1e-3 * (timings.properties - timings.mangle), + properties: 1e-3 * (timings.output - timings.properties), + output: 1e-3 * (timings.end - timings.output), + total: 1e-3 * (timings.end - timings.start) + }; + } + if (warnings.length) { + result.warnings = warnings; + } + return result; + } catch (ex) { + return { error: ex }; + } finally { + AST_Node.log_function(); + AST_Node.disable_validation(); + } +} diff --git a/lib/mozilla-ast.js b/lib/mozilla-ast.js index 1deb18e2568..3e0534c8d55 100644 --- a/lib/mozilla-ast.js +++ b/lib/mozilla-ast.js @@ -1,7 +1,7 @@ /*********************************************************************** A JavaScript tokenizer / parser / beautifier / compressor. - https://github.com/mishoo/UglifyJS2 + https://github.com/mishoo/UglifyJS -------------------------------- (C) --------------------------------- @@ -43,22 +43,175 @@ "use strict"; -(function(){ - +(function() { var MOZ_TO_ME = { - ExpressionStatement: function(M) { - var expr = M.expression; - if (expr.type === "Literal" && typeof expr.value === "string") { - return new AST_Directive({ - start: my_start_token(M), - end: my_end_token(M), - value: expr.value - }); + Program: function(M) { + return new AST_Toplevel({ + start: my_start_token(M), + end: my_end_token(M), + body: normalize_directives(M.body.map(from_moz)), + }); + }, + ArrowFunctionExpression: function(M) { + var argnames = [], rest = null; + M.params.forEach(function(param) { + if (param.type == "RestElement") { + rest = from_moz(param.argument); + } else { + argnames.push(from_moz(param)); + } + }); + var fn = new (M.async ? AST_AsyncArrow : AST_Arrow)({ + start: my_start_token(M), + end: my_end_token(M), + argnames: argnames, + rest: rest, + }); + var node = from_moz(M.body); + if (node instanceof AST_BlockStatement) { + fn.body = normalize_directives(node.body); + fn.value = null; + } else { + fn.body = []; + fn.value = node; + } + return fn; + }, + FunctionDeclaration: function(M) { + var ctor; + if (M.async) { + ctor = M.generator ? AST_AsyncGeneratorDefun : AST_AsyncDefun; + } else { + ctor = M.generator ? AST_GeneratorDefun : AST_Defun; } - return new AST_SimpleStatement({ + var argnames = [], rest = null; + M.params.forEach(function(param) { + if (param.type == "RestElement") { + rest = from_moz(param.argument); + } else { + argnames.push(from_moz(param)); + } + }); + return new ctor({ start: my_start_token(M), end: my_end_token(M), - body: from_moz(expr) + name: from_moz(M.id), + argnames: argnames, + rest: rest, + body: normalize_directives(from_moz(M.body).body), + }); + }, + FunctionExpression: function(M) { + var ctor; + if (M.async) { + ctor = M.generator ? AST_AsyncGeneratorFunction : AST_AsyncFunction; + } else { + ctor = M.generator ? AST_GeneratorFunction : AST_Function; + } + var argnames = [], rest = null; + M.params.forEach(function(param) { + if (param.type == "RestElement") { + rest = from_moz(param.argument); + } else { + argnames.push(from_moz(param)); + } + }); + return new ctor({ + start: my_start_token(M), + end: my_end_token(M), + name: from_moz(M.id), + argnames: argnames, + rest: rest, + body: normalize_directives(from_moz(M.body).body), + }); + }, + ClassDeclaration: function(M) { + return new AST_DefClass({ + start: my_start_token(M), + end: my_end_token(M), + name: from_moz(M.id), + extends: from_moz(M.superClass), + properties: M.body.body.map(from_moz), + }); + }, + ClassExpression: function(M) { + return new AST_ClassExpression({ + start: my_start_token(M), + end: my_end_token(M), + name: from_moz(M.id), + extends: from_moz(M.superClass), + properties: M.body.body.map(from_moz), + }); + }, + MethodDefinition: function(M) { + var key = M.key, internal = false; + if (M.computed) { + key = from_moz(key); + } else if (key.type == "PrivateIdentifier") { + internal = true; + key = "#" + key.name; + } else { + key = read_name(key); + } + var ctor = AST_ClassMethod, value = from_moz(M.value); + switch (M.kind) { + case "get": + ctor = AST_ClassGetter; + value = new AST_Accessor(value); + break; + case "set": + ctor = AST_ClassSetter; + value = new AST_Accessor(value); + break; + } + return new ctor({ + start: my_start_token(M), + end: my_end_token(M), + key: key, + private: internal, + static: M.static, + value: value, + }); + }, + PropertyDefinition: function(M) { + var key = M.key, internal = false; + if (M.computed) { + key = from_moz(key); + } else if (key.type == "PrivateIdentifier") { + internal = true; + key = "#" + key.name; + } else { + key = read_name(key); + } + return new AST_ClassField({ + start: my_start_token(M), + end: my_end_token(M), + key: key, + private: internal, + static: M.static, + value: from_moz(M.value), + }); + }, + StaticBlock: function(M) { + var start = my_start_token(M); + var end = my_end_token(M); + return new AST_ClassInit({ + start: start, + end: end, + value: new AST_ClassInitBlock({ + start: start, + end: end, + body: normalize_directives(M.body.map(from_moz)), + }), + }); + }, + ForOfStatement: function(M) { + return new (M.await ? AST_ForAwaitOf : AST_ForOf)({ + start: my_start_token(M), + end: my_end_token(M), + init: from_moz(M.left), + object: from_moz(M.right), + body: from_moz(M.body), }); }, TryStatement: function(M) { @@ -71,48 +224,87 @@ end : my_end_token(M), body : from_moz(M.block).body, bcatch : from_moz(handlers[0]), - bfinally : M.finalizer ? new AST_Finally(from_moz(M.finalizer)) : null + bfinally : M.finalizer ? new AST_Finally(from_moz(M.finalizer)) : null, }); }, Property: function(M) { - var key = M.key; - var name = key.type == "Identifier" ? key.name : key.value; + var key = M.computed ? from_moz(M.key) : read_name(M.key); var args = { - start : my_start_token(key), - end : my_end_token(M.value), - key : name, - value : from_moz(M.value) + start: my_start_token(M), + end: my_end_token(M), + key: key, + value: from_moz(M.value), }; - switch (M.kind) { - case "init": - return new AST_ObjectKeyVal(args); - case "set": - args.value.name = from_moz(key); - return new AST_ObjectSetter(args); - case "get": - args.value.name = from_moz(key); - return new AST_ObjectGetter(args); - } + if (M.kind == "init") return new (M.method ? AST_ObjectMethod : AST_ObjectKeyVal)(args); + args.value = new AST_Accessor(args.value); + if (M.kind == "get") return new AST_ObjectGetter(args); + if (M.kind == "set") return new AST_ObjectSetter(args); }, - ObjectExpression: function(M) { - return new AST_Object({ - start : my_start_token(M), - end : my_end_token(M), - properties : M.properties.map(function(prop){ - prop.type = "Property"; - return from_moz(prop) - }) + ArrayExpression: function(M) { + return new AST_Array({ + start: my_start_token(M), + end: my_end_token(M), + elements: M.elements.map(function(elem) { + return elem === null ? new AST_Hole() : from_moz(elem); + }), + }); + }, + ArrayPattern: function(M) { + var elements = [], rest = null; + M.elements.forEach(function(el) { + if (el === null) { + elements.push(new AST_Hole()); + } else if (el.type == "RestElement") { + rest = from_moz(el.argument); + } else { + elements.push(from_moz(el)); + } + }); + return new AST_DestructuredArray({ + start: my_start_token(M), + end: my_end_token(M), + elements: elements, + rest: rest, }); }, - SequenceExpression: function(M) { - return AST_Seq.from_array(M.expressions.map(from_moz)); + ObjectPattern: function(M) { + var props = [], rest = null; + M.properties.forEach(function(prop) { + if (prop.type == "RestElement") { + rest = from_moz(prop.argument); + } else { + props.push(new AST_DestructuredKeyVal(from_moz(prop))); + } + }); + return new AST_DestructuredObject({ + start: my_start_token(M), + end: my_end_token(M), + properties: props, + rest: rest, + }); }, MemberExpression: function(M) { return new (M.computed ? AST_Sub : AST_Dot)({ - start : my_start_token(M), - end : my_end_token(M), - property : M.computed ? from_moz(M.property) : M.property.name, - expression : from_moz(M.object) + start: my_start_token(M), + end: my_end_token(M), + optional: M.optional, + expression: from_moz(M.object), + property: M.computed ? from_moz(M.property) : M.property.name, + }); + }, + MetaProperty: function(M) { + var expr = from_moz(M.meta); + var prop = read_name(M.property); + if (expr.name == "new" && prop == "target") return new AST_NewTarget({ + start: my_start_token(M), + end: my_end_token(M), + name: "new.target", + }); + return new AST_Dot({ + start: my_start_token(M), + end: my_end_token(M), + expression: expr, + property: prop, }); }, SwitchCase: function(M) { @@ -120,50 +312,286 @@ start : my_start_token(M), end : my_end_token(M), expression : from_moz(M.test), - body : M.consequent.map(from_moz) + body : M.consequent.map(from_moz), + }); + }, + ExportAllDeclaration: function(M) { + var start = my_start_token(M); + var end = my_end_token(M); + return new AST_ExportForeign({ + start: start, + end: end, + aliases: [ M.exported ? from_moz_alias(M.exported) : new AST_String({ + start: start, + value: "*", + end: end, + }) ], + keys: [ new AST_String({ + start: start, + value: "*", + end: end, + }) ], + path: from_moz(M.source), + }); + }, + ExportDefaultDeclaration: function(M) { + var decl = from_moz(M.declaration); + if (!decl.name) switch (decl.CTOR) { + case AST_AsyncDefun: + decl = new AST_AsyncFunction(decl); + break; + case AST_AsyncGeneratorDefun: + decl = new AST_AsyncGeneratorFunction(decl); + break; + case AST_DefClass: + decl = new AST_ClassExpression(decl); + break; + case AST_Defun: + decl = new AST_Function(decl); + break; + case AST_GeneratorDefun: + decl = new AST_GeneratorFunction(decl); + break; + } + return new AST_ExportDefault({ + start: my_start_token(M), + end: my_end_token(M), + body: decl, + }); + }, + ExportNamedDeclaration: function(M) { + if (M.declaration) return new AST_ExportDeclaration({ + start: my_start_token(M), + end: my_end_token(M), + body: from_moz(M.declaration), + }); + if (M.source) { + var aliases = [], keys = []; + M.specifiers.forEach(function(prop) { + aliases.push(from_moz_alias(prop.exported)); + keys.push(from_moz_alias(prop.local)); + }); + return new AST_ExportForeign({ + start: my_start_token(M), + end: my_end_token(M), + aliases: aliases, + keys: keys, + path: from_moz(M.source), + }); + } + return new AST_ExportReferences({ + start: my_start_token(M), + end: my_end_token(M), + properties: M.specifiers.map(function(prop) { + var sym = new AST_SymbolExport(from_moz(prop.local)); + sym.alias = from_moz_alias(prop.exported); + return sym; + }), + }); + }, + ImportDeclaration: function(M) { + var start = my_start_token(M); + var end = my_end_token(M); + var all = null, def = null, props = null; + M.specifiers.forEach(function(prop) { + var sym = new AST_SymbolImport(from_moz(prop.local)); + switch (prop.type) { + case "ImportDefaultSpecifier": + def = sym; + def.key = new AST_String({ + start: start, + value: "", + end: end, + }); + break; + case "ImportNamespaceSpecifier": + all = sym; + all.key = new AST_String({ + start: start, + value: "*", + end: end, + }); + break; + default: + sym.key = from_moz_alias(prop.imported); + if (!props) props = []; + props.push(sym); + break; + } + }); + return new AST_Import({ + start: start, + end: end, + all: all, + default: def, + properties: props, + path: from_moz(M.source), + }); + }, + ImportExpression: function(M) { + var start = my_start_token(M); + var arg = from_moz(M.source); + return new AST_Call({ + start: start, + end: my_end_token(M), + expression: new AST_SymbolRef({ + start: start, + end: arg.start, + name: "import", + }), + args: [ arg ], }); }, VariableDeclaration: function(M) { - return new (M.kind === "const" ? AST_Const : AST_Var)({ - start : my_start_token(M), - end : my_end_token(M), - definitions : M.declarations.map(from_moz) + return new ({ + const: AST_Const, + let: AST_Let, + }[M.kind] || AST_Var)({ + start: my_start_token(M), + end: my_end_token(M), + definitions: M.declarations.map(from_moz), }); }, Literal: function(M) { - var val = M.value, args = { - start : my_start_token(M), - end : my_end_token(M) + var args = { + start: my_start_token(M), + end: my_end_token(M), }; + if (M.bigint) { + args.value = M.bigint.toLowerCase() + "n"; + return new AST_BigInt(args); + } + var val = M.value; if (val === null) return new AST_Null(args); + var rx = M.regex; + if (rx && rx.pattern) { + // RegExpLiteral as per ESTree AST spec + args.value = new RegExp(rx.pattern, rx.flags); + args.value.raw_source = rx.pattern; + return new AST_RegExp(args); + } else if (rx) { + // support legacy RegExp + args.value = M.regex && M.raw ? M.raw : val; + return new AST_RegExp(args); + } switch (typeof val) { case "string": args.value = val; return new AST_String(args); case "number": - args.value = val; - return new AST_Number(args); + if (isNaN(val)) return new AST_NaN(args); + var negate, node; + if (isFinite(val)) { + negate = 1 / val < 0; + args.value = negate ? -val : val; + node = new AST_Number(args); + } else { + negate = val < 0; + node = new AST_Infinity(args); + } + return negate ? new AST_UnaryPrefix({ + start: args.start, + end: args.end, + operator: "-", + expression: node, + }) : node; case "boolean": return new (val ? AST_True : AST_False)(args); - default: - args.value = val; - return new AST_RegExp(args); } }, + TemplateLiteral: function(M) { + return new AST_Template({ + start: my_start_token(M), + end: my_end_token(M), + expressions: M.expressions.map(from_moz), + strings: M.quasis.map(function(el) { + return el.value.raw; + }), + }); + }, + TaggedTemplateExpression: function(M) { + var tmpl = from_moz(M.quasi); + tmpl.start = my_start_token(M); + tmpl.end = my_end_token(M); + tmpl.tag = from_moz(M.tag); + return tmpl; + }, Identifier: function(M) { - var p = FROM_MOZ_STACK[FROM_MOZ_STACK.length - 2]; - return new ( p.type == "LabeledStatement" ? AST_Label - : p.type == "VariableDeclarator" && p.id === M ? (p.kind == "const" ? AST_SymbolConst : AST_SymbolVar) - : p.type == "FunctionExpression" ? (p.id === M ? AST_SymbolLambda : AST_SymbolFunarg) - : p.type == "FunctionDeclaration" ? (p.id === M ? AST_SymbolDefun : AST_SymbolFunarg) - : p.type == "CatchClause" ? AST_SymbolCatch - : p.type == "BreakStatement" || p.type == "ContinueStatement" ? AST_LabelRef - : AST_SymbolRef)({ - start : my_start_token(M), - end : my_end_token(M), - name : M.name - }); - } + var p, level = FROM_MOZ_STACK.length - 1; + do { + p = FROM_MOZ_STACK[--level]; + } while (p.type == "ArrayPattern" + || p.type == "AssignmentPattern" && p.left === FROM_MOZ_STACK[level + 1] + || p.type == "ObjectPattern" + || p.type == "Property" && p.value === FROM_MOZ_STACK[level + 1] + || p.type == "VariableDeclarator" && p.id === FROM_MOZ_STACK[level + 1]); + var ctor = AST_SymbolRef; + switch (p.type) { + case "ArrowFunctionExpression": + if (p.body !== FROM_MOZ_STACK[level + 1]) ctor = AST_SymbolFunarg; + break; + case "BreakStatement": + case "ContinueStatement": + ctor = AST_LabelRef; + break; + case "CatchClause": + ctor = AST_SymbolCatch; + break; + case "ClassDeclaration": + if (p.id === FROM_MOZ_STACK[level + 1]) ctor = AST_SymbolDefClass; + break; + case "ClassExpression": + if (p.id === FROM_MOZ_STACK[level + 1]) ctor = AST_SymbolClass; + break; + case "FunctionDeclaration": + ctor = p.id === FROM_MOZ_STACK[level + 1] ? AST_SymbolDefun : AST_SymbolFunarg; + break; + case "FunctionExpression": + ctor = p.id === FROM_MOZ_STACK[level + 1] ? AST_SymbolLambda : AST_SymbolFunarg; + break; + case "LabeledStatement": + ctor = AST_Label; + break; + case "VariableDeclaration": + ctor = { + const: AST_SymbolConst, + let: AST_SymbolLet, + }[p.kind] || AST_SymbolVar; + break; + } + return new ctor({ + start: my_start_token(M), + end: my_end_token(M), + name: M.name, + }); + }, + Super: function(M) { + return new AST_Super({ + start: my_start_token(M), + end: my_end_token(M), + name: "super", + }); + }, + ThisExpression: function(M) { + return new AST_This({ + start: my_start_token(M), + end: my_end_token(M), + name: "this", + }); + }, + ParenthesizedExpression: function(M) { + var node = from_moz(M.expression); + if (!node.start.parens) node.start.parens = []; + node.start.parens.push(my_start_token(M)); + if (!node.end.parens) node.end.parens = []; + node.end.parens.push(my_end_token(M)); + return node; + }, + ChainExpression: function(M) { + var node = from_moz(M.expression); + node.terminal = true; + return node; + }, }; MOZ_TO_ME.UpdateExpression = @@ -178,8 +606,8 @@ }); }; - map("Program", AST_Toplevel, "body@body"); map("EmptyStatement", AST_EmptyStatement); + map("ExpressionStatement", AST_SimpleStatement, "expression>body"); map("BlockStatement", AST_BlockStatement, "body@body"); map("IfStatement", AST_If, "test>condition, consequent>body, alternate>alternative"); map("LabeledStatement", AST_LabeledStatement, "label>label, body>body"); @@ -194,34 +622,155 @@ map("ForStatement", AST_For, "init>init, test>condition, update>step, body>body"); map("ForInStatement", AST_ForIn, "left>init, right>object, body>body"); map("DebuggerStatement", AST_Debugger); - map("FunctionDeclaration", AST_Defun, "id>name, params@argnames, body%body"); map("VariableDeclarator", AST_VarDef, "id>name, init>value"); map("CatchClause", AST_Catch, "param>argname, body%body"); - map("ThisExpression", AST_This); - map("ArrayExpression", AST_Array, "elements@elements"); - map("FunctionExpression", AST_Function, "id>name, params@argnames, body%body"); map("BinaryExpression", AST_Binary, "operator=operator, left>left, right>right"); map("LogicalExpression", AST_Binary, "operator=operator, left>left, right>right"); map("AssignmentExpression", AST_Assign, "operator=operator, left>left, right>right"); + map("AssignmentPattern", AST_DefaultValue, "left>name, right>value"); map("ConditionalExpression", AST_Conditional, "test>condition, consequent>consequent, alternate>alternative"); - map("NewExpression", AST_New, "callee>expression, arguments@args"); - map("CallExpression", AST_Call, "callee>expression, arguments@args"); + map("NewExpression", AST_New, "callee>expression, arguments@args, pure=pure"); + map("CallExpression", AST_Call, "callee>expression, arguments@args, optional=optional, pure=pure"); + map("SequenceExpression", AST_Sequence, "expressions@expressions"); + map("SpreadElement", AST_Spread, "argument>expression"); + map("ObjectExpression", AST_Object, "properties@properties"); + map("AwaitExpression", AST_Await, "argument>expression"); + map("YieldExpression", AST_Yield, "argument>expression, delegate=nested"); - def_to_moz(AST_Directive, function To_Moz_Directive(M) { + def_to_moz(AST_Toplevel, function To_Moz_Program(M) { + return to_moz_scope("Program", M); + }); + + def_to_moz(AST_LambdaDefinition, function To_Moz_FunctionDeclaration(M) { + var params = M.argnames.map(to_moz); + if (M.rest) params.push({ + type: "RestElement", + argument: to_moz(M.rest), + }); return { - type: "ExpressionStatement", - expression: { + type: "FunctionDeclaration", + id: to_moz(M.name), + async: is_async(M), + generator: is_generator(M), + params: params, + body: to_moz_scope("BlockStatement", M), + }; + }); + + def_to_moz(AST_Lambda, function To_Moz_FunctionExpression(M) { + var params = M.argnames.map(to_moz); + if (M.rest) params.push({ + type: "RestElement", + argument: to_moz(M.rest), + }); + if (is_arrow(M)) return { + type: "ArrowFunctionExpression", + async: is_async(M), + params: params, + body: M.value ? to_moz(M.value) : to_moz_scope("BlockStatement", M), + }; + return { + type: "FunctionExpression", + id: to_moz(M.name), + async: is_async(M), + generator: is_generator(M), + params: params, + body: to_moz_scope("BlockStatement", M), + }; + }); + + def_to_moz(AST_DefClass, function To_Moz_ClassDeclaration(M) { + return { + type: "ClassDeclaration", + id: to_moz(M.name), + superClass: to_moz(M.extends), + body: { + type: "ClassBody", + body: M.properties.map(to_moz), + }, + }; + }); + + def_to_moz(AST_ClassExpression, function To_Moz_ClassExpression(M) { + return { + type: "ClassExpression", + id: to_moz(M.name), + superClass: to_moz(M.extends), + body: { + type: "ClassBody", + body: M.properties.map(to_moz), + }, + }; + }); + + function To_Moz_MethodDefinition(kind) { + return function(M) { + var computed = M.key instanceof AST_Node; + var key = computed ? to_moz(M.key) : M.private ? { + type: "PrivateIdentifier", + name: M.key.slice(1), + } : { type: "Literal", - value: M.value - } + value: M.key, + }; + return { + type: "MethodDefinition", + kind: kind, + computed: computed, + key: key, + static: M.static, + value: to_moz(M.value), + }; + }; + } + def_to_moz(AST_ClassGetter, To_Moz_MethodDefinition("get")); + def_to_moz(AST_ClassSetter, To_Moz_MethodDefinition("set")); + def_to_moz(AST_ClassMethod, To_Moz_MethodDefinition("method")); + + def_to_moz(AST_ClassField, function To_Moz_PropertyDefinition(M) { + var computed = M.key instanceof AST_Node; + var key = computed ? to_moz(M.key) : M.private ? { + type: "PrivateIdentifier", + name: M.key.slice(1), + } : { + type: "Literal", + value: M.key, }; + return { + type: "PropertyDefinition", + computed: computed, + key: key, + static: M.static, + value: to_moz(M.value), + }; + }); + + def_to_moz(AST_ClassInit, function To_Moz_StaticBlock(M) { + return to_moz_scope("StaticBlock", M.value); }); - def_to_moz(AST_SimpleStatement, function To_Moz_ExpressionStatement(M) { + function To_Moz_ForOfStatement(is_await) { + return function(M) { + return { + type: "ForOfStatement", + await: is_await, + left: to_moz(M.init), + right: to_moz(M.object), + body: to_moz(M.body), + }; + }; + } + def_to_moz(AST_ForAwaitOf, To_Moz_ForOfStatement(true)); + def_to_moz(AST_ForOf, To_Moz_ForOfStatement(false)); + + def_to_moz(AST_Directive, function To_Moz_Directive(M) { return { type: "ExpressionStatement", - expression: to_moz(M.body) + expression: set_moz_loc(M, { + type: "Literal", + value: M.value, + }), }; }); @@ -229,7 +778,7 @@ return { type: "SwitchCase", test: to_moz(M.expression), - consequent: M.body.map(to_moz) + consequent: M.body.map(to_moz), }; }); @@ -239,7 +788,7 @@ block: to_moz_block(M), handler: to_moz(M.bcatch), guardedHandlers: [], - finalizer: to_moz(M.bfinally) + finalizer: to_moz(M.bfinally), }; }); @@ -248,33 +797,114 @@ type: "CatchClause", param: to_moz(M.argname), guard: null, - body: to_moz_block(M) + body: to_moz_block(M), }; }); - def_to_moz(AST_Definitions, function To_Moz_VariableDeclaration(M) { + def_to_moz(AST_ExportDeclaration, function To_Moz_ExportNamedDeclaration_declaration(M) { return { - type: "VariableDeclaration", - kind: M instanceof AST_Const ? "const" : "var", - declarations: M.definitions.map(to_moz) + type: "ExportNamedDeclaration", + declaration: to_moz(M.body), }; }); - def_to_moz(AST_Seq, function To_Moz_SequenceExpression(M) { + def_to_moz(AST_ExportDefault, function To_Moz_ExportDefaultDeclaration(M) { return { - type: "SequenceExpression", - expressions: M.to_array().map(to_moz) + type: "ExportDefaultDeclaration", + declaration: to_moz(M.body), }; }); - def_to_moz(AST_PropAccess, function To_Moz_MemberExpression(M) { - var isComputed = M instanceof AST_Sub; + def_to_moz(AST_ExportForeign, function To_Moz_ExportAllDeclaration_ExportNamedDeclaration(M) { + if (M.keys[0].value == "*") return { + type: "ExportAllDeclaration", + exported: M.aliases[0].value == "*" ? null : to_moz_alias(M.aliases[0]), + source: to_moz(M.path), + }; + var specifiers = []; + for (var i = 0; i < M.aliases.length; i++) { + specifiers.push(set_moz_loc({ + start: M.keys[i].start, + end: M.aliases[i].end, + }, { + type: "ExportSpecifier", + local: to_moz_alias(M.keys[i]), + exported: to_moz_alias(M.aliases[i]), + })); + } + return { + type: "ExportNamedDeclaration", + specifiers: specifiers, + source: to_moz(M.path), + }; + }); + + def_to_moz(AST_ExportReferences, function To_Moz_ExportNamedDeclaration_specifiers(M) { + return { + type: "ExportNamedDeclaration", + specifiers: M.properties.map(function(prop) { + return set_moz_loc({ + start: prop.start, + end: prop.alias.end, + }, { + type: "ExportSpecifier", + local: to_moz(prop), + exported: to_moz_alias(prop.alias), + }); + }), + }; + }); + + def_to_moz(AST_Import, function To_Moz_ImportDeclaration(M) { + var specifiers = M.properties ? M.properties.map(function(prop) { + return set_moz_loc({ + start: prop.key.start, + end: prop.end, + }, { + type: "ImportSpecifier", + local: to_moz(prop), + imported: to_moz_alias(prop.key), + }); + }) : []; + if (M.all) specifiers.unshift(set_moz_loc(M.all, { + type: "ImportNamespaceSpecifier", + local: to_moz(M.all), + })); + if (M.default) specifiers.unshift(set_moz_loc(M.default, { + type: "ImportDefaultSpecifier", + local: to_moz(M.default), + })); + return { + type: "ImportDeclaration", + specifiers: specifiers, + source: to_moz(M.path), + }; + }); + + def_to_moz(AST_Definitions, function To_Moz_VariableDeclaration(M) { return { + type: "VariableDeclaration", + kind: M.TYPE.toLowerCase(), + declarations: M.definitions.map(to_moz), + }; + }); + + def_to_moz(AST_PropAccess, function To_Moz_MemberExpression(M) { + var computed = M instanceof AST_Sub; + var expr = { type: "MemberExpression", object: to_moz(M.expression), - computed: isComputed, - property: isComputed ? to_moz(M.property) : {type: "Identifier", name: M.property} + computed: computed, + optional: M.optional, + property: computed ? to_moz(M.property) : { + type: "Identifier", + name: M.property, + }, }; + return M.terminal ? { + type: "ChainExpression", + expression: expr, + } : expr; }); def_to_moz(AST_Unary, function To_Moz_Unary(M) { @@ -295,34 +925,73 @@ }; }); - def_to_moz(AST_Object, function To_Moz_ObjectExpression(M) { + def_to_moz(AST_Array, function To_Moz_ArrayExpression(M) { + return { + type: "ArrayExpression", + elements: M.elements.map(to_moz), + }; + }); + + def_to_moz(AST_DestructuredArray, function To_Moz_ArrayPattern(M) { + var elements = M.elements.map(to_moz); + if (M.rest) elements.push({ + type: "RestElement", + argument: to_moz(M.rest), + }); + return { + type: "ArrayPattern", + elements: elements, + }; + }); + + def_to_moz(AST_DestructuredKeyVal, function To_Moz_Property(M) { + var computed = M.key instanceof AST_Node; + var key = computed ? to_moz(M.key) : { + type: "Literal", + value: M.key, + }; + return { + type: "Property", + kind: "init", + computed: computed, + key: key, + value: to_moz(M.value), + }; + }); + + def_to_moz(AST_DestructuredObject, function To_Moz_ObjectPattern(M) { + var props = M.properties.map(to_moz); + if (M.rest) props.push({ + type: "RestElement", + argument: to_moz(M.rest), + }); return { - type: "ObjectExpression", - properties: M.properties.map(to_moz) + type: "ObjectPattern", + properties: props, }; }); def_to_moz(AST_ObjectProperty, function To_Moz_Property(M) { - var key = ( - is_identifier(M.key) - ? {type: "Identifier", name: M.key} - : {type: "Literal", value: M.key} - ); + var computed = M.key instanceof AST_Node; + var key = computed ? to_moz(M.key) : { + type: "Literal", + value: M.key, + }; var kind; if (M instanceof AST_ObjectKeyVal) { kind = "init"; - } else - if (M instanceof AST_ObjectGetter) { + } else if (M instanceof AST_ObjectGetter) { kind = "get"; - } else - if (M instanceof AST_ObjectSetter) { + } else if (M instanceof AST_ObjectSetter) { kind = "set"; } return { type: "Property", kind: kind, + computed: computed, + method: M instanceof AST_ObjectMethod, key: key, - value: to_moz(M.value) + value: to_moz(M.value), }; }); @@ -330,45 +999,133 @@ var def = M.definition(); return { type: "Identifier", - name: def ? def.mangled_name || def.name : M.name + name: def && def.mangled_name || M.name, + }; + }); + + def_to_moz(AST_Super, function To_Moz_Super() { + return { type: "Super" }; + }); + + def_to_moz(AST_This, function To_Moz_ThisExpression() { + return { type: "ThisExpression" }; + }); + + def_to_moz(AST_NewTarget, function To_Moz_MetaProperty() { + return { + type: "MetaProperty", + meta: { + type: "Identifier", + name: "new", + }, + property: { + type: "Identifier", + name: "target", + }, + }; + }); + + def_to_moz(AST_RegExp, function To_Moz_RegExpLiteral(M) { + var flags = M.value.toString().match(/\/([gimuy]*)$/)[1]; + var value = "/" + M.value.raw_source + "/" + flags; + return { + type: "Literal", + value: value, + raw: value, + regex: { + pattern: M.value.raw_source, + flags: flags, + }, }; }); - def_to_moz(AST_Constant, function To_Moz_Literal(M) { + def_to_moz(AST_BigInt, function To_Moz_BigInt(M) { var value = M.value; - if (typeof value === 'number' && (value < 0 || (value === 0 && 1 / value < 0))) { + return { + type: "Literal", + bigint: value.slice(0, -1), + raw: value, + }; + }); + + function To_Moz_Literal(M) { + var value = M.value; + if (typeof value === "number" && (value < 0 || (value === 0 && 1 / value < 0))) { return { type: "UnaryExpression", operator: "-", prefix: true, argument: { type: "Literal", - value: -value - } + value: -value, + raw: M.start.raw, + }, }; } return { type: "Literal", - value: value + value: value, + raw: M.start.raw, }; - }); + } + def_to_moz(AST_Boolean, To_Moz_Literal); + def_to_moz(AST_Constant, To_Moz_Literal); + def_to_moz(AST_Null, To_Moz_Literal); def_to_moz(AST_Atom, function To_Moz_Atom(M) { return { type: "Identifier", - name: String(M.value) + name: String(M.value), }; }); - AST_Boolean.DEFMETHOD("to_mozilla_ast", AST_Constant.prototype.to_mozilla_ast); - AST_Null.DEFMETHOD("to_mozilla_ast", AST_Constant.prototype.to_mozilla_ast); - AST_Hole.DEFMETHOD("to_mozilla_ast", function To_Moz_ArrayHole() { return null }); + def_to_moz(AST_Template, function To_Moz_TemplateLiteral_TaggedTemplateExpression(M) { + var last = M.strings.length - 1; + var tmpl = { + type: "TemplateLiteral", + expressions: M.expressions.map(to_moz), + quasis: M.strings.map(function(str, index) { + return { + type: "TemplateElement", + tail: index == last, + value: { raw: str }, + }; + }), + }; + if (!M.tag) return tmpl; + return { + type: "TaggedTemplateExpression", + tag: to_moz(M.tag), + quasi: tmpl, + }; + }); AST_Block.DEFMETHOD("to_mozilla_ast", AST_BlockStatement.prototype.to_mozilla_ast); - AST_Lambda.DEFMETHOD("to_mozilla_ast", AST_Function.prototype.to_mozilla_ast); + AST_Hole.DEFMETHOD("to_mozilla_ast", return_null); + AST_Node.DEFMETHOD("to_mozilla_ast", function() { + throw new Error("Cannot convert AST_" + this.TYPE); + }); /* -----[ tools ]----- */ + function normalize_directives(body) { + for (var i = 0; i < body.length; i++) { + var stat = body[i]; + if (!(stat instanceof AST_SimpleStatement)) break; + var node = stat.body; + if (!(node instanceof AST_String)) break; + if (stat.start.pos !== node.start.pos) break; + body[i] = new AST_Directive(node); + } + return body; + } + + function raw_token(moznode) { + if (moznode.type == "Literal") { + return moznode.raw != null ? moznode.raw : moznode.value + ""; + } + } + function my_start_token(moznode) { var loc = moznode.loc, start = loc && loc.start; var range = moznode.range; @@ -379,9 +1136,10 @@ pos : range ? range[0] : moznode.start, endline : start && start.line, endcol : start && start.column, - endpos : range ? range[0] : moznode.start + endpos : range ? range[0] : moznode.start, + raw : raw_token(moznode), }); - }; + } function my_end_token(moznode) { var loc = moznode.loc, end = loc && loc.end; @@ -393,83 +1151,111 @@ pos : range ? range[1] : moznode.end, endline : end && end.line, endcol : end && end.column, - endpos : range ? range[1] : moznode.end + endpos : range ? range[1] : moznode.end, + raw : raw_token(moznode), }); - }; + } - function map(moztype, mytype, propmap) { - var moz_to_me = "function From_Moz_" + moztype + "(M){\n"; - moz_to_me += "return new " + mytype.name + "({\n" + - "start: my_start_token(M),\n" + - "end: my_end_token(M)"; + function read_name(M) { + return "" + M[M.type == "Identifier" ? "name" : "value"]; + } - var me_to_moz = "function To_Moz_" + moztype + "(M){\n"; - me_to_moz += "return {\n" + - "type: " + JSON.stringify(moztype); + function map(moztype, mytype, propmap) { + var moz_to_me = [ + "start: my_start_token(M)", + "end: my_end_token(M)", + ]; + var me_to_moz = [ + "type: " + JSON.stringify(moztype), + ]; - if (propmap) propmap.split(/\s*,\s*/).forEach(function(prop){ + if (propmap) propmap.split(/\s*,\s*/).forEach(function(prop) { var m = /([a-z0-9$_]+)(=|@|>|%)([a-z0-9$_]+)/i.exec(prop); if (!m) throw new Error("Can't understand property map: " + prop); var moz = m[1], how = m[2], my = m[3]; - moz_to_me += ",\n" + my + ": "; - me_to_moz += ",\n" + moz + ": "; switch (how) { - case "@": - moz_to_me += "M." + moz + ".map(from_moz)"; - me_to_moz += "M." + my + ".map(to_moz)"; - break; - case ">": - moz_to_me += "from_moz(M." + moz + ")"; - me_to_moz += "to_moz(M." + my + ")"; - break; - case "=": - moz_to_me += "M." + moz; - me_to_moz += "M." + my; - break; - case "%": - moz_to_me += "from_moz(M." + moz + ").body"; - me_to_moz += "to_moz_block(M)"; - break; - default: - throw new Error("Can't understand operator in propmap: " + prop); + case "@": + moz_to_me.push(my + ": M." + moz + ".map(from_moz)"); + me_to_moz.push(moz + ": M." + my + ".map(to_moz)"); + break; + case ">": + moz_to_me.push(my + ": from_moz(M." + moz + ")"); + me_to_moz.push(moz + ": to_moz(M." + my + ")"); + break; + case "=": + moz_to_me.push(my + ": M." + moz); + me_to_moz.push(moz + ": M." + my); + break; + case "%": + moz_to_me.push(my + ": from_moz(M." + moz + ").body"); + me_to_moz.push(moz + ": to_moz_block(M)"); + break; + default: + throw new Error("Can't understand operator in propmap: " + prop); } }); - moz_to_me += "\n})\n}"; - me_to_moz += "\n}\n}"; - - //moz_to_me = parse(moz_to_me).print_to_string({ beautify: true }); - //me_to_moz = parse(me_to_moz).print_to_string({ beautify: true }); - //console.log(moz_to_me); - - moz_to_me = new Function("my_start_token", "my_end_token", "from_moz", "return(" + moz_to_me + ")")( - my_start_token, my_end_token, from_moz - ); - me_to_moz = new Function("to_moz", "to_moz_block", "return(" + me_to_moz + ")")( - to_moz, to_moz_block - ); - MOZ_TO_ME[moztype] = moz_to_me; - def_to_moz(mytype, me_to_moz); - }; + MOZ_TO_ME[moztype] = new Function("U2", "my_start_token", "my_end_token", "from_moz", [ + "return function From_Moz_" + moztype + "(M) {", + " return new U2.AST_" + mytype.TYPE + "({", + moz_to_me.join(",\n"), + " });", + "};", + ].join("\n"))(exports, my_start_token, my_end_token, from_moz); + def_to_moz(mytype, new Function("to_moz", "to_moz_block", "to_moz_scope", [ + "return function To_Moz_" + moztype + "(M) {", + " return {", + me_to_moz.join(",\n"), + " };", + "};", + ].join("\n"))(to_moz, to_moz_block, to_moz_scope)); + } var FROM_MOZ_STACK = null; - function from_moz(node) { - FROM_MOZ_STACK.push(node); - var ret = node != null ? MOZ_TO_ME[node.type](node) : null; + function from_moz(moz) { + FROM_MOZ_STACK.push(moz); + var node = null; + if (moz) { + if (!HOP(MOZ_TO_ME, moz.type)) throw new Error("Unsupported type: " + moz.type); + node = MOZ_TO_ME[moz.type](moz); + } FROM_MOZ_STACK.pop(); - return ret; - }; + return node; + } - AST_Node.from_mozilla_ast = function(node){ + function from_moz_alias(moz) { + return new AST_String({ + start: my_start_token(moz), + value: read_name(moz), + end: my_end_token(moz), + }); + } + + AST_Node.from_mozilla_ast = function(node) { var save_stack = FROM_MOZ_STACK; FROM_MOZ_STACK = []; var ast = from_moz(node); FROM_MOZ_STACK = save_stack; + ast.walk(new TreeWalker(function(node) { + if (node instanceof AST_LabelRef) { + for (var level = 0, parent; parent = this.parent(level); level++) { + if (parent instanceof AST_Scope) break; + if (parent instanceof AST_LabeledStatement && parent.label.name == node.name) { + node.thedef = parent.label; + break; + } + } + if (!node.thedef) { + var s = node.start; + js_error("Undefined label " + node.name, s.file, s.line, s.col, s.pos); + } + } + })); return ast; }; - function set_moz_loc(mynode, moznode, myparent) { + function set_moz_loc(mynode, moznode) { var start = mynode.start; var end = mynode.end; if (start.pos != null && end.endpos != null) { @@ -478,30 +1264,47 @@ if (start.line) { moznode.loc = { start: {line: start.line, column: start.col}, - end: end.endline ? {line: end.endline, column: end.endcol} : null + end: end.endline ? {line: end.endline, column: end.endcol} : null, }; if (start.file) { moznode.loc.source = start.file; } } return moznode; - }; + } function def_to_moz(mytype, handler) { mytype.DEFMETHOD("to_mozilla_ast", function() { return set_moz_loc(this, handler(this)); }); - }; + } function to_moz(node) { return node != null ? node.to_mozilla_ast() : null; - }; + } + + function to_moz_alias(alias) { + return is_identifier_string(alias.value) ? set_moz_loc(alias, { + type: "Identifier", + name: alias.value, + }) : to_moz(alias); + } function to_moz_block(node) { return { type: "BlockStatement", - body: node.body.map(to_moz) + body: node.body.map(to_moz), }; - }; + } + function to_moz_scope(type, node) { + var body = node.body.map(to_moz); + if (node.body[0] instanceof AST_SimpleStatement && node.body[0].body instanceof AST_String) { + body.unshift(to_moz(new AST_EmptyStatement(node.body[0]))); + } + return { + type: type, + body: body, + }; + } })(); diff --git a/lib/output.js b/lib/output.js index c93e865ab5f..1461f4a8dcc 100644 --- a/lib/output.js +++ b/lib/output.js @@ -1,7 +1,7 @@ /*********************************************************************** A JavaScript tokenizer / parser / beautifier / compressor. - https://github.com/mishoo/UglifyJS2 + https://github.com/mishoo/UglifyJS -------------------------------- (C) --------------------------------- @@ -43,37 +43,108 @@ "use strict"; -function OutputStream(options) { +function is_some_comments(comment) { + // multiline comment + return comment.type == "comment2" && /@preserve|@license|@cc_on/i.test(comment.value); +} +function OutputStream(options) { options = defaults(options, { - indent_start : 0, - indent_level : 4, - quote_keys : false, - space_colon : true, + annotations : false, ascii_only : false, - unescape_regexps : false, - inline_script : false, - width : 80, - max_line_len : 32000, beautify : false, - source_map : null, - bracketize : false, - semicolons : true, + braces : false, comments : false, - preserve_line : false, - screw_ie8 : false, + extendscript : false, + galio : false, + ie : false, + indent_level : 4, + indent_start : 0, + inline_script : true, + keep_quoted_props: false, + max_line_len : false, preamble : null, - replace_mangled : null + replace_mangled : null, + preserve_line : false, + quote_keys : false, + quote_style : 0, + semicolons : true, + shebang : true, + source_map : null, + v8 : false, + webkit : false, + width : 80, + wrap_iife : false, }, true); - var indentation = 0; + // Convert comment option to RegExp if necessary and set up comments filter + var comment_filter = return_false; // Default case, throw all comments away + if (options.comments) { + var comments = options.comments; + if (typeof options.comments === "string" && /^\/.*\/[a-zA-Z]*$/.test(options.comments)) { + var regex_pos = options.comments.lastIndexOf("/"); + comments = new RegExp( + options.comments.substr(1, regex_pos - 1), + options.comments.substr(regex_pos + 1) + ); + } + if (comments instanceof RegExp) { + comment_filter = function(comment) { + return comment.type != "comment5" && comments.test(comment.value); + }; + } else if (typeof comments === "function") { + comment_filter = function(comment) { + return comment.type != "comment5" && comments(this, comment); + }; + } else if (comments === "some") { + comment_filter = is_some_comments; + } else { // NOTE includes "all" option + comment_filter = return_true; + } + } + + function make_indent(value) { + if (typeof value == "number") return new Array(value + 1).join(" "); + if (!value) return ""; + if (!/^\s*$/.test(value)) throw new Error("unsupported indentation: " + JSON.stringify("" + value)); + return value; + } + var current_col = 0; var current_line = 1; - var current_pos = 0; - var OUTPUT = ""; + var current_indent = make_indent(options.indent_start); + var full_indent = make_indent(options.indent_level); + var half_indent = full_indent.length + 1 >> 1; + var last; + var line_end = 0; + var line_fixed = true; + var mappings = options.source_map && []; + var mapping_name; + var mapping_token; + var might_need_space; + var might_need_semicolon; + var need_newline_indented = false; + var need_space = false; + var output; + var stack; + var stored = ""; + + function reset() { + last = ""; + might_need_space = false; + might_need_semicolon = false; + stack = []; + var str = output; + output = ""; + return str; + } - function to_ascii(str, identifier) { - return str.replace(/[\u0080-\uffff]/g, function(ch) { + reset(); + var to_utf8 = options.ascii_only ? function(str, identifier) { + if (identifier) str = str.replace(/[\ud800-\udbff][\udc00-\udfff]/g, function(ch) { + return "\\u{" + (ch.charCodeAt(0) - 0xd7c0 << 10 | ch.charCodeAt(1) - 0xdc00).toString(16) + "}"; + }); + return str.replace(/[\u0000-\u001f\u007f-\uffff]/g, function(ch) { var code = ch.charCodeAt(0).toString(16); if (code.length <= 2 && !identifier) { while (code.length < 2) code = "0" + code; @@ -83,121 +154,218 @@ function OutputStream(options) { return "\\u" + code; } }); + } : function(str) { + var s = ""; + for (var i = 0, j = 0; i < str.length; i++) { + var code = str.charCodeAt(i); + if (is_surrogate_pair_head(code)) { + if (is_surrogate_pair_tail(str.charCodeAt(i + 1))) { + i++; + continue; + } + } else if (!is_surrogate_pair_tail(code)) { + continue; + } + s += str.slice(j, i) + "\\u" + code.toString(16); + j = i + 1; + } + return j == 0 ? str : s + str.slice(j); + }; + + function quote_single(str) { + return "'" + str.replace(/\x27/g, "\\'") + "'"; + } + + function quote_double(str) { + return '"' + str.replace(/\x22/g, '\\"') + '"'; + } + + var quote_string = [ + null, + quote_single, + quote_double, + function(str, quote) { + return quote == "'" ? quote_single(str) : quote_double(str); + }, + ][options.quote_style] || function(str, quote, dq, sq) { + return dq > sq ? quote_single(str) : quote_double(str); }; - function make_string(str) { + function make_string(str, quote) { var dq = 0, sq = 0; - str = str.replace(/[\\\b\f\n\r\t\x22\x27\u2028\u2029\0\ufeff]/g, function(s){ + str = str.replace(/[\\\b\f\n\r\v\t\x22\x27\u2028\u2029\0\ufeff]/g, function(s, i) { switch (s) { + case '"': ++dq; return '"'; + case "'": ++sq; return "'"; case "\\": return "\\\\"; - case "\b": return "\\b"; - case "\f": return "\\f"; case "\n": return "\\n"; case "\r": return "\\r"; + case "\t": return "\\t"; + case "\b": return "\\b"; + case "\f": return "\\f"; + case "\x0B": return options.ie ? "\\x0B" : "\\v"; case "\u2028": return "\\u2028"; case "\u2029": return "\\u2029"; - case '"': ++dq; return '"'; - case "'": ++sq; return "'"; - case "\0": return "\\x00"; case "\ufeff": return "\\ufeff"; + case "\0": + return /[0-9]/.test(str.charAt(i+1)) ? "\\x00" : "\\0"; } return s; }); - if (options.ascii_only) str = to_ascii(str); - if (dq > sq) return "'" + str.replace(/\x27/g, "\\'") + "'"; - else return '"' + str.replace(/\x22/g, '\\"') + '"'; - }; - - function encode_string(str) { - var ret = make_string(str); - if (options.inline_script) - ret = ret.replace(/<\x2fscript([>\/\t\n\f\r ])/gi, "<\\/script$1"); - return ret; - }; + return quote_string(to_utf8(str), quote, dq, sq); + } - function make_name(name) { - name = name.toString(); - if (options.ascii_only) - name = to_ascii(name, true); - return name; - }; + /* -----[ beautification/minification ]----- */ - function make_indent(back) { - return repeat_string(" ", options.indent_start + indentation - back * options.indent_level); - }; + var adjust_mappings = mappings ? function(line, col) { + mappings.forEach(function(mapping) { + mapping.line += line; + mapping.col += col; + }); + } : noop; - /* -----[ beautification/minification ]----- */ + var flush_mappings = mappings ? function() { + mappings.forEach(function(mapping) { + options.source_map.add( + mapping.token.file, + mapping.line, mapping.col, + mapping.token.line, mapping.token.col, + !mapping.name && mapping.token.type == "name" ? mapping.token.value : mapping.name + ); + }); + mappings = []; + } : noop; - var might_need_space = false; - var might_need_semicolon = false; - var last = null; + function insert_newlines(count) { + stored += output.slice(0, line_end); + output = output.slice(line_end); + var new_col = output.length; + adjust_mappings(count, new_col - current_col); + current_line += count; + current_col = new_col; + while (count--) stored += "\n"; + } - function last_char() { - return last.charAt(last.length - 1); - }; + var fix_line = options.max_line_len ? function(flush) { + if (line_fixed) { + if (current_col > options.max_line_len) { + AST_Node.warn("Output exceeds {max_line_len} characters", options); + } + return; + } + if (current_col > options.max_line_len) { + insert_newlines(1); + line_fixed = true; + } + if (line_fixed || flush) flush_mappings(); + } : noop; - function maybe_newline() { - if (options.max_line_len && current_col > options.max_line_len) - print("\n"); - }; + var require_semicolon = makePredicate("( [ + * / - , ."); - var requireSemicolonChars = makePredicate("( [ + * / - , ."); + function require_space(prev, ch, str) { + return is_identifier_char(prev) && (is_identifier_char(ch) || ch == "\\") + || (ch == "/" && ch == prev) + || ((ch == "+" || ch == "-") && ch == last) + || last == "--" && ch == ">" + || last == "!" && str == "--" + || prev == "/" && (str == "in" || str == "instanceof"); + } - function print(str) { - str = String(str); + var print = options.beautify + || options.comments + || options.max_line_len + || options.preserve_line + || options.shebang + || !options.semicolons + || options.source_map + || options.width ? function(str) { var ch = str.charAt(0); + if (need_newline_indented && ch) { + need_newline_indented = false; + if (ch != "\n") { + print("\n"); + indent(); + } + } + if (need_space && ch) { + need_space = false; + if (!/[\s;})]/.test(ch)) { + space(); + } + } + var prev = last.slice(-1); if (might_need_semicolon) { - if ((!ch || ";}".indexOf(ch) < 0) && !/[;]$/.test(last)) { - if (options.semicolons || requireSemicolonChars(ch)) { - OUTPUT += ";"; + might_need_semicolon = false; + if (prev == ":" && ch == "}" || prev != ";" && (!ch || ";}".indexOf(ch) < 0)) { + var need_semicolon = require_semicolon[ch]; + if (need_semicolon || options.semicolons) { + output += ";"; current_col++; - current_pos++; + if (!line_fixed) { + fix_line(); + if (line_fixed && !need_semicolon && output == ";") { + output = ""; + current_col = 0; + } + } + if (line_end == output.length - 1) line_end++; } else { - OUTPUT += "\n"; - current_pos++; + fix_line(); + output += "\n"; current_line++; current_col = 0; + // reset the semicolon flag, since we didn't print one + // now and might still have to later + if (/^\s+$/.test(str)) might_need_semicolon = true; } - if (!options.beautify) - might_need_space = false; - } - might_need_semicolon = false; - maybe_newline(); - } - - if (!options.beautify && options.preserve_line && stack[stack.length - 1]) { - var target_line = stack[stack.length - 1].start.line; - while (current_line < target_line) { - OUTPUT += "\n"; - current_pos++; - current_line++; - current_col = 0; - might_need_space = false; + if (!options.beautify) might_need_space = false; } } if (might_need_space) { - var prev = last_char(); - if ((is_identifier_char(prev) - && (is_identifier_char(ch) || ch == "\\")) - || (/^[\+\-\/]$/.test(ch) && ch == prev)) - { - OUTPUT += " "; + if (require_space(prev, ch, str)) { + output += " "; current_col++; - current_pos++; } - might_need_space = false; + if (prev != "<" || str != "!") might_need_space = false; + } + + if (mapping_token) { + mappings.push({ + token: mapping_token, + name: mapping_name, + line: current_line, + col: current_col, + }); + mapping_token = false; + if (line_fixed) flush_mappings(); } + + output += str; var a = str.split(/\r?\n/), n = a.length - 1; current_line += n; - if (n == 0) { - current_col += a[n].length; - } else { + current_col += a[0].length; + if (n > 0) { + fix_line(); current_col = a[n].length; } - current_pos += str.length; last = str; - OUTPUT += str; + } : function(str) { + var ch = str.charAt(0); + var prev = last.slice(-1); + if (might_need_semicolon) { + might_need_semicolon = false; + if (prev == ":" && ch == "}" || (!ch || ";}".indexOf(ch) < 0) && prev != ";") { + output += ";"; + might_need_space = false; + } + } + if (might_need_space) { + if (require_space(prev, ch, str)) output += " "; + if (prev != "<" || str != "!") might_need_space = false; + } + output += str; + last = str; }; var space = options.beautify ? function() { @@ -207,23 +375,27 @@ function OutputStream(options) { }; var indent = options.beautify ? function(half) { - if (options.beautify) { - print(make_indent(half ? 0.5 : 0)); - } + if (need_newline_indented) print("\n"); + print(half ? current_indent.slice(0, -half_indent) : current_indent); } : noop; - var with_indent = options.beautify ? function(col, cont) { - if (col === true) col = next_indent(); - var save_indentation = indentation; - indentation = col; - var ret = cont(); - indentation = save_indentation; - return ret; - } : function(col, cont) { return cont() }; + var with_indent = options.beautify ? function(cont) { + var save_indentation = current_indent; + current_indent += full_indent; + cont(); + current_indent = save_indentation; + } : function(cont) { cont() }; + + var may_add_newline = options.max_line_len || options.preserve_line ? function() { + fix_line(); + line_end = output.length; + line_fixed = false; + } : noop; var newline = options.beautify ? function() { print("\n"); - } : maybe_newline; + line_end = output.length; + } : may_add_newline; var semicolon = options.beautify ? function() { print(";"); @@ -232,90 +404,174 @@ function OutputStream(options) { }; function force_semicolon() { - might_need_semicolon = false; + if (might_need_semicolon) print(";"); print(";"); - }; - - function next_indent() { - return indentation + options.indent_level; - }; + } - function with_block(cont) { - var ret; + function with_block(cont, end) { print("{"); newline(); - with_indent(next_indent(), function(){ - ret = cont(); - }); + with_indent(cont); + add_mapping(end); indent(); print("}"); - return ret; - }; + } function with_parens(cont) { print("("); - //XXX: still nice to have that for argument lists - //var ret = with_indent(current_col, cont); - var ret = cont(); + may_add_newline(); + cont(); + may_add_newline(); print(")"); - return ret; - }; + } function with_square(cont) { print("["); - //var ret = with_indent(current_col, cont); - var ret = cont(); + may_add_newline(); + cont(); + may_add_newline(); print("]"); - return ret; - }; + } function comma() { + may_add_newline(); print(","); + may_add_newline(); space(); - }; + } function colon() { print(":"); - if (options.space_colon) space(); - }; + space(); + } - var add_mapping = options.source_map ? function(token, name) { - try { - if (token) options.source_map.add( - token.file || "?", - current_line, current_col, - token.line, token.col, - (!name && token.type == "name") ? token.value : name - ); - } catch(ex) { - AST_Node.warn("Couldn't figure out mapping for {file}:{line},{col} → {cline},{ccol} [{name}]", { - file: token.file, - line: token.line, - col: token.col, - cline: current_line, - ccol: current_col, - name: name || "" - }) - } + var add_mapping = mappings ? function(token, name) { + mapping_token = token; + mapping_name = name; } : noop; function get() { - return OUTPUT; - }; + if (!line_fixed) fix_line(true); + return stored + output; + } + + function has_nlb() { + return /(^|\n) *$/.test(output); + } + + function pad_comment(token, force) { + if (need_newline_indented) return; + if (token.nlb && (force || !has_nlb())) { + need_newline_indented = true; + } else if (force) { + need_space = true; + } + } + + function print_comment(comment) { + var value = comment.value.replace(/[@#]__PURE__/g, " "); + if (/^\s*$/.test(value) && !/^\s*$/.test(comment.value)) return false; + if (/comment[134]/.test(comment.type)) { + print("//" + value); + need_newline_indented = true; + } else if (comment.type == "comment2") { + print("/*" + value + "*/"); + } + return true; + } + + function should_merge_comments(node, parent) { + if (parent instanceof AST_Binary) return parent.left === node; + if (parent.TYPE == "Call") return parent.expression === node; + if (parent instanceof AST_Conditional) return parent.condition === node; + if (parent instanceof AST_Dot) return parent.expression === node; + if (parent instanceof AST_Exit) return true; + if (parent instanceof AST_Sequence) return parent.expressions[0] === node; + if (parent instanceof AST_Sub) return parent.expression === node; + if (parent instanceof AST_UnaryPostfix) return true; + if (parent instanceof AST_Yield) return true; + } + + function prepend_comments(node) { + var self = this; + var scan; + if (node instanceof AST_Exit) { + scan = node.value; + } else if (node instanceof AST_Yield) { + scan = node.expression; + } + var comments = dump(node); + if (!comments) comments = []; + + if (scan) { + var tw = new TreeWalker(function(node) { + if (!should_merge_comments(node, tw.parent())) return true; + var before = dump(node); + if (before) comments = comments.concat(before); + }); + tw.push(node); + scan.walk(tw); + } + + if (current_line == 1 && current_col == 0) { + if (comments.length > 0 && options.shebang && comments[0].type == "comment5") { + print("#!" + comments.shift().value + "\n"); + indent(); + } + var preamble = options.preamble; + if (preamble) print(preamble.replace(/\r\n?|\u2028|\u2029|(^|\S)\s*$/g, "$1\n")); + } + + comments = comments.filter(comment_filter, node); + var printed = false; + comments.forEach(function(comment, index) { + pad_comment(comment, index); + if (print_comment(comment)) printed = true; + }); + if (printed) pad_comment(node.start, true); + + function dump(node) { + var token = node.start; + if (!token) { + if (!scan) return; + node.start = token = new AST_Token(); + } + var comments = token.comments_before; + if (!comments) { + if (!scan) return; + token.comments_before = comments = []; + } + if (comments._dumped === self) return; + comments._dumped = self; + return comments; + } + } - if (options.preamble) { - print(options.preamble.replace(/\r\n?|[\n\u2028\u2029]|\s*$/g, "\n")); + function append_comments(node, tail) { + var self = this; + var token = node.end; + if (!token) return; + var comments = token[tail ? "comments_before" : "comments_after"]; + if (!comments || comments._dumped === self) return; + if (!(node instanceof AST_Statement || all(comments, function(c) { + return !/comment[134]/.test(c.type); + }))) return; + comments._dumped = self; + comments.filter(comment_filter, node).forEach(function(comment, index) { + pad_comment(comment, index || !tail); + print_comment(comment); + }); } - var stack = []; return { replace_mangled : options.replace_mangled, get : get, - toString : get, + reset : reset, indent : indent, - indentation : function() { return indentation }, - current_width : function() { return current_col - indentation }, - should_break : function() { return options.width && this.current_width() >= options.width }, + should_break : options.beautify && options.width ? function() { + return current_col >= options.width; + } : return_false, + has_parens : function() { return last.slice(-1) == "(" }, newline : newline, print : print, space : space, @@ -324,46 +580,51 @@ function OutputStream(options) { last : function() { return last }, semicolon : semicolon, force_semicolon : force_semicolon, - to_ascii : to_ascii, - print_name : function(name) { print(make_name(name)) }, - print_string : function(str) { print(encode_string(str)) }, - next_indent : next_indent, + to_utf8 : to_utf8, + print_name : function(name) { print(to_utf8(name.toString(), true)) }, + print_string : options.inline_script ? function(str, quote) { + str = make_string(str, quote).replace(/<\x2f(script)([>\/\t\n\f\r ])/gi, "<\\/$1$2"); + print(str.replace(/\x3c!--/g, "\\x3c!--").replace(/--\x3e/g, "--\\x3e")); + } : function(str, quote) { + print(make_string(str, quote)); + }, with_indent : with_indent, with_block : with_block, with_parens : with_parens, with_square : with_square, add_mapping : add_mapping, option : function(opt) { return options[opt] }, - line : function() { return current_line }, - col : function() { return current_col }, - pos : function() { return current_pos }, + prepend_comments: options.comments || options.shebang ? prepend_comments : noop, + append_comments : options.comments ? append_comments : noop, push_node : function(node) { stack.push(node) }, - pop_node : function() { return stack.pop() }, - stack : function() { return stack }, + pop_node : options.preserve_line ? function() { + var node = stack.pop(); + if (node.start && node.start.line > current_line) { + insert_newlines(node.start.line - current_line); + } + } : function() { + stack.pop(); + }, parent : function(n) { return stack[stack.length - 2 - (n || 0)]; - } + }, }; - -}; +} /* -----[ code generators ]----- */ -(function(){ +(function() { /* -----[ utils ]----- */ function DEFPRINT(nodetype, generator) { nodetype.DEFMETHOD("_codegen", generator); - }; + } - AST_Node.DEFMETHOD("print", function(stream, force_parens){ - var self = this, generator = self._codegen; - function doit() { - self.add_comments(stream); - self.add_source_map(stream); - generator(self, stream); - } + var use_asm = false; + + AST_Node.DEFMETHOD("print", function(stream, force_parens) { + var self = this; stream.push_node(self); if (force_parens || self.needs_parens(stream)) { stream.with_parens(doit); @@ -371,296 +632,374 @@ function OutputStream(options) { doit(); } stream.pop_node(); - }); - AST_Node.DEFMETHOD("print_to_string", function(options){ - var s = OutputStream(options); - this.print(s); - return s.get(); + function doit() { + stream.prepend_comments(self); + self.add_source_map(stream); + self._codegen(stream); + stream.append_comments(self); + } }); - - /* -----[ comments ]----- */ - - AST_Node.DEFMETHOD("add_comments", function(output){ - var c = output.option("comments"), self = this; - if (c) { - var start = self.start; - if (start && !start._comments_dumped) { - start._comments_dumped = true; - var comments = start.comments_before || []; - - // XXX: ugly fix for https://github.com/mishoo/UglifyJS2/issues/112 - // and https://github.com/mishoo/UglifyJS2/issues/372 - if (self instanceof AST_Exit && self.value) { - self.value.walk(new TreeWalker(function(node){ - if (node.start && node.start.comments_before) { - comments = comments.concat(node.start.comments_before); - node.start.comments_before = []; - } - if (node instanceof AST_Function || - node instanceof AST_Array || - node instanceof AST_Object) - { - return true; // don't go inside. - } - })); - } - - if (c.test) { - comments = comments.filter(function(comment){ - return c.test(comment.value); - }); - } else if (typeof c == "function") { - comments = comments.filter(function(comment){ - return c(self, comment); - }); - } - comments.forEach(function(c){ - if (/comment[134]/.test(c.type)) { - output.print("//" + c.value + "\n"); - output.indent(); - } - else if (c.type == "comment2") { - output.print("/*" + c.value + "*/"); - if (start.nlb) { - output.print("\n"); - output.indent(); - } else { - output.space(); - } - } - }); - } + var readonly = OutputStream({ + inline_script: false, + shebang: false, + width: false, + }); + AST_Node.DEFMETHOD("print_to_string", function(options) { + if (options) { + var stream = OutputStream(options); + this.print(stream); + return stream.get(); } + this.print(readonly); + return readonly.reset(); }); /* -----[ PARENTHESES ]----- */ function PARENS(nodetype, func) { - if (Array.isArray(nodetype)) { - nodetype.forEach(function(nodetype){ - PARENS(nodetype, func); - }); - } else { - nodetype.DEFMETHOD("needs_parens", func); - } - }; + nodetype.DEFMETHOD("needs_parens", func); + } - PARENS(AST_Node, function(){ - return false; - }); + PARENS(AST_Node, return_false); // a function expression needs parens around it when it's provably // the first token to appear in a statement. - PARENS(AST_Function, function(output){ - return first_in_statement(output); - }); + function needs_parens_function(output) { + var p = output.parent(); + if (!output.has_parens() && first_in_statement(output, false, true)) { + // export default function() {} + // export default (function foo() {}); + // export default (function() {})(foo); + // export default (function() {})`foo`; + // export default (function() {}) ? foo : bar; + return this.name || !(p instanceof AST_ExportDefault); + } + if (output.option("webkit") && p instanceof AST_PropAccess && p.expression === this) return true; + if (output.option("wrap_iife") && p instanceof AST_Call && p.expression === this) return true; + } + PARENS(AST_AsyncFunction, needs_parens_function); + PARENS(AST_AsyncGeneratorFunction, needs_parens_function); + PARENS(AST_ClassExpression, needs_parens_function); + PARENS(AST_Function, needs_parens_function); + PARENS(AST_GeneratorFunction, needs_parens_function); // same goes for an object literal, because otherwise it would be // interpreted as a block of code. - PARENS(AST_Object, function(output){ - return first_in_statement(output); - }); + function needs_parens_obj(output) { + return !output.has_parens() && first_in_statement(output, true); + } + PARENS(AST_Object, needs_parens_obj); - PARENS([ AST_Unary, AST_Undefined ], function(output){ + function needs_parens_unary(output) { var p = output.parent(); - return p instanceof AST_PropAccess && p.expression === this; - }); + // (-x) ** y + if (p instanceof AST_Binary) return p.operator == "**" && p.left === this; + // (await x)(y) + // new (await x) + if (p instanceof AST_Call) return p.expression === this; + // class extends (x++) {} + // class x extends (typeof y) {} + if (p instanceof AST_Class) return true; + // (x++)[y] + // (typeof x).y + // https://github.com/mishoo/UglifyJS/issues/115 + if (p instanceof AST_PropAccess) return p.expression === this; + // (~x)`foo` + if (p instanceof AST_Template) return p.tag === this; + } + PARENS(AST_Await, needs_parens_unary); + PARENS(AST_Unary, needs_parens_unary); - PARENS(AST_Seq, function(output){ + PARENS(AST_Sequence, function(output) { var p = output.parent(); - return p instanceof AST_Call // (foo, bar)() or foo(1, (2, 3), 4) - || p instanceof AST_Unary // !(foo, bar, baz) - || p instanceof AST_Binary // 1 + (2, 3) + 4 ==> 8 - || p instanceof AST_VarDef // var a = (1, 2), b = a + a; ==> b == 4 - || p instanceof AST_PropAccess // (1, {foo:2}).foo or (1, {foo:2})["foo"] ==> 2 - || p instanceof AST_Array // [ 1, (2, 3), 4 ] ==> [ 1, 3, 4 ] - || p instanceof AST_ObjectProperty // { foo: (1, 2) }.foo ==> 2 - || p instanceof AST_Conditional /* (false, true) ? (a = 10, b = 20) : (c = 30) - * ==> 20 (side effect, set a := 10 and b := 20) */ - ; - }); - - PARENS(AST_Binary, function(output){ + // [ 1, (2, 3), 4 ] ---> [ 1, 3, 4 ] + return p instanceof AST_Array + // () ---> (foo, bar) + || is_arrow(p) && p.value === this + // await (foo, bar) + || p instanceof AST_Await + // 1 + (2, 3) + 4 ---> 8 + || p instanceof AST_Binary + // new (foo, bar) or foo(1, (2, 3), 4) + || p instanceof AST_Call + // class extends (foo, bar) {} + // class foo extends (bar, baz) {} + || p instanceof AST_Class + // class { foo = (bar, baz) } + // class { [(foo, bar)]() {} } + || p instanceof AST_ClassProperty + // (false, true) ? (a = 10, b = 20) : (c = 30) + // ---> 20 (side effect, set a := 10 and b := 20) + || p instanceof AST_Conditional + // [ a = (1, 2) ] = [] ---> a == 2 + || p instanceof AST_DefaultValue + // { [(1, 2)]: foo } = bar + // { 1: (2, foo) } = bar + || p instanceof AST_DestructuredKeyVal + // export default (foo, bar) + || p instanceof AST_ExportDefault + // for (foo of (bar, baz)); + || p instanceof AST_ForOf + // { [(1, 2)]: 3 }[2] ---> 3 + // { foo: (1, 2) }.foo ---> 2 + || p instanceof AST_ObjectProperty + // (1, {foo:2}).foo or (1, {foo:2})["foo"] ---> 2 + || p instanceof AST_PropAccess && p.expression === this + // ...(foo, bar, baz) + || p instanceof AST_Spread + // (foo, bar)`baz` + || p instanceof AST_Template && p.tag === this + // !(foo, bar, baz) + || p instanceof AST_Unary + // var a = (1, 2), b = a + a; ---> b == 4 + || p instanceof AST_VarDef + // yield (foo, bar) + || p instanceof AST_Yield; + }); + + PARENS(AST_Binary, function(output) { var p = output.parent(); - // (foo && bar)() - if (p instanceof AST_Call && p.expression === this) - return true; - // typeof (foo && bar) - if (p instanceof AST_Unary) - return true; - // (foo && bar)["prop"], (foo && bar).prop - if (p instanceof AST_PropAccess && p.expression === this) - return true; - // this deals with precedence: 3 * (2 + 1) + // await (foo && bar) + if (p instanceof AST_Await) return true; + // this deals with precedence: + // 3 * (2 + 1) + // 3 - (2 - 1) + // (1 ** 2) ** 3 if (p instanceof AST_Binary) { var po = p.operator, pp = PRECEDENCE[po]; var so = this.operator, sp = PRECEDENCE[so]; - if (pp > sp - || (pp == sp - && this === p.right)) { - return true; - } - } - }); - - PARENS(AST_PropAccess, function(output){ - var p = output.parent(); - if (p instanceof AST_New && p.expression === this) { - // i.e. new (foo.bar().baz) - // - // if there's one call into this subtree, then we need - // parens around it too, otherwise the call will be - // interpreted as passing the arguments to the upper New - // expression. - try { - this.walk(new TreeWalker(function(node){ - if (node instanceof AST_Call) throw p; - })); - } catch(ex) { - if (ex !== p) throw ex; - return true; - } + return pp > sp + || po == "??" && (so == "&&" || so == "||") + || (pp == sp && this === p[po == "**" ? "left" : "right"]); } + // (foo && bar)() + if (p instanceof AST_Call) return p.expression === this; + // class extends (foo && bar) {} + // class foo extends (bar || null) {} + if (p instanceof AST_Class) return true; + // (foo && bar)["prop"], (foo && bar).prop + if (p instanceof AST_PropAccess) return p.expression === this; + // (foo && bar)`` + if (p instanceof AST_Template) return p.tag === this; + // typeof (foo && bar) + if (p instanceof AST_Unary) return true; }); - PARENS(AST_Call, function(output){ - var p = output.parent(), p1; - if (p instanceof AST_New && p.expression === this) - return true; + function need_chain_parens(node, parent) { + if (!node.terminal) return false; + if (!(parent instanceof AST_Call || parent instanceof AST_PropAccess)) return false; + return parent.expression === node; + } - // workaround for Safari bug. + PARENS(AST_PropAccess, function(output) { + var node = this; + var p = output.parent(); + // i.e. new (foo().bar) + // + // if there's one call into this subtree, then we need + // parens around it too, otherwise the call will be + // interpreted as passing the arguments to the upper New + // expression. + if (p instanceof AST_New && p.expression === node && root_expr(node).TYPE == "Call") return true; + // (foo?.bar)() + // (foo?.bar).baz + // new (foo?.bar)() + return need_chain_parens(node, p); + }); + + PARENS(AST_Call, function(output) { + var node = this; + var p = output.parent(); + if (p instanceof AST_New) return p.expression === node; // https://bugs.webkit.org/show_bug.cgi?id=123506 - return this.expression instanceof AST_Function + if (output.option("webkit") + && node.expression instanceof AST_Function && p instanceof AST_PropAccess - && p.expression === this - && (p1 = output.parent(1)) instanceof AST_Assign - && p1.left === p; + && p.expression === node) { + var g = output.parent(1); + if (g instanceof AST_Assign && g.left === p) return true; + } + // (foo?.())() + // (foo?.()).bar + // new (foo?.())() + return need_chain_parens(node, p); }); - PARENS(AST_New, function(output){ + PARENS(AST_New, function(output) { + if (need_constructor_parens(this, output)) return false; var p = output.parent(); - if (no_constructor_parens(this, output) - && (p instanceof AST_PropAccess // (new Date).getTime(), (new Date)["getTime"]() - || p instanceof AST_Call && p.expression === this)) // (new foo)(bar) - return true; + // (new foo)(bar) + if (p instanceof AST_Call) return p.expression === this; + // (new Date).getTime(), (new Date)["getTime"]() + if (p instanceof AST_PropAccess) return true; + // (new foo)`bar` + if (p instanceof AST_Template) return p.tag === this; }); - PARENS(AST_Number, function(output){ + PARENS(AST_Number, function(output) { + if (!output.option("galio")) return false; + // https://github.com/mishoo/UglifyJS/pull/1009 var p = output.parent(); - if (this.getValue() < 0 && p instanceof AST_PropAccess && p.expression === this) - return true; + return p instanceof AST_PropAccess && p.expression === this && /^0/.test(make_num(this.value)); }); - PARENS([ AST_Assign, AST_Conditional ], function (output){ + function needs_parens_assign_cond(self, output) { var p = output.parent(); - // !(a = false) → true - if (p instanceof AST_Unary) - return true; + // await (a = foo) + if (p instanceof AST_Await) return true; // 1 + (a = 2) + 3 → 6, side effect setting a = 2 - if (p instanceof AST_Binary && !(p instanceof AST_Assign)) - return true; + if (p instanceof AST_Binary) return !(p instanceof AST_Assign); // (a = func)() —or— new (a = Object)() - if (p instanceof AST_Call && p.expression === this) - return true; + if (p instanceof AST_Call) return p.expression === self; + // class extends (a = foo) {} + // class foo extends (bar ? baz : moo) {} + if (p instanceof AST_Class) return true; // (a = foo) ? bar : baz - if (p instanceof AST_Conditional && p.condition === this) - return true; + if (p instanceof AST_Conditional) return p.condition === self; // (a = foo)["prop"] —or— (a = foo).prop - if (p instanceof AST_PropAccess && p.expression === this) - return true; + if (p instanceof AST_PropAccess) return p.expression === self; + // (a = foo)`bar` + if (p instanceof AST_Template) return p.tag === self; + // !(a = false) → true + if (p instanceof AST_Unary) return true; + } + PARENS(AST_Arrow, function(output) { + return needs_parens_assign_cond(this, output); + }); + PARENS(AST_Assign, function(output) { + if (needs_parens_assign_cond(this, output)) return true; + // v8 parser bug ---> workaround + // f([1], [a] = []) ---> f([1], ([a] = [])) + if (output.option("v8")) return this.left instanceof AST_Destructured; + // ({ p: a } = o); + if (this.left instanceof AST_DestructuredObject) return needs_parens_obj(output); + }); + PARENS(AST_AsyncArrow, function(output) { + return needs_parens_assign_cond(this, output); + }); + PARENS(AST_Conditional, function(output) { + return needs_parens_assign_cond(this, output) + // https://github.com/mishoo/UglifyJS/issues/1144 + || output.option("extendscript") && output.parent() instanceof AST_Conditional; + }); + PARENS(AST_Yield, function(output) { + return needs_parens_assign_cond(this, output); }); /* -----[ PRINTERS ]----- */ - DEFPRINT(AST_Directive, function(self, output){ - output.print_string(self.value); + DEFPRINT(AST_Directive, function(output) { + var quote = this.quote; + var value = this.value; + switch (output.option("quote_style")) { + case 0: + case 2: + if (value.indexOf('"') == -1) quote = '"'; + break; + case 1: + if (value.indexOf("'") == -1) quote = "'"; + break; + } + output.print(quote + value + quote); output.semicolon(); }); - DEFPRINT(AST_Debugger, function(self, output){ + DEFPRINT(AST_Debugger, function(output) { output.print("debugger"); output.semicolon(); }); /* -----[ statements ]----- */ - function display_body(body, is_toplevel, output) { + function display_body(body, is_toplevel, output, allow_directives) { var last = body.length - 1; - body.forEach(function(stmt, i){ - if (!(stmt instanceof AST_EmptyStatement)) { - output.indent(); - stmt.print(output); - if (!(i == last && is_toplevel)) { - output.newline(); - if (is_toplevel) output.newline(); + var in_directive = allow_directives; + var was_asm = use_asm; + body.forEach(function(stmt, i) { + if (in_directive) { + if (stmt instanceof AST_Directive) { + if (stmt.value == "use asm") use_asm = true; + } else if (!(stmt instanceof AST_EmptyStatement)) { + if (stmt instanceof AST_SimpleStatement && stmt.body instanceof AST_String) { + output.force_semicolon(); + } + in_directive = false; } } + if (stmt instanceof AST_EmptyStatement) return; + output.indent(); + stmt.print(output); + if (i == last && is_toplevel) return; + output.newline(); + if (is_toplevel) output.newline(); }); - }; - - AST_StatementWithBody.DEFMETHOD("_do_print_body", function(output){ - force_statement(this.body, output); - }); + use_asm = was_asm; + } - DEFPRINT(AST_Statement, function(self, output){ - self.body.print(output); - output.semicolon(); - }); - DEFPRINT(AST_Toplevel, function(self, output){ - display_body(self.body, true, output); + DEFPRINT(AST_Toplevel, function(output) { + display_body(this.body, true, output, true); output.print(""); }); - DEFPRINT(AST_LabeledStatement, function(self, output){ - self.label.print(output); + DEFPRINT(AST_LabeledStatement, function(output) { + this.label.print(output); output.colon(); - self.body.print(output); + this.body.print(output); }); - DEFPRINT(AST_SimpleStatement, function(self, output){ - self.body.print(output); + DEFPRINT(AST_SimpleStatement, function(output) { + this.body.print(output); output.semicolon(); }); - function print_bracketed(body, output) { - if (body.length > 0) output.with_block(function(){ - display_body(body, false, output); + function print_braced_empty(self, output) { + output.print("{"); + output.with_indent(function() { + output.append_comments(self, true); }); - else output.print("{}"); - }; - DEFPRINT(AST_BlockStatement, function(self, output){ - print_bracketed(self.body, output); + output.print("}"); + } + function print_braced(self, output, allow_directives) { + if (self.body.length > 0) { + output.with_block(function() { + display_body(self.body, false, output, allow_directives); + }, self.end); + } else print_braced_empty(self, output); + } + DEFPRINT(AST_BlockStatement, function(output) { + print_braced(this, output); }); - DEFPRINT(AST_EmptyStatement, function(self, output){ + DEFPRINT(AST_EmptyStatement, function(output) { output.semicolon(); }); - DEFPRINT(AST_Do, function(self, output){ + DEFPRINT(AST_Do, function(output) { + var self = this; output.print("do"); - output.space(); - self._do_print_body(output); + make_block(self.body, output); output.space(); output.print("while"); output.space(); - output.with_parens(function(){ + output.with_parens(function() { self.condition.print(output); }); output.semicolon(); }); - DEFPRINT(AST_While, function(self, output){ + DEFPRINT(AST_While, function(output) { + var self = this; output.print("while"); output.space(); - output.with_parens(function(){ + output.with_parens(function() { self.condition.print(output); }); - output.space(); - self._do_print_body(output); + force_statement(self.body, output); }); - DEFPRINT(AST_For, function(self, output){ + DEFPRINT(AST_For, function(output) { + var self = this; output.print("for"); output.space(); - output.with_parens(function(){ - if (self.init && !(self.init instanceof AST_EmptyStatement)) { + output.with_parens(function() { + if (self.init) { if (self.init instanceof AST_Definitions) { self.init.print(output); } else { - parenthesize_for_noin(self.init, output, true); + parenthesize_for_no_in(self.init, output, true); } output.print(";"); output.space(); @@ -678,112 +1017,295 @@ function OutputStream(options) { self.step.print(output); } }); - output.space(); - self._do_print_body(output); + force_statement(self.body, output); }); - DEFPRINT(AST_ForIn, function(self, output){ - output.print("for"); - output.space(); - output.with_parens(function(){ - self.init.print(output); - output.space(); - output.print("in"); + function print_for_enum(prefix, infix) { + return function(output) { + var self = this; + output.print(prefix); output.space(); - self.object.print(output); - }); - output.space(); - self._do_print_body(output); - }); - DEFPRINT(AST_With, function(self, output){ + output.with_parens(function() { + self.init.print(output); + output.space(); + output.print(infix); + output.space(); + self.object.print(output); + }); + force_statement(self.body, output); + }; + } + DEFPRINT(AST_ForAwaitOf, print_for_enum("for await", "of")); + DEFPRINT(AST_ForIn, print_for_enum("for", "in")); + DEFPRINT(AST_ForOf, print_for_enum("for", "of")); + DEFPRINT(AST_With, function(output) { + var self = this; output.print("with"); output.space(); - output.with_parens(function(){ + output.with_parens(function() { self.expression.print(output); }); + force_statement(self.body, output); + }); + DEFPRINT(AST_ExportDeclaration, function(output) { + output.print("export"); output.space(); - self._do_print_body(output); + this.body.print(output); }); - - /* -----[ functions ]----- */ - AST_Lambda.DEFMETHOD("_do_print", function(output, nokeyword){ - var self = this; - if (!nokeyword) { - output.print("function"); + DEFPRINT(AST_ExportDefault, function(output) { + output.print("export"); + output.space(); + output.print("default"); + output.space(); + var body = this.body; + body.print(output); + if (body instanceof AST_ClassExpression) { + if (!body.name) return; } - if (self.name) { - output.space(); - self.name.print(output); + if (body instanceof AST_DefClass) return; + if (body instanceof AST_LambdaDefinition) return; + if (body instanceof AST_LambdaExpression) { + if (!body.name && !is_arrow(body)) return; } - output.with_parens(function(){ - self.argnames.forEach(function(arg, i){ - if (i) output.comma(); - arg.print(output); - }); - }); - output.space(); - print_bracketed(self.body, output); - }); - DEFPRINT(AST_Lambda, function(self, output){ - self._do_print(output); + output.semicolon(); }); + function print_alias(alias, output) { + var value = alias.value; + if (value == "*" || is_identifier_string(value)) { + output.print_name(value); + } else { + output.print_string(value, alias.quote); + } + } + DEFPRINT(AST_ExportForeign, function(output) { + var self = this; + output.print("export"); + output.space(); + var len = self.keys.length; + if (len == 0) { + print_braced_empty(self, output); + } else if (self.keys[0].value == "*") { + print_entry(0); + } else output.with_block(function() { + output.indent(); + print_entry(0); + for (var i = 1; i < len; i++) { + output.print(","); + output.newline(); + output.indent(); + print_entry(i); + } + output.newline(); + }, self.end); + output.space(); + output.print("from"); + output.space(); + self.path.print(output); + output.semicolon(); - /* -----[ exits ]----- */ - AST_Exit.DEFMETHOD("_do_print", function(output, kind){ - output.print(kind); - if (this.value) { + function print_entry(index) { + var alias = self.aliases[index]; + var key = self.keys[index]; + print_alias(key, output); + if (alias.value != key.value) { + output.space(); + output.print("as"); + output.space(); + print_alias(alias, output); + } + } + }); + DEFPRINT(AST_ExportReferences, function(output) { + var self = this; + output.print("export"); + output.space(); + print_properties(self, output); + output.semicolon(); + }); + DEFPRINT(AST_Import, function(output) { + var self = this; + output.print("import"); + output.space(); + if (self.default) self.default.print(output); + if (self.all) { + if (self.default) output.comma(); + self.all.print(output); + } + if (self.properties) { + if (self.default) output.comma(); + print_properties(self, output); + } + if (self.all || self.default || self.properties) { + output.space(); + output.print("from"); output.space(); - this.value.print(output); } + self.path.print(output); output.semicolon(); }); - DEFPRINT(AST_Return, function(self, output){ - self._do_print(output, "return"); + + /* -----[ functions ]----- */ + function print_funargs(self, output) { + output.with_parens(function() { + self.argnames.forEach(function(arg, i) { + if (i) output.comma(); + arg.print(output); + }); + if (self.rest) { + if (self.argnames.length) output.comma(); + output.print("..."); + self.rest.print(output); + } + }); + } + function print_arrow(self, output) { + var argname = self.argnames.length == 1 && !self.rest && self.argnames[0]; + if (argname instanceof AST_SymbolFunarg && argname.name != "yield") { + argname.print(output); + } else { + print_funargs(self, output); + } + output.space(); + output.print("=>"); + output.space(); + if (self.value) { + self.value.print(output); + } else { + print_braced(self, output, true); + } + } + DEFPRINT(AST_Arrow, function(output) { + print_arrow(this, output); + }); + DEFPRINT(AST_AsyncArrow, function(output) { + output.print("async"); + output.space(); + print_arrow(this, output); }); - DEFPRINT(AST_Throw, function(self, output){ - self._do_print(output, "throw"); + function print_lambda(self, output) { + if (self.name) { + output.space(); + self.name.print(output); + } + print_funargs(self, output); + output.space(); + print_braced(self, output, true); + } + DEFPRINT(AST_Lambda, function(output) { + output.print("function"); + print_lambda(this, output); }); + function print_async(output) { + output.print("async"); + output.space(); + output.print("function"); + print_lambda(this, output); + } + DEFPRINT(AST_AsyncDefun, print_async); + DEFPRINT(AST_AsyncFunction, print_async); + function print_async_generator(output) { + output.print("async"); + output.space(); + output.print("function*"); + print_lambda(this, output); + } + DEFPRINT(AST_AsyncGeneratorDefun, print_async_generator); + DEFPRINT(AST_AsyncGeneratorFunction, print_async_generator); + function print_generator(output) { + output.print("function*"); + print_lambda(this, output); + } + DEFPRINT(AST_GeneratorDefun, print_generator); + DEFPRINT(AST_GeneratorFunction, print_generator); - /* -----[ loop control ]----- */ - AST_LoopControl.DEFMETHOD("_do_print", function(output, kind){ - output.print(kind); - if (this.label) { + /* -----[ classes ]----- */ + DEFPRINT(AST_Class, function(output) { + var self = this; + output.print("class"); + if (self.name) { + output.space(); + self.name.print(output); + } + if (self.extends) { + output.space(); + output.print("extends"); + output.space(); + self.extends.print(output); + } + output.space(); + print_properties(self, output, true); + }); + DEFPRINT(AST_ClassField, function(output) { + var self = this; + if (self.static) { + output.print("static"); output.space(); - this.label.print(output); + } + print_property_key(self, output); + if (self.value) { + output.space(); + output.print("="); + output.space(); + self.value.print(output); } output.semicolon(); }); - DEFPRINT(AST_Break, function(self, output){ - self._do_print(output, "break"); + DEFPRINT(AST_ClassGetter, print_accessor("get")); + DEFPRINT(AST_ClassSetter, print_accessor("set")); + function print_method(self, output) { + var fn = self.value; + if (is_async(fn)) { + output.print("async"); + output.space(); + } + if (is_generator(fn)) output.print("*"); + print_property_key(self, output); + print_lambda(self.value, output); + } + DEFPRINT(AST_ClassMethod, function(output) { + var self = this; + if (self.static) { + output.print("static"); + output.space(); + } + print_method(self, output); }); - DEFPRINT(AST_Continue, function(self, output){ - self._do_print(output, "continue"); + DEFPRINT(AST_ClassInit, function(output) { + output.print("static"); + output.space(); + print_braced(this.value, output); }); + /* -----[ jumps ]----- */ + function print_jump(kind, prop) { + return function(output) { + output.print(kind); + var target = this[prop]; + if (target) { + output.space(); + target.print(output); + } + output.semicolon(); + }; + } + DEFPRINT(AST_Return, print_jump("return", "value")); + DEFPRINT(AST_Throw, print_jump("throw", "value")); + DEFPRINT(AST_Break, print_jump("break", "label")); + DEFPRINT(AST_Continue, print_jump("continue", "label")); + /* -----[ if ]----- */ function make_then(self, output) { - if (output.option("bracketize")) { - make_block(self.body, output); - return; - } + var b = self.body; + if (output.option("braces") && !(b instanceof AST_Const || b instanceof AST_Let) + || output.option("ie") && b instanceof AST_Do) + return make_block(b, output); // The squeezer replaces "block"-s that contain only a single // statement with the statement itself; technically, the AST // is correct, but this can create problems when we output an // IF having an ELSE clause where the THEN clause ends in an // IF *without* an ELSE block (then the outer ELSE would refer // to the inner IF). This function checks for this case and - // adds the block brackets if needed. - if (!self.body) - return output.force_semicolon(); - if (self.body instanceof AST_Do - && !output.option("screw_ie8")) { - // https://github.com/mishoo/UglifyJS/issues/#issue/57 IE - // croaks with "syntax error" on code like this: if (foo) - // do ... while(cond); else ... we need block brackets - // around do/while - make_block(self.body, output); - return; - } - var b = self.body; + // adds the block braces if needed. + if (!b) return output.force_semicolon(); while (true) { if (b instanceof AST_If) { if (!b.alternative) { @@ -791,76 +1313,81 @@ function OutputStream(options) { return; } b = b.alternative; - } - else if (b instanceof AST_StatementWithBody) { + } else if (b instanceof AST_StatementWithBody) { b = b.body; - } - else break; + } else break; } force_statement(self.body, output); - }; - DEFPRINT(AST_If, function(self, output){ + } + DEFPRINT(AST_If, function(output) { + var self = this; output.print("if"); output.space(); - output.with_parens(function(){ + output.with_parens(function() { self.condition.print(output); }); - output.space(); if (self.alternative) { make_then(self, output); output.space(); output.print("else"); - output.space(); - force_statement(self.alternative, output); + if (self.alternative instanceof AST_If) { + output.space(); + self.alternative.print(output); + } else { + force_statement(self.alternative, output); + } } else { - self._do_print_body(output); + force_statement(self.body, output); } }); /* -----[ switch ]----- */ - DEFPRINT(AST_Switch, function(self, output){ + DEFPRINT(AST_Switch, function(output) { + var self = this; output.print("switch"); output.space(); - output.with_parens(function(){ + output.with_parens(function() { self.expression.print(output); }); output.space(); - if (self.body.length > 0) output.with_block(function(){ - self.body.forEach(function(stmt, i){ - if (i) output.newline(); + var last = self.body.length - 1; + if (last < 0) print_braced_empty(self, output); + else output.with_block(function() { + self.body.forEach(function(branch, i) { output.indent(true); - stmt.print(output); + branch.print(output); + if (i < last && branch.body.length > 0) + output.newline(); }); - }); - else output.print("{}"); + }, self.end); }); - AST_SwitchBranch.DEFMETHOD("_do_print_body", function(output){ - if (this.body.length > 0) { + function print_branch_body(self, output) { + output.newline(); + self.body.forEach(function(stmt) { + output.indent(); + stmt.print(output); output.newline(); - this.body.forEach(function(stmt){ - output.indent(); - stmt.print(output); - output.newline(); - }); - } - }); - DEFPRINT(AST_Default, function(self, output){ + }); + } + DEFPRINT(AST_Default, function(output) { output.print("default:"); - self._do_print_body(output); + print_branch_body(this, output); }); - DEFPRINT(AST_Case, function(self, output){ + DEFPRINT(AST_Case, function(output) { + var self = this; output.print("case"); output.space(); self.expression.print(output); output.print(":"); - self._do_print_body(output); + print_branch_body(self, output); }); /* -----[ exceptions ]----- */ - DEFPRINT(AST_Try, function(self, output){ + DEFPRINT(AST_Try, function(output) { + var self = this; output.print("try"); output.space(); - print_bracketed(self.body, output); + print_braced(self, output); if (self.bcatch) { output.space(); self.bcatch.print(output); @@ -870,163 +1397,182 @@ function OutputStream(options) { self.bfinally.print(output); } }); - DEFPRINT(AST_Catch, function(self, output){ + DEFPRINT(AST_Catch, function(output) { + var self = this; output.print("catch"); + if (self.argname) { + output.space(); + output.with_parens(function() { + self.argname.print(output); + }); + } output.space(); - output.with_parens(function(){ - self.argname.print(output); - }); - output.space(); - print_bracketed(self.body, output); + print_braced(self, output); }); - DEFPRINT(AST_Finally, function(self, output){ + DEFPRINT(AST_Finally, function(output) { output.print("finally"); output.space(); - print_bracketed(self.body, output); + print_braced(this, output); }); - /* -----[ var/const ]----- */ - AST_Definitions.DEFMETHOD("_do_print", function(output, kind){ - output.print(kind); - output.space(); - this.definitions.forEach(function(def, i){ - if (i) output.comma(); - def.print(output); - }); - var p = output.parent(); - var in_for = p instanceof AST_For || p instanceof AST_ForIn; - var avoid_semicolon = in_for && p.init === this; - if (!avoid_semicolon) - output.semicolon(); - }); - DEFPRINT(AST_Var, function(self, output){ - self._do_print(output, "var"); - }); - DEFPRINT(AST_Const, function(self, output){ - self._do_print(output, "const"); - }); - - function parenthesize_for_noin(node, output, noin) { - if (!noin) node.print(output); - else try { - // need to take some precautions here: - // https://github.com/mishoo/UglifyJS2/issues/60 - node.walk(new TreeWalker(function(node){ - if (node instanceof AST_Binary && node.operator == "in") - throw output; - })); - node.print(output); - } catch(ex) { - if (ex !== output) throw ex; - node.print(output, true); - } - }; + function print_definitions(type) { + return function(output) { + var self = this; + output.print(type); + output.space(); + self.definitions.forEach(function(def, i) { + if (i) output.comma(); + def.print(output); + }); + var p = output.parent(); + if (!(p instanceof AST_IterationStatement && p.init === self)) output.semicolon(); + }; + } + DEFPRINT(AST_Const, print_definitions("const")); + DEFPRINT(AST_Let, print_definitions("let")); + DEFPRINT(AST_Var, print_definitions("var")); + + function parenthesize_for_no_in(node, output, no_in) { + var parens = false; + // need to take some precautions here: + // https://github.com/mishoo/UglifyJS/issues/60 + if (no_in) node.walk(new TreeWalker(function(node) { + if (parens) return true; + if (node instanceof AST_Binary && node.operator == "in") return parens = true; + if (node instanceof AST_Scope && !(is_arrow(node) && node.value)) return true; + })); + node.print(output, parens); + } - DEFPRINT(AST_VarDef, function(self, output){ + DEFPRINT(AST_VarDef, function(output) { + var self = this; self.name.print(output); if (self.value) { output.space(); output.print("="); output.space(); var p = output.parent(1); - var noin = p instanceof AST_For || p instanceof AST_ForIn; - parenthesize_for_noin(self.value, output, noin); + var no_in = p instanceof AST_For || p instanceof AST_ForEnumeration; + parenthesize_for_no_in(self.value, output, no_in); } }); + DEFPRINT(AST_DefaultValue, function(output) { + var self = this; + self.name.print(output); + output.space(); + output.print("="); + output.space(); + self.value.print(output); + }); + /* -----[ other expressions ]----- */ - DEFPRINT(AST_Call, function(self, output){ - self.expression.print(output); - if (self instanceof AST_New && no_constructor_parens(self, output)) - return; - output.with_parens(function(){ - self.args.forEach(function(expr, i){ + function print_annotation(self, output) { + if (!output.option("annotations")) return; + if (!self.pure) return; + var level = 0, parent = self, node; + do { + node = parent; + parent = output.parent(level++); + if (parent instanceof AST_Call && parent.expression === node) return; + } while (parent instanceof AST_PropAccess && parent.expression === node); + output.print("/*@__PURE__*/"); + } + function print_call_args(self, output) { + output.with_parens(function() { + self.args.forEach(function(expr, i) { if (i) output.comma(); expr.print(output); }); + output.add_mapping(self.end); }); + } + DEFPRINT(AST_Call, function(output) { + var self = this; + print_annotation(self, output); + self.expression.print(output); + if (self.optional) output.print("?."); + print_call_args(self, output); }); - DEFPRINT(AST_New, function(self, output){ + DEFPRINT(AST_New, function(output) { + var self = this; + print_annotation(self, output); output.print("new"); output.space(); - AST_Call.prototype._codegen(self, output); + self.expression.print(output); + if (need_constructor_parens(self, output)) print_call_args(self, output); }); - - AST_Seq.DEFMETHOD("_do_print", function(output){ - this.car.print(output); - if (this.cdr) { - output.comma(); - if (output.should_break()) { - output.newline(); - output.indent(); + DEFPRINT(AST_Sequence, function(output) { + this.expressions.forEach(function(node, index) { + if (index > 0) { + output.comma(); + if (output.should_break()) { + output.newline(); + output.indent(); + } } - this.cdr.print(output); - } + node.print(output); + }); }); - DEFPRINT(AST_Seq, function(self, output){ - self._do_print(output); - // var p = output.parent(); - // if (p instanceof AST_Statement) { - // output.with_indent(output.next_indent(), function(){ - // self._do_print(output); - // }); - // } else { - // self._do_print(output); - // } - }); - DEFPRINT(AST_Dot, function(self, output){ + DEFPRINT(AST_Dot, function(output) { + var self = this; var expr = self.expression; expr.print(output); - if (expr instanceof AST_Number && expr.getValue() >= 0) { - if (!/[xa-f.]/i.test(output.last())) { - output.print("."); - } + var prop = self.property; + if (output.option("ie") && RESERVED_WORDS[prop] || self.quoted && output.option("keep_quoted_props")) { + if (self.optional) output.print("?."); + output.with_square(function() { + output.add_mapping(self.end); + output.print_string(prop); + }); + } else { + if (expr instanceof AST_Number && !/[ex.)]/i.test(output.last())) output.print("."); + output.print(self.optional ? "?." : "."); + // the name after dot would be mapped about here. + output.add_mapping(self.end); + output.print_name(prop); } - output.print("."); - // the name after dot would be mapped about here. - output.add_mapping(self.end); - output.print_name(self.property); }); - DEFPRINT(AST_Sub, function(self, output){ + DEFPRINT(AST_Sub, function(output) { + var self = this; self.expression.print(output); - output.print("["); - self.property.print(output); - output.print("]"); + if (self.optional) output.print("?."); + output.with_square(function() { + self.property.print(output); + }); + }); + DEFPRINT(AST_Spread, function(output) { + output.print("..."); + this.expression.print(output); }); - DEFPRINT(AST_UnaryPrefix, function(self, output){ - var op = self.operator; + DEFPRINT(AST_UnaryPrefix, function(output) { + var op = this.operator; + var exp = this.expression; output.print(op); if (/^[a-z]/i.test(op) || (/[+-]$/.test(op) - && self.expression instanceof AST_UnaryPrefix - && /^[+-]/.test(self.expression.operator))) { + && exp instanceof AST_UnaryPrefix + && /^[+-]/.test(exp.operator))) { output.space(); } - self.expression.print(output); + exp.print(output); }); - DEFPRINT(AST_UnaryPostfix, function(self, output){ + DEFPRINT(AST_UnaryPostfix, function(output) { + var self = this; self.expression.print(output); + output.add_mapping(self.end); output.print(self.operator); }); - DEFPRINT(AST_Binary, function(self, output){ + DEFPRINT(AST_Binary, function(output) { + var self = this; self.left.print(output); output.space(); output.print(self.operator); - if (self.operator == "<" - && self.right instanceof AST_UnaryPrefix - && self.right.operator == "!" - && self.right.expression instanceof AST_UnaryPrefix - && self.right.expression.operator == "--") { - // space is mandatory to avoid outputting ") && S.newline_before) { + forward(3); + skip_line_comment("comment4"); + continue; + } } - if (looking_at("-->") && S.newline_before) { - forward(3); - return skip_line_comment("comment4"); + var ch = peek(); + if (!ch) return token("eof"); + var code = ch.charCodeAt(0); + switch (code) { + case 34: case 39: return read_string(ch); + case 46: return handle_dot(); + case 47: + var tok = handle_slash(); + if (tok === next_token) continue; + return tok; } + if (is_digit(code)) return read_num(); + if (PUNC_CHARS[ch]) return token("punc", next()); + if (looking_at("=>")) return token("punc", next() + next()); + if (OPERATOR_CHARS[ch]) return read_operator(); + if (code == 35 || code == 92 || !NON_IDENTIFIER_CHARS[ch]) return read_word(); + break; } - var ch = peek(); - if (!ch) return token("eof"); - var code = ch.charCodeAt(0); - switch (code) { - case 34: case 39: return read_string(); - case 46: return handle_dot(); - case 47: return handle_slash(); - } - if (is_digit(code)) return read_num(); - if (PUNC_CHARS(ch)) return token("punc", next()); - if (OPERATOR_CHARS(ch)) return read_operator(); - if (code == 92 || is_identifier_start(code)) return read_word(); parse_error("Unexpected character '" + ch + "'"); - }; + } next_token.context = function(nc) { if (nc) S = nc; return S; }; - return next_token; + next_token.add_directive = function(directive) { + S.directives[directive] = true; + } + + next_token.push_directives_stack = function() { + S.directives = Object.create(S.directives); + } -}; + next_token.pop_directives_stack = function() { + S.directives = Object.getPrototypeOf(S.directives); + } + + next_token.has_directive = function(directive) { + return !!S.directives[directive]; + } + + return next_token; +} /* -----[ Parser (constants) ]----- */ -var UNARY_PREFIX = makePredicate([ - "typeof", - "void", - "delete", - "--", - "++", - "!", - "~", - "-", - "+" -]); +var UNARY_PREFIX = makePredicate("typeof void delete -- ++ ! ~ - +"); -var UNARY_POSTFIX = makePredicate([ "--", "++" ]); +var UNARY_POSTFIX = makePredicate("-- ++"); -var ASSIGNMENT = makePredicate([ "=", "+=", "-=", "/=", "*=", "%=", ">>=", "<<=", ">>>=", "|=", "^=", "&=" ]); +var ASSIGNMENT = makePredicate("= += -= /= *= %= **= >>= <<= >>>= &= |= ^= &&= ||= ??="); -var PRECEDENCE = (function(a, ret){ - for (var i = 0; i < a.length; ++i) { - var b = a[i]; - for (var j = 0; j < b.length; ++j) { - ret[b[j]] = i + 1; +var PRECEDENCE = function(a, ret) { + for (var i = 0; i < a.length;) { + var b = a[i++]; + for (var j = 0; j < b.length; j++) { + ret[b[j]] = i; } } return ret; -})( - [ - ["||"], - ["&&"], - ["|"], - ["^"], - ["&"], - ["==", "===", "!=", "!=="], - ["<", ">", "<=", ">=", "in", "instanceof"], - [">>", "<<", ">>>"], - ["+", "-"], - ["*", "/", "%"] - ], - {} -); - -var STATEMENTS_WITH_LABELS = array_to_hash([ "for", "do", "while", "switch" ]); - -var ATOMIC_START_TOKEN = array_to_hash([ "atom", "num", "string", "regexp", "name" ]); +}([ + ["??"], + ["||"], + ["&&"], + ["|"], + ["^"], + ["&"], + ["==", "===", "!=", "!=="], + ["<", ">", "<=", ">=", "in", "instanceof"], + [">>", "<<", ">>>"], + ["+", "-"], + ["*", "/", "%"], + ["**"], +], {}); + +var ATOMIC_START_TOKEN = makePredicate("atom bigint num regexp string"); /* -----[ Parser ]----- */ function parse($TEXT, options) { - options = defaults(options, { - strict : false, - filename : null, - toplevel : null, + bare_returns : false, expression : false, + filename : null, html5_comments : true, - bare_returns : false, - }); + module : false, + shebang : true, + strict : false, + toplevel : null, + }, true); var S = { - input : (typeof $TEXT == "string" - ? tokenizer($TEXT, options.filename, - options.html5_comments) - : $TEXT), - token : null, - prev : null, - peeked : null, - in_function : 0, + input : typeof $TEXT == "string" + ? tokenizer($TEXT, options.filename, options.html5_comments, options.shebang) + : $TEXT, + in_async : false, in_directives : true, + in_funarg : -1, + in_function : 0, + in_generator : false, in_loop : 0, - labels : [] + labels : [], + peeked : null, + prev : null, + token : null, }; S.token = next(); function is(type, value) { return is_token(S.token, type, value); - }; + } - function peek() { return S.peeked || (S.peeked = S.input()); }; + function peek() { + return S.peeked || (S.peeked = S.input()); + } function next() { S.prev = S.token; @@ -660,11 +733,11 @@ function parse($TEXT, options) { S.token.type == "string" || is("punc", ";") ); return S.token; - }; + } function prev() { return S.prev; - }; + } function croak(msg, line, col, pos) { var ctx = S.input.context(); @@ -673,80 +746,131 @@ function parse($TEXT, options) { line != null ? line : ctx.tokline, col != null ? col : ctx.tokcol, pos != null ? pos : ctx.tokpos); - }; + } function token_error(token, msg) { croak(msg, token.line, token.col); - }; + } + + function token_to_string(type, value) { + return type + (value === undefined ? "" : " «" + value + "»"); + } function unexpected(token) { - if (token == null) - token = S.token; - token_error(token, "Unexpected token: " + token.type + " (" + token.value + ")"); - }; + if (token == null) token = S.token; + token_error(token, "Unexpected token: " + token_to_string(token.type, token.value)); + } function expect_token(type, val) { - if (is(type, val)) { - return next(); - } - token_error(S.token, "Unexpected token " + S.token.type + " «" + S.token.value + "»" + ", expected " + type + " «" + val + "»"); - }; + if (is(type, val)) return next(); + token_error(S.token, "Unexpected token: " + token_to_string(S.token.type, S.token.value) + ", expected: " + token_to_string(type, val)); + } + + function expect(punc) { + return expect_token("punc", punc); + } - function expect(punc) { return expect_token("punc", punc); }; + function has_newline_before(token) { + return token.nlb || !all(token.comments_before, function(comment) { + return !comment.nlb; + }); + } function can_insert_semicolon() { - return !options.strict && ( - S.token.nlb || is("eof") || is("punc", "}") - ); - }; + return !options.strict + && (is("eof") || is("punc", "}") || has_newline_before(S.token)); + } - function semicolon() { + function semicolon(optional) { if (is("punc", ";")) next(); - else if (!can_insert_semicolon()) unexpected(); - }; + else if (!optional && !can_insert_semicolon()) expect(";"); + } - function parenthesised() { + function parenthesized() { expect("("); - var exp = expression(true); + var exp = expression(); expect(")"); return exp; - }; + } function embed_tokens(parser) { return function() { var start = S.token; - var expr = parser(); + var expr = parser.apply(null, arguments); var end = prev(); expr.start = start; expr.end = end; return expr; }; - }; + } function handle_regexp() { if (is("operator", "/") || is("operator", "/=")) { S.peeked = null; S.token = S.input(S.token.value.substr(1)); // force regexp } - }; + } - var statement = embed_tokens(function() { - var tmp; + var statement = embed_tokens(function(toplevel) { handle_regexp(); switch (S.token.type) { case "string": - var dir = S.in_directives, stat = simple_statement(); - // XXXv2: decide how to fix directives - if (dir && stat.body instanceof AST_String && !is("punc", ",")) - return new AST_Directive({ value: stat.body.value }); - return stat; + var dir = S.in_directives; + var body = expression(); + if (dir) { + if (body instanceof AST_String) { + var value = body.start.raw.slice(1, -1); + S.input.add_directive(value); + body.value = value; + } else { + S.in_directives = dir = false; + } + } + semicolon(); + return dir ? new AST_Directive(body) : new AST_SimpleStatement({ body: body }); case "num": + case "bigint": case "regexp": case "operator": case "atom": return simple_statement(); case "name": + switch (S.token.value) { + case "async": + if (is_token(peek(), "keyword", "function")) { + next(); + next(); + if (!is("operator", "*")) return function_(AST_AsyncDefun); + next(); + return function_(AST_AsyncGeneratorDefun); + } + break; + case "await": + if (S.in_async) return simple_statement(); + break; + case "export": + if (!toplevel && options.module !== "") unexpected(); + next(); + return export_(); + case "import": + var token = peek(); + if (token.type == "punc" && /^[(.]$/.test(token.value)) break; + if (!toplevel && options.module !== "") unexpected(); + next(); + return import_(); + case "let": + if (is_vardefs()) { + next(); + var node = let_(); + semicolon(); + return node; + } + break; + case "yield": + if (S.in_generator) return simple_statement(); + break; + } return is_token(peek(), "punc", ":") ? labeled_statement() : simple_statement(); @@ -761,8 +885,10 @@ function parse($TEXT, options) { }); case "[": case "(": + case "`": return simple_statement(); case ";": + S.in_directives = false; next(); return new AST_EmptyStatement(); default: @@ -770,86 +896,119 @@ function parse($TEXT, options) { } case "keyword": - switch (tmp = S.token.value, next(), tmp) { + switch (S.token.value) { case "break": + next(); return break_cont(AST_Break); + case "class": + next(); + return class_(AST_DefClass); + + case "const": + next(); + var node = const_(); + semicolon(); + return node; + case "continue": + next(); return break_cont(AST_Continue); case "debugger": + next(); semicolon(); return new AST_Debugger(); case "do": + next(); + var body = in_loop(statement); + expect_token("keyword", "while"); + var condition = parenthesized(); + semicolon(true); return new AST_Do({ - body : in_loop(statement), - condition : (expect_token("keyword", "while"), tmp = parenthesised(), semicolon(), tmp) + body : body, + condition : condition, }); case "while": + next(); return new AST_While({ - condition : parenthesised(), - body : in_loop(statement) + condition : parenthesized(), + body : in_loop(statement), }); case "for": + next(); return for_(); case "function": - return function_(AST_Defun); + next(); + if (!is("operator", "*")) return function_(AST_Defun); + next(); + return function_(AST_GeneratorDefun); case "if": + next(); return if_(); case "return": if (S.in_function == 0 && !options.bare_returns) croak("'return' outside of function"); - return new AST_Return({ - value: ( is("punc", ";") - ? (next(), null) - : can_insert_semicolon() - ? null - : (tmp = expression(true), semicolon(), tmp) ) - }); + next(); + var value = null; + if (is("punc", ";")) { + next(); + } else if (!can_insert_semicolon()) { + value = expression(); + semicolon(); + } + return new AST_Return({ value: value }); case "switch": + next(); return new AST_Switch({ - expression : parenthesised(), - body : in_loop(switch_body_) + expression : parenthesized(), + body : in_loop(switch_body_), }); case "throw": - if (S.token.nlb) + next(); + if (has_newline_before(S.token)) croak("Illegal newline after 'throw'"); - return new AST_Throw({ - value: (tmp = expression(true), semicolon(), tmp) - }); + var value = expression(); + semicolon(); + return new AST_Throw({ value: value }); case "try": + next(); return try_(); case "var": - return tmp = var_(), semicolon(), tmp; - - case "const": - return tmp = const_(), semicolon(), tmp; + next(); + var node = var_(); + semicolon(); + return node; case "with": + if (S.input.has_directive("use strict")) { + croak("Strict mode may not include a with statement"); + } + next(); return new AST_With({ - expression : parenthesised(), - body : statement() + expression : parenthesized(), + body : statement(), }); - - default: - unexpected(); } } + unexpected(); }); function labeled_statement() { var label = as_symbol(AST_Label); - if (find_if(function(l){ return l.name == label.name }, S.labels)) { + if (!all(S.labels, function(l) { + return l.name != label.name; + })) { // ECMA-262, 12.12: An ECMAScript program is considered // syntactically incorrect if it contains a // LabelledStatement that is enclosed by a @@ -863,21 +1022,21 @@ function parse($TEXT, options) { if (!(stat instanceof AST_IterationStatement)) { // check for `continue` that refers to this label. // those should be reported as syntax errors. - // https://github.com/mishoo/UglifyJS2/issues/287 - label.references.forEach(function(ref){ + // https://github.com/mishoo/UglifyJS/issues/287 + label.references.forEach(function(ref) { if (ref instanceof AST_Continue) { - ref = ref.label.start; - croak("Continue label `" + label.name + "` refers to non-IterationStatement.", - ref.line, ref.col, ref.pos); + token_error(ref.label.start, "Continue label `" + label.name + "` must refer to IterationStatement"); } }); } return new AST_LabeledStatement({ body: stat, label: label }); - }; + } - function simple_statement(tmp) { - return new AST_SimpleStatement({ body: (tmp = expression(true), semicolon(), tmp) }); - }; + function simple_statement() { + var body = expression(); + semicolon(); + return new AST_SimpleStatement({ body: body }); + } function break_cont(type) { var label = null, ldef; @@ -885,142 +1044,644 @@ function parse($TEXT, options) { label = as_symbol(AST_LabelRef, true); } if (label != null) { - ldef = find_if(function(l){ return l.name == label.name }, S.labels); - if (!ldef) - croak("Undefined label " + label.name); + ldef = find_if(function(l) { + return l.name == label.name; + }, S.labels); + if (!ldef) token_error(label.start, "Undefined label " + label.name); label.thedef = ldef; - } - else if (S.in_loop == 0) - croak(type.TYPE + " not inside a loop or switch"); + } else if (S.in_loop == 0) croak(type.TYPE + " not inside a loop or switch"); semicolon(); var stat = new type({ label: label }); if (ldef) ldef.references.push(stat); return stat; - }; + } - function for_() { - expect("("); - var init = null; - if (!is("punc", ";")) { - init = is("keyword", "var") - ? (next(), var_(true)) - : expression(true, true); - if (is("operator", "in")) { - if (init instanceof AST_Var && init.definitions.length > 1) - croak("Only one variable declaration allowed in for..in loop"); + function has_modifier(name, no_nlb) { + if (!is("name", name)) return; + var token = peek(); + if (!token) return; + if (is_token(token, "operator", "=")) return; + if (token.type == "punc" && /^[(;}]$/.test(token.value)) return; + if (no_nlb && has_newline_before(token)) return; + return next(); + } + + function class_(ctor) { + var was_async = S.in_async; + var was_gen = S.in_generator; + S.input.push_directives_stack(); + S.input.add_directive("use strict"); + var name; + if (ctor === AST_DefClass) { + name = as_symbol(AST_SymbolDefClass); + } else { + name = as_symbol(AST_SymbolClass, true); + } + var parent = null; + if (is("keyword", "extends")) { + next(); + handle_regexp(); + parent = expr_atom(true); + } + expect("{"); + var props = []; + while (!is("punc", "}")) { + if (is("punc", ";")) { next(); - return for_in(init); + continue; } - } - return regular_for(init); - }; - - function regular_for(init) { - expect(";"); - var test = is("punc", ";") ? null : expression(true); - expect(";"); - var step = is("punc", ")") ? null : expression(true); - expect(")"); - return new AST_For({ + var start = S.token; + var fixed = !!has_modifier("static"); + var async = has_modifier("async", true); + if (is("operator", "*")) { + next(); + var internal = is("name") && /^#/.test(S.token.value); + var key = as_property_key(); + var gen_start = S.token; + var gen = function_(async ? AST_AsyncGeneratorFunction : AST_GeneratorFunction); + gen.start = gen_start; + gen.end = prev(); + props.push(new AST_ClassMethod({ + start: start, + static: fixed, + private: internal, + key: key, + value: gen, + end: prev(), + })); + continue; + } + if (fixed && is("punc", "{")) { + props.push(new AST_ClassInit({ + start: start, + value: new AST_ClassInitBlock({ + start: start, + body: block_(), + end: prev(), + }), + end: prev(), + })); + continue; + } + var internal = is("name") && /^#/.test(S.token.value); + var key = as_property_key(); + if (is("punc", "(")) { + var func_start = S.token; + var func = function_(async ? AST_AsyncFunction : AST_Function); + func.start = func_start; + func.end = prev(); + props.push(new AST_ClassMethod({ + start: start, + static: fixed, + private: internal, + key: key, + value: func, + end: prev(), + })); + continue; + } + if (async) unexpected(async); + var value = null; + if (is("operator", "=")) { + next(); + S.in_async = false; + S.in_generator = false; + value = maybe_assign(); + S.in_generator = was_gen; + S.in_async = was_async; + } else if (!(is("punc", ";") || is("punc", "}"))) { + var type = null; + switch (key) { + case "get": + type = AST_ClassGetter; + break; + case "set": + type = AST_ClassSetter; + break; + } + if (type) { + props.push(new type({ + start: start, + static: fixed, + private: is("name") && /^#/.test(S.token.value), + key: as_property_key(), + value: create_accessor(), + end: prev(), + })); + continue; + } + } + semicolon(); + props.push(new AST_ClassField({ + start: start, + static: fixed, + private: internal, + key: key, + value: value, + end: prev(), + })); + } + next(); + S.input.pop_directives_stack(); + S.in_generator = was_gen; + S.in_async = was_async; + return new ctor({ + extends: parent, + name: name, + properties: props, + }); + } + + function for_() { + var await_token = is("name", "await") && next(); + expect("("); + var init = null; + if (await_token || !is("punc", ";")) { + init = is("keyword", "const") + ? (next(), const_(true)) + : is("name", "let") && is_vardefs() + ? (next(), let_(true)) + : is("keyword", "var") + ? (next(), var_(true)) + : expression(true); + var ctor; + if (await_token) { + expect_token("name", "of"); + ctor = AST_ForAwaitOf; + } else if (is("operator", "in")) { + next(); + ctor = AST_ForIn; + } else if (is("name", "of")) { + next(); + ctor = AST_ForOf; + } + if (ctor) { + if (init instanceof AST_Definitions) { + if (init.definitions.length > 1) { + token_error(init.start, "Only one variable declaration allowed in for..in/of loop"); + } + if (ctor !== AST_ForIn && init.definitions[0].value) { + token_error(init.definitions[0].value.start, "No initializers allowed in for..of loop"); + } + } else if (!(is_assignable(init) || (init = to_destructured(init)) instanceof AST_Destructured)) { + token_error(init.start, "Invalid left-hand side in for..in/of loop"); + } + return for_enum(ctor, init); + } + } + return regular_for(init); + } + + function regular_for(init) { + expect(";"); + var test = is("punc", ";") ? null : expression(); + expect(";"); + var step = is("punc", ")") ? null : expression(); + expect(")"); + return new AST_For({ init : init, condition : test, step : step, body : in_loop(statement) }); - }; + } - function for_in(init) { - var lhs = init instanceof AST_Var ? init.definitions[0].name : null; - var obj = expression(true); + function for_enum(ctor, init) { + handle_regexp(); + var obj = expression(); expect(")"); - return new AST_ForIn({ + return new ctor({ init : init, - name : lhs, object : obj, body : in_loop(statement) }); - }; + } + + function to_funarg(node) { + if (node instanceof AST_Array) { + var rest = null; + if (node.elements[node.elements.length - 1] instanceof AST_Spread) { + rest = to_funarg(node.elements.pop().expression); + } + return new AST_DestructuredArray({ + start: node.start, + elements: node.elements.map(to_funarg), + rest: rest, + end: node.end, + }); + } + if (node instanceof AST_Assign) return new AST_DefaultValue({ + start: node.start, + name: to_funarg(node.left), + value: node.right, + end: node.end, + }); + if (node instanceof AST_DefaultValue) { + node.name = to_funarg(node.name); + return node; + } + if (node instanceof AST_DestructuredArray) { + node.elements = node.elements.map(to_funarg); + if (node.rest) node.rest = to_funarg(node.rest); + return node; + } + if (node instanceof AST_DestructuredObject) { + node.properties.forEach(function(prop) { + prop.value = to_funarg(prop.value); + }); + if (node.rest) node.rest = to_funarg(node.rest); + return node; + } + if (node instanceof AST_Hole) return node; + if (node instanceof AST_Object) { + var rest = null; + if (node.properties[node.properties.length - 1] instanceof AST_Spread) { + rest = to_funarg(node.properties.pop().expression); + } + return new AST_DestructuredObject({ + start: node.start, + properties: node.properties.map(function(prop) { + if (!(prop instanceof AST_ObjectKeyVal)) token_error(prop.start, "Invalid destructuring assignment"); + return new AST_DestructuredKeyVal({ + start: prop.start, + key: prop.key, + value: to_funarg(prop.value), + end: prop.end, + }); + }), + rest: rest, + end: node.end, + }); + } + if (node instanceof AST_SymbolFunarg) return node; + if (node instanceof AST_SymbolRef) return new AST_SymbolFunarg(node); + if (node instanceof AST_Yield) return new AST_SymbolFunarg({ + start: node.start, + name: "yield", + end: node.end, + }); + token_error(node.start, "Invalid arrow parameter"); + } + + function arrow(exprs, start, async) { + var was_async = S.in_async; + var was_gen = S.in_generator; + S.in_async = async; + S.in_generator = false; + var was_funarg = S.in_funarg; + S.in_funarg = S.in_function; + var argnames = exprs.map(to_funarg); + var rest = exprs.rest || null; + if (rest) rest = to_funarg(rest); + S.in_funarg = was_funarg; + expect("=>"); + var body, value; + var loop = S.in_loop; + var labels = S.labels; + ++S.in_function; + S.input.push_directives_stack(); + S.in_loop = 0; + S.labels = []; + if (is("punc", "{")) { + S.in_directives = true; + body = block_(); + value = null; + } else { + body = []; + handle_regexp(); + value = maybe_assign(); + } + var is_strict = S.input.has_directive("use strict"); + S.input.pop_directives_stack(); + --S.in_function; + S.in_loop = loop; + S.labels = labels; + S.in_generator = was_gen; + S.in_async = was_async; + var node = new (async ? AST_AsyncArrow : AST_Arrow)({ + start: start, + argnames: argnames, + rest: rest, + body: body, + value: value, + end: prev(), + }); + if (is_strict) node.each_argname(strict_verify_symbol); + return node; + } var function_ = function(ctor) { - var in_statement = ctor === AST_Defun; - var name = is("name") ? as_symbol(in_statement ? AST_SymbolDefun : AST_SymbolLambda) : null; - if (in_statement && !name) - unexpected(); + var was_async = S.in_async; + var was_gen = S.in_generator; + var name; + if (/Defun$/.test(ctor.TYPE)) { + name = as_symbol(AST_SymbolDefun); + S.in_async = /^Async/.test(ctor.TYPE); + S.in_generator = /Generator/.test(ctor.TYPE); + } else { + S.in_async = /^Async/.test(ctor.TYPE); + S.in_generator = /Generator/.test(ctor.TYPE); + name = as_symbol(AST_SymbolLambda, true); + } + if (name && ctor !== AST_Accessor && !(name instanceof AST_SymbolDeclaration)) + unexpected(prev()); expect("("); - return new ctor({ + var was_funarg = S.in_funarg; + S.in_funarg = S.in_function; + var argnames = expr_list(")", !options.strict, false, function() { + return maybe_default(AST_SymbolFunarg); + }); + S.in_funarg = was_funarg; + var loop = S.in_loop; + var labels = S.labels; + ++S.in_function; + S.in_directives = true; + S.input.push_directives_stack(); + S.in_loop = 0; + S.labels = []; + var body = block_(); + var is_strict = S.input.has_directive("use strict"); + S.input.pop_directives_stack(); + --S.in_function; + S.in_loop = loop; + S.labels = labels; + S.in_generator = was_gen; + S.in_async = was_async; + var node = new ctor({ name: name, - argnames: (function(first, a){ - while (!is("punc", ")")) { - if (first) first = false; else expect(","); - a.push(as_symbol(AST_SymbolFunarg)); - } - next(); - return a; - })(true, []), - body: (function(loop, labels){ - ++S.in_function; - S.in_directives = true; - S.in_loop = 0; - S.labels = []; - var a = block_(); - --S.in_function; - S.in_loop = loop; - S.labels = labels; - return a; - })(S.in_loop, S.labels) + argnames: argnames, + rest: argnames.rest || null, + body: body, }); + if (is_strict) { + if (name) strict_verify_symbol(name); + node.each_argname(strict_verify_symbol); + } + return node; }; function if_() { - var cond = parenthesised(), body = statement(), belse = null; + var cond = parenthesized(), body = statement(), alt = null; if (is("keyword", "else")) { next(); - belse = statement(); + alt = statement(); } return new AST_If({ condition : cond, body : body, - alternative : belse + alternative : alt, }); - }; + } + + function is_alias() { + return is("name") || is("string") || is_identifier_string(S.token.value); + } + + function make_string(token) { + return new AST_String({ + start: token, + quote: token.quote, + value: token.value, + end: token, + }); + } + + function as_path() { + var path = S.token; + expect_token("string"); + semicolon(); + return make_string(path); + } + + function export_() { + if (is("operator", "*")) { + var key = S.token; + var alias = key; + next(); + if (is("name", "as")) { + next(); + if (!is_alias()) expect_token("name"); + alias = S.token; + next(); + } + expect_token("name", "from"); + return new AST_ExportForeign({ + aliases: [ make_string(alias) ], + keys: [ make_string(key) ], + path: as_path(), + }); + } + if (is("punc", "{")) { + next(); + var aliases = []; + var keys = []; + while (is_alias()) { + var key = S.token; + next(); + keys.push(key); + if (is("name", "as")) { + next(); + if (!is_alias()) expect_token("name"); + aliases.push(S.token); + next(); + } else { + aliases.push(key); + } + if (!is("punc", "}")) expect(","); + } + expect("}"); + if (is("name", "from")) { + next(); + return new AST_ExportForeign({ + aliases: aliases.map(make_string), + keys: keys.map(make_string), + path: as_path(), + }); + } + semicolon(); + return new AST_ExportReferences({ + properties: keys.map(function(token, index) { + if (!is_token(token, "name")) token_error(token, "Name expected"); + var sym = _make_symbol(AST_SymbolExport, token); + sym.alias = make_string(aliases[index]); + return sym; + }), + }); + } + if (is("keyword", "default")) { + next(); + var start = S.token; + var body = export_default_decl(); + if (body) { + body.start = start; + body.end = prev(); + } else { + handle_regexp(); + body = expression(); + semicolon(); + } + return new AST_ExportDefault({ body: body }); + } + return new AST_ExportDeclaration({ body: export_decl() }); + } + + function maybe_named(def, expr) { + if (expr.name) { + expr = new def(expr); + expr.name = new (def === AST_DefClass ? AST_SymbolDefClass : AST_SymbolDefun)(expr.name); + } + return expr; + } + + function export_default_decl() { + if (is("name", "async")) { + if (!is_token(peek(), "keyword", "function")) return; + next(); + next(); + if (!is("operator", "*")) return maybe_named(AST_AsyncDefun, function_(AST_AsyncFunction)); + next(); + return maybe_named(AST_AsyncGeneratorDefun, function_(AST_AsyncGeneratorFunction)); + } else if (is("keyword")) switch (S.token.value) { + case "class": + next(); + return maybe_named(AST_DefClass, class_(AST_ClassExpression)); + case "function": + next(); + if (!is("operator", "*")) return maybe_named(AST_Defun, function_(AST_Function)); + next(); + return maybe_named(AST_GeneratorDefun, function_(AST_GeneratorFunction)); + } + } + + var export_decl = embed_tokens(function() { + if (is("name")) switch (S.token.value) { + case "async": + next(); + expect_token("keyword", "function"); + if (!is("operator", "*")) return function_(AST_AsyncDefun); + next(); + return function_(AST_AsyncGeneratorDefun); + case "let": + next(); + var node = let_(); + semicolon(); + return node; + } else if (is("keyword")) switch (S.token.value) { + case "class": + next(); + return class_(AST_DefClass); + case "const": + next(); + var node = const_(); + semicolon(); + return node; + case "function": + next(); + if (!is("operator", "*")) return function_(AST_Defun); + next(); + return function_(AST_GeneratorDefun); + case "var": + next(); + var node = var_(); + semicolon(); + return node; + } + unexpected(); + }); + + function import_() { + var all = null; + var def = as_symbol(AST_SymbolImport, true); + var props = null; + var cont; + if (def) { + def.key = new AST_String({ + start: def.start, + value: "", + end: def.end, + }); + if (cont = is("punc", ",")) next(); + } else { + cont = !is("string"); + } + if (cont) { + if (is("operator", "*")) { + var key = S.token; + next(); + expect_token("name", "as"); + all = as_symbol(AST_SymbolImport); + all.key = make_string(key); + } else { + expect("{"); + props = []; + while (is_alias()) { + var alias; + if (is_token(peek(), "name", "as")) { + var key = S.token; + next(); + next(); + alias = as_symbol(AST_SymbolImport); + alias.key = make_string(key); + } else { + alias = as_symbol(AST_SymbolImport); + alias.key = new AST_String({ + start: alias.start, + value: alias.name, + end: alias.end, + }); + } + props.push(alias); + if (!is("punc", "}")) expect(","); + } + expect("}"); + } + } + if (all || def || props) expect_token("name", "from"); + return new AST_Import({ + all: all, + default: def, + path: as_path(), + properties: props, + }); + } function block_() { expect("{"); var a = []; while (!is("punc", "}")) { - if (is("eof")) unexpected(); + if (is("eof")) expect("}"); a.push(statement()); } next(); return a; - }; + } function switch_body_() { expect("{"); - var a = [], cur = null, branch = null, tmp; + var a = [], branch, cur, default_branch, tmp; while (!is("punc", "}")) { - if (is("eof")) unexpected(); + if (is("eof")) expect("}"); if (is("keyword", "case")) { if (branch) branch.end = prev(); cur = []; branch = new AST_Case({ start : (tmp = S.token, next(), tmp), - expression : expression(true), + expression : expression(), body : cur }); a.push(branch); expect(":"); - } - else if (is("keyword", "default")) { + } else if (is("keyword", "default")) { if (branch) branch.end = prev(); + if (default_branch) croak("More than one default clause in switch statement"); cur = []; branch = new AST_Default({ start : (tmp = S.token, next(), expect(":"), tmp), body : cur }); a.push(branch); - } - else { + default_branch = branch; + } else { if (!cur) unexpected(); cur.push(statement()); } @@ -1028,16 +1689,19 @@ function parse($TEXT, options) { if (branch) branch.end = prev(); next(); return a; - }; + } function try_() { var body = block_(), bcatch = null, bfinally = null; if (is("keyword", "catch")) { var start = S.token; next(); - expect("("); - var name = as_symbol(AST_SymbolCatch); - expect(")"); + var name = null; + if (is("punc", "(")) { + next(); + name = maybe_destructured(AST_SymbolCatch); + expect(")"); + } bcatch = new AST_Catch({ start : start, argname : name, @@ -1061,15 +1725,24 @@ function parse($TEXT, options) { bcatch : bcatch, bfinally : bfinally }); - }; + } - function vardefs(no_in, in_const) { + function vardefs(type, no_in) { var a = []; for (;;) { + var start = S.token; + var name = maybe_destructured(type); + var value = null; + if (is("operator", "=")) { + next(); + value = maybe_assign(no_in); + } else if (!no_in && (type === AST_SymbolConst || name instanceof AST_Destructured)) { + croak("Missing initializer in declaration"); + } a.push(new AST_VarDef({ - start : S.token, - name : as_symbol(in_const ? AST_SymbolConst : AST_SymbolVar), - value : is("operator", "=") ? (next(), expression(false, no_in)) : null, + start : start, + name : name, + value : value, end : prev() })); if (!is("punc", ",")) @@ -1077,89 +1750,144 @@ function parse($TEXT, options) { next(); } return a; + } + + function is_vardefs() { + var token = peek(); + return is_token(token, "name") || is_token(token, "punc", "[") || is_token(token, "punc", "{"); + } + + var const_ = function(no_in) { + return new AST_Const({ + start : prev(), + definitions : vardefs(AST_SymbolConst, no_in), + end : prev() + }); }; - var var_ = function(no_in) { - return new AST_Var({ + var let_ = function(no_in) { + return new AST_Let({ start : prev(), - definitions : vardefs(no_in, false), + definitions : vardefs(AST_SymbolLet, no_in), end : prev() }); }; - var const_ = function() { - return new AST_Const({ + var var_ = function(no_in) { + return new AST_Var({ start : prev(), - definitions : vardefs(false, true), + definitions : vardefs(AST_SymbolVar, no_in), end : prev() }); }; - var new_ = function() { + var new_ = function(allow_calls) { var start = S.token; expect_token("operator", "new"); - var newexp = expr_atom(false), args; - if (is("punc", "(")) { + var call; + if (is("punc", ".") && is_token(peek(), "name", "target")) { + next(); next(); - args = expr_list(")"); + call = new AST_NewTarget(); } else { - args = []; - } - return subscripts(new AST_New({ - start : start, - expression : newexp, - args : args, - end : prev() - }), true); + 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 }); + } + call.start = start; + call.end = prev(); + return subscripts(call, allow_calls); }; function as_atom_node() { - var tok = S.token, ret; + var ret, tok = S.token, value = tok.value; switch (tok.type) { - case "name": - case "keyword": - ret = _make_symbol(AST_SymbolRef); - break; case "num": - ret = new AST_Number({ start: tok, end: tok, value: tok.value }); + if (isFinite(value)) { + ret = new AST_Number({ value: value }); + } else { + ret = new AST_Infinity(); + if (value < 0) ret = new AST_UnaryPrefix({ operator: "-", expression: ret }); + } + break; + case "bigint": + ret = new AST_BigInt({ value: value }); break; case "string": - ret = new AST_String({ start: tok, end: tok, value: tok.value }); + ret = new AST_String({ value: value, quote: tok.quote }); break; case "regexp": - ret = new AST_RegExp({ start: tok, end: tok, value: tok.value }); + ret = new AST_RegExp({ value: value }); break; case "atom": - switch (tok.value) { + switch (value) { case "false": - ret = new AST_False({ start: tok, end: tok }); + ret = new AST_False(); break; case "true": - ret = new AST_True({ start: tok, end: tok }); + ret = new AST_True(); break; case "null": - ret = new AST_Null({ start: tok, end: tok }); + ret = new AST_Null(); break; + default: + unexpected(); } break; + default: + unexpected(); } next(); + ret.start = ret.end = tok; return ret; - }; + } var expr_atom = function(allow_calls) { if (is("operator", "new")) { - return new_(); + return new_(allow_calls); } var start = S.token; if (is("punc")) { switch (start.value) { + case "`": + return subscripts(template(null), allow_calls); case "(": next(); - var ex = expression(true); + if (is("punc", ")")) { + next(); + return arrow([], start); + } + var ex = expression(false, true); + var len = start.comments_before.length; + [].unshift.apply(ex.start.comments_before, start.comments_before); + start.comments_before.length = 0; + start.comments_before = ex.start.comments_before; + start.comments_before_length = len; + if (len == 0 && start.comments_before.length > 0) { + var comment = start.comments_before[0]; + if (!comment.nlb) { + comment.nlb = start.nlb; + start.nlb = false; + } + } + start.comments_after = ex.start.comments_after; ex.start = start; - ex.end = S.token; expect(")"); + var end = prev(); + end.comments_before = ex.end.comments_before; + end.comments_after.forEach(function(comment) { + ex.end.comments_after.push(comment); + if (comment.nlb) S.token.nlb = true; + }); + end.comments_after.length = 0; + end.comments_after = ex.end.comments_after; + ex.end = end; + if (is("punc", "=>")) return arrow(ex instanceof AST_Sequence ? ex.expressions : [ ex ], start); return subscripts(ex, allow_calls); case "[": return subscripts(array_(), allow_calls); @@ -1168,33 +1896,93 @@ function parse($TEXT, options) { } unexpected(); } - if (is("keyword", "function")) { + if (is("keyword")) switch (start.value) { + case "class": next(); - var func = function_(AST_Function); + var clazz = class_(AST_ClassExpression); + clazz.start = start; + clazz.end = prev(); + return subscripts(clazz, allow_calls); + case "function": + next(); + var func; + if (is("operator", "*")) { + next(); + func = function_(AST_GeneratorFunction); + } else { + func = function_(AST_Function); + } func.start = start; func.end = prev(); return subscripts(func, allow_calls); } + if (is("name")) { + var sym = _make_symbol(AST_SymbolRef, start); + next(); + if (sym.name == "async") { + if (is("keyword", "function")) { + next(); + var func; + if (is("operator", "*")) { + next(); + func = function_(AST_AsyncGeneratorFunction); + } else { + func = function_(AST_AsyncFunction); + } + func.start = start; + func.end = prev(); + return subscripts(func, allow_calls); + } + if (is("name") && is_token(peek(), "punc", "=>")) { + start = S.token; + sym = _make_symbol(AST_SymbolRef, start); + next(); + return arrow([ sym ], start, true); + } + if (is("punc", "(")) { + var call = subscripts(sym, allow_calls); + if (!is("punc", "=>")) return call; + var args = call.args; + if (args[args.length - 1] instanceof AST_Spread) { + args.rest = args.pop().expression; + } + return arrow(args, start, true); + } + } + return is("punc", "=>") ? arrow([ sym ], start) : subscripts(sym, allow_calls); + } if (ATOMIC_START_TOKEN[S.token.type]) { return subscripts(as_atom_node(), allow_calls); } unexpected(); }; - function expr_list(closing, allow_trailing_comma, allow_empty) { + function expr_list(closing, allow_trailing_comma, allow_empty, parser) { + if (!parser) parser = maybe_assign; var first = true, a = []; while (!is("punc", closing)) { if (first) first = false; else expect(","); if (allow_trailing_comma && is("punc", closing)) break; - if (is("punc", ",") && allow_empty) { + if (allow_empty && is("punc", ",")) { a.push(new AST_Hole({ start: S.token, end: S.token })); + } else if (!is("operator", "...")) { + a.push(parser()); + } else if (parser === maybe_assign) { + a.push(new AST_Spread({ + start: S.token, + expression: (next(), parser()), + end: prev(), + })); } else { - a.push(expression(false)); + next(); + a.rest = parser(); + if (a.rest instanceof AST_DefaultValue) token_error(a.rest.start, "Invalid rest parameter"); + break; } } - next(); + expect(closing); return a; - }; + } var array_ = embed_tokens(function() { expect("["); @@ -1203,192 +1991,458 @@ function parse($TEXT, options) { }); }); + var create_accessor = embed_tokens(function() { + return function_(AST_Accessor); + }); + var object_ = embed_tokens(function() { expect("{"); var first = true, a = []; while (!is("punc", "}")) { if (first) first = false; else expect(","); - if (!options.strict && is("punc", "}")) - // allow trailing comma - break; + // allow trailing comma + if (!options.strict && is("punc", "}")) break; var start = S.token; - var type = start.type; - var name = as_property_name(); - if (type == "name" && !is("punc", ":")) { - if (name == "get") { - a.push(new AST_ObjectGetter({ - start : start, - key : as_atom_node(), - value : function_(AST_Accessor), - end : prev() - })); - continue; - } - if (name == "set") { - a.push(new AST_ObjectSetter({ - start : start, - key : as_atom_node(), - value : function_(AST_Accessor), - end : prev() - })); - continue; - } + if (is("operator", "*")) { + next(); + var key = as_property_key(); + var gen_start = S.token; + var gen = function_(AST_GeneratorFunction); + gen.start = gen_start; + gen.end = prev(); + a.push(new AST_ObjectMethod({ + start: start, + key: key, + value: gen, + end: prev(), + })); + continue; } - expect(":"); - a.push(new AST_ObjectKeyVal({ - start : start, - key : name, - value : expression(false), - end : prev() - })); + if (is("operator", "...")) { + next(); + a.push(new AST_Spread({ + start: start, + expression: maybe_assign(), + end: prev(), + })); + continue; + } + if (is_token(peek(), "operator", "=")) { + var name = as_symbol(AST_SymbolRef); + next(); + a.push(new AST_ObjectKeyVal({ + start: start, + key: start.value, + value: new AST_Assign({ + start: start, + left: name, + operator: "=", + right: maybe_assign(), + end: prev(), + }), + end: prev(), + })); + continue; + } + if (is_token(peek(), "punc", ",") || is_token(peek(), "punc", "}")) { + a.push(new AST_ObjectKeyVal({ + start: start, + key: start.value, + value: as_symbol(AST_SymbolRef), + end: prev(), + })); + continue; + } + var key = as_property_key(); + if (is("punc", "(")) { + var func_start = S.token; + var func = function_(AST_Function); + func.start = func_start; + func.end = prev(); + a.push(new AST_ObjectMethod({ + start: start, + key: key, + value: func, + end: prev(), + })); + continue; + } + if (is("punc", ":")) { + next(); + a.push(new AST_ObjectKeyVal({ + start: start, + key: key, + value: maybe_assign(), + end: prev(), + })); + continue; + } + if (start.type == "name") switch (key) { + case "async": + var is_gen = is("operator", "*") && next(); + key = as_property_key(); + var func_start = S.token; + var func = function_(is_gen ? AST_AsyncGeneratorFunction : AST_AsyncFunction); + func.start = func_start; + func.end = prev(); + a.push(new AST_ObjectMethod({ + start: start, + key: key, + value: func, + end: prev(), + })); + continue; + case "get": + a.push(new AST_ObjectGetter({ + start: start, + key: as_property_key(), + value: create_accessor(), + end: prev(), + })); + continue; + case "set": + a.push(new AST_ObjectSetter({ + start: start, + key: as_property_key(), + value: create_accessor(), + end: prev(), + })); + continue; + } + unexpected(); } next(); return new AST_Object({ properties: a }); }); - function as_property_name() { + function as_property_key() { var tmp = S.token; - next(); switch (tmp.type) { + case "operator": + if (!KEYWORDS[tmp.value]) unexpected(); case "num": case "string": case "name": - case "operator": case "keyword": case "atom": - return tmp.value; + next(); + return "" + tmp.value; + case "punc": + expect("["); + var key = maybe_assign(); + expect("]"); + return key; default: unexpected(); } - }; + } function as_name() { - var tmp = S.token; - next(); - switch (tmp.type) { - case "name": - case "operator": - case "keyword": - case "atom": - return tmp.value; - default: - unexpected(); - } - }; - - function _make_symbol(type) { var name = S.token.value; - return new (name == "this" ? AST_This : type)({ - name : String(name), - start : S.token, - end : S.token + expect_token("name"); + return name; + } + + function _make_symbol(type, token) { + var name = token.value; + switch (name) { + case "await": + if (S.in_async) unexpected(token); + break; + case "super": + type = AST_Super; + break; + case "this": + type = AST_This; + break; + case "yield": + if (S.in_generator) unexpected(token); + break; + } + return new type({ + name: "" + name, + start: token, + end: token, }); - }; + } + + function strict_verify_symbol(sym) { + if (sym.name == "arguments" || sym.name == "eval" || sym.name == "let") + token_error(sym.start, "Unexpected " + sym.name + " in strict mode"); + } - function as_symbol(type, noerror) { + function as_symbol(type, no_error) { if (!is("name")) { - if (!noerror) croak("Name expected"); + if (!no_error) croak("Name expected"); return null; } - var sym = _make_symbol(type); + var sym = _make_symbol(type, S.token); + if (S.input.has_directive("use strict") && sym instanceof AST_SymbolDeclaration) { + strict_verify_symbol(sym); + } next(); return sym; - }; + } - var subscripts = function(expr, allow_calls) { - var start = expr.start; - if (is("punc", ".")) { + function maybe_destructured(type) { + var start = S.token; + if (is("punc", "[")) { next(); - return subscripts(new AST_Dot({ - start : start, - expression : expr, - property : as_name(), - end : prev() - }), allow_calls); + var elements = expr_list("]", !options.strict, true, function() { + return maybe_default(type); + }); + return new AST_DestructuredArray({ + start: start, + elements: elements, + rest: elements.rest || null, + end: prev(), + }); } - if (is("punc", "[")) { + if (is("punc", "{")) { next(); - var prop = expression(true); - expect("]"); - return subscripts(new AST_Sub({ - start : start, - expression : expr, - property : prop, - end : prev() - }), allow_calls); - } - if (allow_calls && is("punc", "(")) { + var first = true, a = [], rest = null; + while (!is("punc", "}")) { + if (first) first = false; else expect(","); + // allow trailing comma + if (!options.strict && is("punc", "}")) break; + var key_start = S.token; + if (is("punc", "[") || is_token(peek(), "punc", ":")) { + var key = as_property_key(); + expect(":"); + a.push(new AST_DestructuredKeyVal({ + start: key_start, + key: key, + value: maybe_default(type), + end: prev(), + })); + continue; + } + if (is("operator", "...")) { + next(); + rest = maybe_destructured(type); + break; + } + var name = as_symbol(type); + if (is("operator", "=")) { + next(); + name = new AST_DefaultValue({ + start: name.start, + name: name, + value: maybe_assign(), + end: prev(), + }); + } + a.push(new AST_DestructuredKeyVal({ + start: key_start, + key: key_start.value, + value: name, + end: prev(), + })); + } + expect("}"); + return new AST_DestructuredObject({ + start: start, + properties: a, + rest: rest, + end: prev(), + }); + } + return as_symbol(type); + } + + function maybe_default(type) { + var start = S.token; + var name = maybe_destructured(type); + if (!is("operator", "=")) return name; + next(); + return new AST_DefaultValue({ + start: start, + name: name, + value: maybe_assign(), + end: prev(), + }); + } + + function template(tag) { + var start = tag ? tag.start : S.token; + var read = S.input.context().read_template; + var strings = []; + var expressions = []; + while (read(strings)) { next(); - return subscripts(new AST_Call({ - start : start, - expression : expr, - args : expr_list(")"), - end : prev() - }), true); + expressions.push(expression()); + if (!is("punc", "}")) unexpected(); + } + next(); + return new AST_Template({ + start: start, + expressions: expressions, + strings: strings, + tag: tag, + end: prev(), + }); + } + + function subscripts(expr, allow_calls) { + var start = expr.start; + var optional = null; + while (true) { + if (is("operator", "?") && is_token(peek(), "punc", ".")) { + next(); + next(); + optional = expr; + } + if (is("punc", "[")) { + next(); + var prop = expression(); + expect("]"); + expr = new AST_Sub({ + start: start, + optional: optional === expr, + expression: expr, + property: prop, + end: prev(), + }); + } else if (allow_calls && is("punc", "(")) { + next(); + expr = new AST_Call({ + start: start, + optional: optional === expr, + expression: expr, + args: expr_list(")", !options.strict), + end: prev(), + }); + } else if (optional === expr || is("punc", ".")) { + if (optional !== expr) next(); + expr = new AST_Dot({ + start: start, + optional: optional === expr, + expression: expr, + property: as_name(), + end: prev(), + }); + } else if (is("punc", "`")) { + if (optional) croak("Invalid template on optional chain"); + expr = template(expr); + } else { + break; + } + } + if (optional) expr.terminal = true; + if (expr instanceof AST_Call && !expr.pure) { + var start = expr.start; + var comments = start.comments_before; + var i = HOP(start, "comments_before_length") ? start.comments_before_length : comments.length; + while (--i >= 0) { + if (/[@#]__PURE__/.test(comments[i].value)) { + expr.pure = true; + break; + } + } } return expr; - }; + } - var maybe_unary = function(allow_calls) { + function maybe_unary(no_in) { var start = S.token; - if (is("operator") && UNARY_PREFIX(start.value)) { + if (S.in_async && is("name", "await")) { + if (S.in_funarg === S.in_function) croak("Invalid use of await in function argument"); + S.input.context().regex_allowed = true; + next(); + return new AST_Await({ + start: start, + expression: maybe_unary(no_in), + end: prev(), + }); + } + if (S.in_generator && is("name", "yield")) { + if (S.in_funarg === S.in_function) croak("Invalid use of yield in function argument"); + S.input.context().regex_allowed = true; + next(); + var exp = null; + var nested = false; + if (is("operator", "*")) { + next(); + exp = maybe_assign(no_in); + nested = true; + } else if (is("punc") ? !PUNC_AFTER_EXPRESSION[S.token.value] : !can_insert_semicolon()) { + exp = maybe_assign(no_in); + } + return new AST_Yield({ + start: start, + expression: exp, + nested: nested, + end: prev(), + }); + } + if (is("operator") && UNARY_PREFIX[start.value]) { next(); handle_regexp(); - var ex = make_unary(AST_UnaryPrefix, start.value, maybe_unary(allow_calls)); + var ex = make_unary(AST_UnaryPrefix, start, maybe_unary(no_in)); ex.start = start; ex.end = prev(); return ex; } - var val = expr_atom(allow_calls); - while (is("operator") && UNARY_POSTFIX(S.token.value) && !S.token.nlb) { - val = make_unary(AST_UnaryPostfix, S.token.value, val); + var val = expr_atom(true); + while (is("operator") && UNARY_POSTFIX[S.token.value] && !has_newline_before(S.token)) { + val = make_unary(AST_UnaryPostfix, S.token, val); val.start = start; val.end = S.token; next(); } return val; - }; + } - function make_unary(ctor, op, expr) { - if ((op == "++" || op == "--") && !is_assignable(expr)) - croak("Invalid use of " + op + " operator"); + function make_unary(ctor, token, expr) { + var op = token.value; + switch (op) { + case "++": + case "--": + if (!is_assignable(expr)) + token_error(token, "Invalid use of " + op + " operator"); + break; + case "delete": + if (expr instanceof AST_SymbolRef && S.input.has_directive("use strict")) + token_error(expr.start, "Calling delete on expression not allowed in strict mode"); + break; + } return new ctor({ operator: op, expression: expr }); - }; + } - var expr_op = function(left, min_prec, no_in) { + var expr_op = function(left, min_precision, no_in) { var op = is("operator") ? S.token.value : null; if (op == "in" && no_in) op = null; - var prec = op != null ? PRECEDENCE[op] : null; - if (prec != null && prec > min_prec) { + var precision = op != null ? PRECEDENCE[op] : null; + if (precision != null && precision > min_precision) { next(); - var right = expr_op(maybe_unary(true), prec, no_in); + var right = expr_op(maybe_unary(no_in), op == "**" ? precision - 1 : precision, no_in); return expr_op(new AST_Binary({ start : left.start, left : left, operator : op, right : right, - end : right.end - }), min_prec, no_in); + end : right.end, + }), min_precision, no_in); } return left; }; function expr_ops(no_in) { - return expr_op(maybe_unary(true), 0, no_in); - }; + return expr_op(maybe_unary(no_in), 0, no_in); + } var maybe_conditional = function(no_in) { var start = S.token; var expr = expr_ops(no_in); if (is("operator", "?")) { next(); - var yes = expression(false); + var yes = maybe_assign(); expect(":"); return new AST_Conditional({ start : start, condition : expr, consequent : yes, - alternative : expression(false, no_in), + alternative : maybe_assign(no_in), end : prev() }); } @@ -1396,16 +2450,72 @@ function parse($TEXT, options) { }; function is_assignable(expr) { - if (!options.strict) return true; - if (expr instanceof AST_This) return false; - return (expr instanceof AST_PropAccess || expr instanceof AST_Symbol); - }; + return expr instanceof AST_PropAccess && !expr.optional || expr instanceof AST_SymbolRef; + } - var maybe_assign = function(no_in) { + function to_destructured(node) { + if (node instanceof AST_Array) { + var rest = null; + if (node.elements[node.elements.length - 1] instanceof AST_Spread) { + rest = to_destructured(node.elements.pop().expression); + if (!(rest instanceof AST_Destructured || is_assignable(rest))) return node; + } + var elements = node.elements.map(to_destructured); + return all(elements, function(node) { + return node instanceof AST_DefaultValue + || node instanceof AST_Destructured + || node instanceof AST_Hole + || is_assignable(node); + }) ? new AST_DestructuredArray({ + start: node.start, + elements: elements, + rest: rest, + end: node.end, + }) : node; + } + if (node instanceof AST_Assign) { + var name = to_destructured(node.left); + return name instanceof AST_Destructured || is_assignable(name) ? new AST_DefaultValue({ + start: node.start, + name: name, + value: node.right, + end: node.end, + }) : node; + } + if (!(node instanceof AST_Object)) return node; + var rest = null; + if (node.properties[node.properties.length - 1] instanceof AST_Spread) { + rest = to_destructured(node.properties.pop().expression); + if (!(rest instanceof AST_Destructured || is_assignable(rest))) return node; + } + var props = []; + for (var i = 0; i < node.properties.length; i++) { + var prop = node.properties[i]; + if (!(prop instanceof AST_ObjectKeyVal)) return node; + var value = to_destructured(prop.value); + if (!(value instanceof AST_DefaultValue || value instanceof AST_Destructured || is_assignable(value))) { + return node; + } + props.push(new AST_DestructuredKeyVal({ + start: prop.start, + key: prop.key, + value: value, + end: prop.end, + })); + } + return new AST_DestructuredObject({ + start: node.start, + properties: props, + rest: rest, + end: node.end, + }); + } + + function maybe_assign(no_in) { var start = S.token; var left = maybe_conditional(no_in), val = S.token.value; - if (is("operator") && ASSIGNMENT(val)) { - if (is_assignable(left)) { + if (is("operator") && ASSIGNMENT[val]) { + if (is_assignable(left) || val == "=" && (left = to_destructured(left)) instanceof AST_Destructured) { next(); return new AST_Assign({ start : start, @@ -1418,40 +2528,55 @@ function parse($TEXT, options) { croak("Invalid assignment"); } return left; - }; + } - var expression = function(commas, no_in) { + function expression(no_in, maybe_arrow) { var start = S.token; - var expr = maybe_assign(no_in); - if (commas && is("punc", ",")) { + var exprs = []; + while (true) { + if (maybe_arrow && is("operator", "...")) { + next(); + exprs.rest = maybe_destructured(AST_SymbolFunarg); + break; + } + exprs.push(maybe_assign(no_in)); + if (!is("punc", ",")) break; next(); - return new AST_Seq({ - start : start, - car : expr, - cdr : expression(true, no_in), - end : peek() - }); + if (maybe_arrow && is("punc", ")") && is_token(peek(), "punc", "=>")) break; } - return expr; - }; + return exprs.length == 1 && !exprs.rest ? exprs[0] : new AST_Sequence({ + start: start, + expressions: exprs, + end: prev(), + }); + } function in_loop(cont) { ++S.in_loop; var ret = cont(); --S.in_loop; return ret; - }; + } if (options.expression) { - return expression(true); + handle_regexp(); + var exp = expression(); + expect_token("eof"); + return exp; } - return (function(){ + return function() { var start = S.token; var body = []; + if (options.module) { + S.in_async = true; + S.input.add_directive("use strict"); + } + S.input.push_directives_stack(); while (!is("eof")) - body.push(statement()); - var end = prev(); + body.push(statement(true)); + S.input.pop_directives_stack(); + var end = prev() || start; var toplevel = options.toplevel; if (toplevel) { toplevel.body = toplevel.body.concat(body); @@ -1460,6 +2585,5 @@ function parse($TEXT, options) { toplevel = new AST_Toplevel({ start: start, body: body, end: end }); } return toplevel; - })(); - -}; + }(); +} diff --git a/lib/propmangle.js b/lib/propmangle.js new file mode 100644 index 00000000000..3e71b68c624 --- /dev/null +++ b/lib/propmangle.js @@ -0,0 +1,328 @@ +/*********************************************************************** + + A JavaScript tokenizer / parser / beautifier / compressor. + https://github.com/mishoo/UglifyJS + + -------------------------------- (C) --------------------------------- + + Author: Mihai Bazon + + http://mihai.bazon.net/blog + + Distributed under the BSD license: + + Copyright 2012 (c) Mihai Bazon + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + * Redistributions of source code must retain the above + copyright notice, this list of conditions and the following + disclaimer. + + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following + disclaimer in the documentation and/or other materials + provided with the distribution. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER “AS IS” AND ANY + EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE + LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR + TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF + THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + SUCH DAMAGE. + + ***********************************************************************/ + +"use strict"; + +function get_builtins() { + var names = new Dictionary(); + // constants + [ + "NaN", + "null", + "true", + "false", + "Infinity", + "-Infinity", + "undefined", + ].forEach(add); + // global functions + [ + "encodeURI", + "encodeURIComponent", + "escape", + "eval", + "decodeURI", + "decodeURIComponent", + "isFinite", + "isNaN", + "parseFloat", + "parseInt", + "unescape", + ].forEach(add); + // global constructors & objects + var global = Function("return this")(); + [ + "Array", + "ArrayBuffer", + "Atomics", + "BigInt", + "Boolean", + "console", + "DataView", + "Date", + "Error", + "Function", + "Int8Array", + "Intl", + "JSON", + "Map", + "Math", + "Number", + "Object", + "Promise", + "Proxy", + "Reflect", + "RegExp", + "Set", + "String", + "Symbol", + "WebAssembly", + ].forEach(function(name) { + add(name); + var ctor = global[name]; + if (!ctor) return; + Object.getOwnPropertyNames(ctor).map(add); + if (typeof ctor != "function") return; + if (ctor.__proto__) Object.getOwnPropertyNames(ctor.__proto__).map(add); + if (ctor.prototype) Object.getOwnPropertyNames(ctor.prototype).map(add); + try { + Object.getOwnPropertyNames(new ctor()).map(add); + } catch (e) { + try { + Object.getOwnPropertyNames(ctor()).map(add); + } catch (e) {} + } + }); + return (get_builtins = function() { + return names.clone(); + })(); + + function add(name) { + names.set(name, true); + } +} + +function reserve_quoted_keys(ast, reserved) { + ast.walk(new TreeWalker(function(node) { + if (node instanceof AST_ClassProperty + || node instanceof AST_DestructuredKeyVal + || node instanceof AST_ObjectProperty) { + if (node.key instanceof AST_Node) { + addStrings(node.key, add); + } else if (node.start && node.start.quote) { + add(node.key); + } + } else if (node instanceof AST_Dot) { + if (node.quoted) add(node.property); + } else if (node instanceof AST_Sub) { + addStrings(node.property, add); + } + })); + + function add(name) { + push_uniq(reserved, name); + } +} + +function addStrings(node, add) { + if (node instanceof AST_Conditional) { + addStrings(node.consequent, add); + addStrings(node.alternative, add); + } else if (node instanceof AST_Sequence) { + addStrings(node.tail_node(), add); + } else if (node instanceof AST_String) { + add(node.value); + } +} + +function mangle_properties(ast, options) { + options = defaults(options, { + builtins: false, + cache: null, + debug: false, + domprops: false, + keep_quoted: false, + regex: null, + reserved: null, + }, true); + + var reserved = options.builtins ? new Dictionary() : get_builtins(); + if (!options.domprops && typeof domprops !== "undefined") domprops.forEach(function(name) { + reserved.set(name, true); + }); + if (Array.isArray(options.reserved)) options.reserved.forEach(function(name) { + reserved.set(name, true); + }); + + var cname = -1; + var cache; + if (options.cache) { + cache = options.cache.props; + cache.each(function(name) { + reserved.set(name, true); + }); + } else { + cache = new Dictionary(); + } + + var regex = options.regex; + + // note debug is either false (disabled), or a string of the debug suffix to use (enabled). + // note debug may be enabled as an empty string, which is falsy. Also treat passing 'true' + // the same as passing an empty string. + var debug = options.debug !== false; + var debug_suffix; + if (debug) debug_suffix = options.debug === true ? "" : options.debug; + + var names_to_mangle = new Dictionary(); + var unmangleable = reserved.clone(); + + // step 1: find candidates to mangle + ast.walk(new TreeWalker(function(node) { + if (node.TYPE == "Call") { + var exp = node.expression; + if (exp instanceof AST_Dot) switch (exp.property) { + case "defineProperty": + case "getOwnPropertyDescriptor": + if (node.args.length < 2) break; + exp = exp.expression; + if (!(exp instanceof AST_SymbolRef)) break; + if (exp.name != "Object") break; + if (!exp.definition().undeclared) break; + addStrings(node.args[1], add); + break; + case "hasOwnProperty": + if (node.args.length < 1) break; + addStrings(node.args[0], add); + break; + } + } else if (node instanceof AST_ClassProperty + || node instanceof AST_DestructuredKeyVal + || node instanceof AST_ObjectProperty) { + if (node.key instanceof AST_Node) { + addStrings(node.key, add); + } else { + add(node.key); + } + } else if (node instanceof AST_Dot) { + if (is_lhs(node, this.parent())) add(node.property); + } else if (node instanceof AST_Sub) { + if (is_lhs(node, this.parent())) addStrings(node.property, add); + } + })); + + // step 2: renaming properties + ast.walk(new TreeWalker(function(node) { + if (node instanceof AST_Binary) { + if (node.operator == "in") mangleStrings(node.left); + } else if (node.TYPE == "Call") { + var exp = node.expression; + if (exp instanceof AST_Dot) switch (exp.property) { + case "defineProperty": + case "getOwnPropertyDescriptor": + if (node.args.length < 2) break; + exp = exp.expression; + if (!(exp instanceof AST_SymbolRef)) break; + if (exp.name != "Object") break; + if (!exp.definition().undeclared) break; + mangleStrings(node.args[1]); + break; + case "hasOwnProperty": + if (node.args.length < 1) break; + mangleStrings(node.args[0]); + break; + } + } else if (node instanceof AST_ClassProperty + || node instanceof AST_DestructuredKeyVal + || node instanceof AST_ObjectProperty) { + if (node.key instanceof AST_Node) { + mangleStrings(node.key); + } else { + node.key = mangle(node.key); + } + } else if (node instanceof AST_Dot) { + node.property = mangle(node.property); + } else if (node instanceof AST_Sub) { + if (!options.keep_quoted) mangleStrings(node.property); + } + })); + + // only function declarations after this line + + function can_mangle(name) { + if (unmangleable.has(name)) return false; + if (/^-?[0-9]+(\.[0-9]+)?(e[+-][0-9]+)?$/.test(name)) return false; + return true; + } + + function should_mangle(name) { + if (reserved.has(name)) { + AST_Node.info("Preserving reserved property {this}", name); + return false; + } + if (regex && !regex.test(name)) { + AST_Node.info("Preserving excluded property {this}", name); + return false; + } + return cache.has(name) || names_to_mangle.has(name); + } + + function add(name) { + if (can_mangle(name)) names_to_mangle.set(name, true); + if (!should_mangle(name)) unmangleable.set(name, true); + } + + function mangle(name) { + if (!should_mangle(name)) return name; + var mangled = cache.get(name); + if (!mangled) { + if (debug) { + // debug mode: use a prefix and suffix to preserve readability, e.g. o.foo ---> o._$foo$NNN_. + var debug_mangled = "_$" + name + "$" + debug_suffix + "_"; + if (can_mangle(debug_mangled)) mangled = debug_mangled; + } + // either debug mode is off, or it is on and we could not use the mangled name + if (!mangled) do { + mangled = base54(++cname); + } while (!can_mangle(mangled)); + if (/^#/.test(name)) mangled = "#" + mangled; + cache.set(name, mangled); + } + AST_Node.info("Mapping property {name} to {mangled}", { + mangled: mangled, + name: name, + }); + return mangled; + } + + function mangleStrings(node) { + if (node instanceof AST_Sequence) { + mangleStrings(node.tail_node()); + } else if (node instanceof AST_String) { + node.value = mangle(node.value); + } else if (node instanceof AST_Conditional) { + mangleStrings(node.consequent); + mangleStrings(node.alternative); + } + } +} diff --git a/lib/scope.js b/lib/scope.js index 73442a301ba..359c4f97da3 100644 --- a/lib/scope.js +++ b/lib/scope.js @@ -1,7 +1,7 @@ /*********************************************************************** A JavaScript tokenizer / parser / beautifier / compressor. - https://github.com/mishoo/UglifyJS2 + https://github.com/mishoo/UglifyJS -------------------------------- (C) --------------------------------- @@ -43,534 +43,834 @@ "use strict"; -function SymbolDef(scope, index, orig) { +function SymbolDef(id, scope, orig, init) { + this._bits = 0; + this.defun = undefined; + this.eliminated = 0; + this.id = id; + this.init = init; + this.mangled_name = null; this.name = orig.name; this.orig = [ orig ]; - this.scope = scope; this.references = []; - this.global = false; - this.mangled_name = null; - this.undeclared = false; - this.constant = false; - this.index = index; -}; + this.replaced = 0; + this.safe_ids = undefined; + this.scope = scope; +} SymbolDef.prototype = { - unmangleable: function(options) { - if (!options) options = {}; - - return (this.global && !options.toplevel) - || this.undeclared - || (!options.eval && (this.scope.uses_eval || this.scope.uses_with)) - || (options.keep_fnames - && (this.orig[0] instanceof AST_SymbolLambda - || this.orig[0] instanceof AST_SymbolDefun)); + forEach: function(fn) { + this.orig.forEach(fn); + this.references.forEach(fn); }, mangle: function(options) { - if (!this.mangled_name && !this.unmangleable(options)) { - var s = this.scope; - if (!options.screw_ie8 && this.orig[0] instanceof AST_SymbolLambda) - s = s.parent_scope; - this.mangled_name = s.next_mangled(options, this); + if (this.mangled_name) return; + var cache = this.global && options.cache && options.cache.props; + if (cache && cache.has(this.name)) { + this.mangled_name = cache.get(this.name); + } else if (!this.unmangleable(options)) { + var def = this.redefined(); + if (def) { + this.mangled_name = def.mangled_name || def.name; + } else { + this.mangled_name = next_mangled_name(this, options); + } + if (cache) cache.set(this.name, this.mangled_name); } - } + }, + redefined: function() { + var self = this; + var scope = self.defun; + if (!scope) return; + var name = self.name; + var def = scope.variables.get(name) + || scope instanceof AST_Toplevel && scope.globals.get(name) + || self.orig[0] instanceof AST_SymbolConst && find_if(function(def) { + return def.name == name; + }, scope.enclosed); + if (def && def !== self) return def.redefined() || def; + }, + unmangleable: function(options) { + if (this.exported) return true; + if (this.undeclared) return true; + if (!options.eval && this.scope.pinned()) return true; + if (options.keep_fargs && is_funarg(this)) return true; + if (options.keep_fnames) { + var sym = this.orig[0]; + if (sym instanceof AST_SymbolClass) return true; + if (sym instanceof AST_SymbolDefClass) return true; + if (sym instanceof AST_SymbolDefun) return true; + if (sym instanceof AST_SymbolLambda) return true; + } + if (!options.toplevel && this.global) return true; + return false; + }, }; -AST_Toplevel.DEFMETHOD("figure_out_scope", function(options){ +DEF_BITPROPS(SymbolDef, [ + "const_redefs", + "cross_loop", + "direct_access", + "exported", + "global", + "undeclared", +]); + +function is_funarg(def) { + return def.orig[0] instanceof AST_SymbolFunarg || def.orig[1] instanceof AST_SymbolFunarg; +} + +var unary_side_effects = makePredicate("delete ++ --"); + +function is_lhs(node, parent) { + if (parent instanceof AST_Assign) return parent.left === node && node; + if (parent instanceof AST_DefaultValue) return parent.name === node && node; + if (parent instanceof AST_Destructured) return node; + if (parent instanceof AST_DestructuredKeyVal) return node; + if (parent instanceof AST_ForEnumeration) return parent.init === node && node; + if (parent instanceof AST_Unary) return unary_side_effects[parent.operator] && parent.expression; +} + +AST_Toplevel.DEFMETHOD("figure_out_scope", function(options) { options = defaults(options, { - screw_ie8: false + cache: null, + ie: false, }); // pass 1: setup scope chaining and handle definitions var self = this; - var scope = self.parent_scope = null; var defun = null; - var nesting = 0; - var tw = new TreeWalker(function(node, descend){ - if (options.screw_ie8 && node instanceof AST_Catch) { - var save_scope = scope; - scope = new AST_Scope(node); - scope.init_scope_vars(nesting); - scope.parent_scope = save_scope; + var exported = false; + var next_def_id = 0; + var scope = self.parent_scope = null; + var tw = new TreeWalker(function(node, descend) { + if (node instanceof AST_DefClass) { + var save_exported = exported; + exported = tw.parent() instanceof AST_ExportDeclaration; + node.name.walk(tw); + exported = save_exported; + walk_scope(function() { + if (node.extends) node.extends.walk(tw); + node.properties.forEach(function(prop) { + prop.walk(tw); + }); + }); + return true; + } + if (node instanceof AST_Definitions) { + var save_exported = exported; + exported = tw.parent() instanceof AST_ExportDeclaration; descend(); - scope = save_scope; + exported = save_exported; return true; } - if (node instanceof AST_Scope) { - node.init_scope_vars(nesting); - var save_scope = node.parent_scope = scope; - var save_defun = defun; - defun = scope = node; - ++nesting; descend(); --nesting; - scope = save_scope; - defun = save_defun; - return true; // don't descend again in TreeWalker + if (node instanceof AST_LambdaDefinition) { + var save_exported = exported; + exported = tw.parent() instanceof AST_ExportDeclaration; + node.name.walk(tw); + exported = save_exported; + walk_scope(function() { + node.argnames.forEach(function(argname) { + argname.walk(tw); + }); + if (node.rest) node.rest.walk(tw); + walk_body(node, tw); + }); + return true; } - if (node instanceof AST_Directive) { - node.scope = scope; - push_uniq(scope.directives, node.value); + if (node instanceof AST_SwitchBranch) { + node.init_vars(scope); + descend(); + return true; + } + if (node instanceof AST_Try) { + walk_scope(function() { + walk_body(node, tw); + }); + if (node.bcatch) node.bcatch.walk(tw); + if (node.bfinally) node.bfinally.walk(tw); return true; } if (node instanceof AST_With) { - for (var s = scope; s; s = s.parent_scope) + var s = scope; + do { + s = s.resolve(); + if (s.uses_with) break; s.uses_with = true; - return; + } while (s = s.parent_scope); + walk_scope(descend); + return true; + } + if (node instanceof AST_BlockScope) { + walk_scope(descend); + return true; } if (node instanceof AST_Symbol) { node.scope = scope; } - if (node instanceof AST_SymbolLambda) { - defun.def_function(node); - } - else if (node instanceof AST_SymbolDefun) { - // Careful here, the scope where this should be defined is - // the parent scope. The reason is that we enter a new - // scope when we encounter the AST_Defun node (which is - // instanceof AST_Scope) but we get to the symbol a bit - // later. - (node.scope = defun.parent_scope).def_function(node); - } - else if (node instanceof AST_SymbolVar - || node instanceof AST_SymbolConst) { - var def = defun.def_variable(node); - def.constant = node instanceof AST_SymbolConst; - def.init = tw.parent().value; - } - else if (node instanceof AST_SymbolCatch) { - (options.screw_ie8 ? scope : defun) - .def_variable(node); + if (node instanceof AST_Label) { + node.thedef = node; + node.references = []; + } + if (node instanceof AST_SymbolCatch) { + scope.def_variable(node).defun = defun; + } else if (node instanceof AST_SymbolConst) { + var def = scope.def_variable(node); + def.defun = defun; + if (exported) def.exported = true; + } else if (node instanceof AST_SymbolDefun) { + var def = defun.def_function(node, tw.parent()); + if (exported) def.exported = true; + } else if (node instanceof AST_SymbolFunarg) { + defun.def_variable(node); + } else if (node instanceof AST_SymbolLambda) { + var def = defun.def_function(node, node.name == "arguments" ? undefined : defun); + if (options.ie && node.name != "arguments") def.defun = defun.parent_scope.resolve(); + } else if (node instanceof AST_SymbolLet) { + var def = scope.def_variable(node); + if (exported) def.exported = true; + } else if (node instanceof AST_SymbolVar) { + var def = defun.def_variable(node, node instanceof AST_SymbolImport ? undefined : null); + if (exported) def.exported = true; + } + + function walk_scope(descend) { + node.init_vars(scope); + var save_defun = defun; + var save_scope = scope; + if (node instanceof AST_Scope) defun = node; + scope = node; + descend(); + scope = save_scope; + defun = save_defun; } }); + self.make_def = function(orig, init) { + return new SymbolDef(++next_def_id, this, orig, init); + }; self.walk(tw); // pass 2: find back references and eval - var func = null; - var globals = self.globals = new Dictionary(); - var tw = new TreeWalker(function(node, descend){ + self.globals = new Dictionary(); + var in_arg = []; + var tw = new TreeWalker(function(node) { + if (node instanceof AST_Catch) { + if (!(node.argname instanceof AST_Destructured)) return; + in_arg.push(node); + node.argname.walk(tw); + in_arg.pop(); + walk_body(node, tw); + return true; + } if (node instanceof AST_Lambda) { - var prev_func = func; - func = node; - descend(); - func = prev_func; + in_arg.push(node); + if (node.name) node.name.walk(tw); + node.argnames.forEach(function(argname) { + argname.walk(tw); + }); + if (node.rest) node.rest.walk(tw); + in_arg.pop(); + walk_lambda(node, tw); + return true; + } + if (node instanceof AST_LoopControl) { + if (node.label) node.label.thedef.references.push(node); + return true; + } + if (node instanceof AST_SymbolDeclaration) { + var def = node.definition(); + def.preinit = def.references.length; + if (node instanceof AST_SymbolCatch) { + // ensure mangling works if `catch` reuses a scope variable + var redef = def.redefined(); + if (redef) for (var s = node.scope; s; s = s.parent_scope) { + if (!push_uniq(s.enclosed, redef)) break; + if (s === redef.scope) break; + } + } else if (node instanceof AST_SymbolConst) { + // ensure compression works if `const` reuses a scope variable + var redef = def.redefined(); + if (redef) redef.const_redefs = true; + } else if (def.scope !== node.scope && (node instanceof AST_SymbolDefun + || node instanceof AST_SymbolFunarg + || node instanceof AST_SymbolVar)) { + node.mark_enclosed(options); + var redef = node.scope.find_variable(node.name); + if (node.thedef !== redef) { + node.thedef = redef; + redef.orig.push(node); + node.mark_enclosed(options); + } + } + if (node.name != "arguments") return true; + var parent = node instanceof AST_SymbolVar && tw.parent(); + if (parent instanceof AST_VarDef && !parent.value) return true; + var sym = node.scope.resolve().find_variable("arguments"); + if (sym && is_arguments(sym)) sym.scope.uses_arguments = 3; return true; } if (node instanceof AST_SymbolRef) { var name = node.name; var sym = node.scope.find_variable(name); + for (var i = in_arg.length; i > 0 && sym;) { + i = in_arg.lastIndexOf(sym.scope, i - 1); + if (i < 0) break; + var decl = sym.orig[0]; + if (decl instanceof AST_SymbolCatch + || decl instanceof AST_SymbolFunarg + || decl instanceof AST_SymbolLambda) { + node.in_arg = true; + break; + } + sym = sym.scope.parent_scope.find_variable(name); + } if (!sym) { - var g; - if (globals.has(name)) { - g = globals.get(name); - } else { - g = new SymbolDef(self, globals.size(), node); - g.undeclared = true; - g.global = true; - globals.set(name, g); + sym = self.def_global(node); + } else if (name == "arguments" && is_arguments(sym)) { + var parent = tw.parent(); + if (is_lhs(node, parent)) { + sym.scope.uses_arguments = 3; + } else if (sym.scope.uses_arguments < 2 + && !(parent instanceof AST_PropAccess && parent.expression === node)) { + sym.scope.uses_arguments = 2; + } else if (!sym.scope.uses_arguments) { + sym.scope.uses_arguments = true; } - node.thedef = g; - if (name == "eval" && tw.parent() instanceof AST_Call) { - for (var s = node.scope; s && !s.uses_eval; s = s.parent_scope) + } + if (name == "eval") { + var parent = tw.parent(); + if (parent.TYPE == "Call" && parent.expression === node) { + var s = node.scope; + do { + s = s.resolve(); + if (s.uses_eval) break; s.uses_eval = true; + } while (s = s.parent_scope); + } else if (sym.undeclared) { + self.uses_eval = true; } - if (func && name == "arguments") { - func.uses_arguments = true; - } - } else { - node.thedef = sym; } - node.reference(); + if (sym.init instanceof AST_LambdaDefinition && sym.scope !== sym.init.name.scope) { + var scope = node.scope; + do { + if (scope === sym.init.name.scope) break; + } while (scope = scope.parent_scope); + if (!scope) sym.init = undefined; + } + node.thedef = sym; + node.reference(options); return true; } }); self.walk(tw); -}); -AST_Scope.DEFMETHOD("init_scope_vars", function(nesting){ - this.directives = []; // contains the directives defined in this scope, i.e. "use strict" - this.variables = new Dictionary(); // map name to AST_SymbolVar (variables defined in this scope; includes functions) - this.functions = new Dictionary(); // map name to AST_SymbolDefun (functions defined in this scope) - this.uses_with = false; // will be set to true if this or some nested scope uses the `with` statement - this.uses_eval = false; // will be set to true if this or nested scope uses the global `eval` - this.parent_scope = null; // the parent scope - this.enclosed = []; // a list of variables from this or outer scope(s) that are referenced from this or inner scopes - this.cname = -1; // the current index for mangling functions/variables - this.nesting = nesting; // the nesting level of this scope (0 means toplevel) + // pass 3: fix up any scoping issue with IE8 + if (options.ie) self.walk(new TreeWalker(function(node) { + if (node instanceof AST_SymbolCatch) { + var def = node.thedef; + var scope = def.defun; + if (def.name != "arguments" && scope.name instanceof AST_SymbolLambda && scope.name.name == def.name) { + scope = scope.parent_scope.resolve(); + } + redefine(node, scope); + return true; + } + if (node instanceof AST_SymbolLambda) { + var def = node.thedef; + if (!redefine(node, node.scope.parent_scope.resolve())) { + def.defun = undefined; + } else if (typeof node.thedef.init !== "undefined") { + node.thedef.init = false; + } else if (def.init) { + node.thedef.init = def.init; + } + return true; + } + })); + + function is_arguments(sym) { + return sym.orig[0] instanceof AST_SymbolFunarg + && !(sym.orig[1] instanceof AST_SymbolFunarg || sym.orig[2] instanceof AST_SymbolFunarg) + && !is_arrow(sym.scope); + } + + function redefine(node, scope) { + var name = node.name; + var old_def = node.thedef; + if (!all(old_def.orig, function(sym) { + return !(sym instanceof AST_SymbolConst || sym instanceof AST_SymbolLet); + })) return false; + var new_def = scope.find_variable(name); + if (new_def) { + var redef = new_def.redefined(); + if (redef) new_def = redef; + } else { + new_def = self.globals.get(name); + } + if (new_def) { + new_def.orig.push(node); + } else { + new_def = scope.def_variable(node); + } + if (new_def.undeclared) self.variables.set(name, new_def); + if (name == "arguments" && is_arguments(old_def) && node instanceof AST_SymbolLambda) return true; + old_def.defun = new_def.scope; + old_def.forEach(function(node) { + node.redef = old_def; + node.thedef = new_def; + node.reference(options); + }); + return true; + } }); -AST_Scope.DEFMETHOD("strict", function(){ - return this.has_directive("use strict"); +AST_Toplevel.DEFMETHOD("def_global", function(node) { + var globals = this.globals, name = node.name; + if (globals.has(name)) { + return globals.get(name); + } else { + var g = this.make_def(node); + g.undeclared = true; + g.global = true; + globals.set(name, g); + return g; + } }); -AST_Lambda.DEFMETHOD("init_scope_vars", function(){ - AST_Scope.prototype.init_scope_vars.apply(this, arguments); +function init_block_vars(scope, parent, orig) { + // variables from this or outer scope(s) that are referenced from this or inner scopes + scope.enclosed = orig ? orig.enclosed.slice() : []; + // map name to AST_SymbolDefun (functions defined in this scope) + scope.functions = orig ? orig.functions.clone() : new Dictionary(); + // map name to AST_SymbolVar (variables defined in this scope; includes functions) + scope.variables = orig ? orig.variables.clone() : new Dictionary(); + if (!parent) return; + // top-level tracking of SymbolDef instances + scope.make_def = parent.make_def; + // the parent scope (null if this is the top level) + scope.parent_scope = parent; +} + +function init_scope_vars(scope, parent, orig) { + init_block_vars(scope, parent, orig); + // will be set to true if this or nested scope uses the global `eval` + scope.uses_eval = false; + // will be set to true if this or some nested scope uses the `with` statement + scope.uses_with = false; +} + +AST_BlockScope.DEFMETHOD("init_vars", function(parent, orig) { + init_block_vars(this, parent, orig); +}); +AST_Scope.DEFMETHOD("init_vars", function(parent, orig) { + init_scope_vars(this, parent, orig); +}); +AST_Arrow.DEFMETHOD("init_vars", function(parent, orig) { + init_scope_vars(this, parent, orig); + return this; +}); +AST_AsyncArrow.DEFMETHOD("init_vars", function(parent, orig) { + init_scope_vars(this, parent, orig); +}); +AST_Lambda.DEFMETHOD("init_vars", function(parent, orig) { + init_scope_vars(this, parent, orig); this.uses_arguments = false; + this.def_variable(new AST_SymbolFunarg({ + name: "arguments", + scope: this, + start: this.start, + end: this.end, + })); + return this; }); -AST_SymbolRef.DEFMETHOD("reference", function() { +AST_Symbol.DEFMETHOD("mark_enclosed", function(options) { var def = this.definition(); - def.references.push(this); - var s = this.scope; - while (s) { - push_uniq(s.enclosed, def); + for (var s = this.scope; s; s = s.parent_scope) { + if (!push_uniq(s.enclosed, def)) break; + if (!options) { + s._var_names = undefined; + } else { + if (options.keep_fargs && s instanceof AST_Lambda) s.each_argname(function(arg) { + push_uniq(def.scope.enclosed, arg.definition()); + }); + if (options.keep_fnames) s.functions.each(function(d) { + push_uniq(def.scope.enclosed, d); + }); + } if (s === def.scope) break; - s = s.parent_scope; } - this.frame = this.scope.nesting - def.scope.nesting; }); -AST_Scope.DEFMETHOD("find_variable", function(name){ - if (name instanceof AST_Symbol) name = name.name; - return this.variables.get(name) - || (this.parent_scope && this.parent_scope.find_variable(name)); +AST_Symbol.DEFMETHOD("reference", function(options) { + this.definition().references.push(this); + this.mark_enclosed(options); }); -AST_Scope.DEFMETHOD("has_directive", function(value){ - return this.parent_scope && this.parent_scope.has_directive(value) - || (this.directives.indexOf(value) >= 0 ? this : null); +AST_BlockScope.DEFMETHOD("find_variable", function(name) { + return this.variables.get(name) + || this.parent_scope && this.parent_scope.find_variable(name); }); -AST_Scope.DEFMETHOD("def_function", function(symbol){ - this.functions.set(symbol.name, this.def_variable(symbol)); +AST_BlockScope.DEFMETHOD("def_function", function(symbol, init) { + var def = this.def_variable(symbol, init); + if (!def.init || def.init instanceof AST_LambdaDefinition) def.init = init; + this.functions.set(symbol.name, def); + return def; }); -AST_Scope.DEFMETHOD("def_variable", function(symbol){ - var def; - if (!this.variables.has(symbol.name)) { - def = new SymbolDef(this, this.variables.size(), symbol); +AST_BlockScope.DEFMETHOD("def_variable", function(symbol, init) { + var def = this.variables.get(symbol.name); + if (def) { + def.orig.push(symbol); + if (def.init instanceof AST_LambdaExpression) def.init = init; + } else { + def = this.make_def(symbol, init); this.variables.set(symbol.name, def); def.global = !this.parent_scope; - } else { - def = this.variables.get(symbol.name); - def.orig.push(symbol); } return symbol.thedef = def; }); -AST_Scope.DEFMETHOD("next_mangled", function(options){ - var ext = this.enclosed; - out: while (true) { - var m = base54(++this.cname); - if (!is_identifier(m)) continue; // skip over "do" - - // https://github.com/mishoo/UglifyJS2/issues/242 -- do not - // shadow a name excepted from mangling. - if (options.except.indexOf(m) >= 0) continue; - - // we must ensure that the mangled name does not shadow a name - // from some parent scope that is referenced in this or in - // inner scopes. - for (var i = ext.length; --i >= 0;) { - var sym = ext[i]; - var name = sym.mangled_name || (sym.unmangleable(options) && sym.name); - if (m == name) continue out; - } - return m; +function names_in_use(scope, options) { + var names = scope.names_in_use; + if (!names) { + scope.cname = -1; + scope.cname_holes = []; + scope.names_in_use = names = new Dictionary(); + var cache = options.cache && options.cache.props; + scope.enclosed.forEach(function(def) { + if (def.unmangleable(options)) names.set(def.name, true); + if (def.global && cache && cache.has(def.name)) { + names.set(cache.get(def.name), true); + } + }); + } + return names; +} + +function next_mangled_name(def, options) { + var scope = def.scope; + var in_use = names_in_use(scope, options); + var holes = scope.cname_holes; + var names = new Dictionary(); + var scopes = [ scope ]; + def.forEach(function(sym) { + var scope = sym.scope; + do { + if (member(scope, scopes)) break; + names_in_use(scope, options).each(function(marker, name) { + names.set(name, marker); + }); + scopes.push(scope); + } while (scope = scope.parent_scope); + }); + var name; + for (var i = 0; i < holes.length; i++) { + name = base54(holes[i]); + if (names.has(name)) continue; + holes.splice(i, 1); + in_use.set(name, true); + return name; } -}); - -AST_Function.DEFMETHOD("next_mangled", function(options, def){ - // #179, #326 - // in Safari strict mode, something like (function x(x){...}) is a syntax error; - // a function expression's argument cannot shadow the function expression's name - - var tricky_def = def.orig[0] instanceof AST_SymbolFunarg && this.name && this.name.definition(); while (true) { - var name = AST_Lambda.prototype.next_mangled.call(this, options, def); - if (!(tricky_def && tricky_def.mangled_name == name)) - return name; + name = base54(++scope.cname); + if (in_use.has(name) || RESERVED_WORDS[name] || options.reserved.has[name]) continue; + if (!names.has(name)) break; + holes.push(scope.cname); } -}); - -AST_Scope.DEFMETHOD("references", function(sym){ - if (sym instanceof AST_Symbol) sym = sym.definition(); - return this.enclosed.indexOf(sym) < 0 ? null : sym; -}); + in_use.set(name, true); + return name; +} -AST_Symbol.DEFMETHOD("unmangleable", function(options){ - return this.definition().unmangleable(options); -}); - -// property accessors are not mangleable -AST_SymbolAccessor.DEFMETHOD("unmangleable", function(){ - return true; +AST_Symbol.DEFMETHOD("unmangleable", function(options) { + var def = this.definition(); + return !def || def.unmangleable(options); }); // labels are always mangleable -AST_Label.DEFMETHOD("unmangleable", function(){ - return false; -}); - -AST_Symbol.DEFMETHOD("unreferenced", function(){ - return this.definition().references.length == 0 - && !(this.scope.uses_eval || this.scope.uses_with); -}); - -AST_Symbol.DEFMETHOD("undeclared", function(){ - return this.definition().undeclared; -}); - -AST_LabelRef.DEFMETHOD("undeclared", function(){ - return false; -}); - -AST_Label.DEFMETHOD("undeclared", function(){ - return false; -}); +AST_Label.DEFMETHOD("unmangleable", return_false); -AST_Symbol.DEFMETHOD("definition", function(){ +AST_Symbol.DEFMETHOD("definition", function() { return this.thedef; }); -AST_Symbol.DEFMETHOD("global", function(){ - return this.definition().global; -}); - -AST_Toplevel.DEFMETHOD("_default_mangler_options", function(options){ - return defaults(options, { - except : [], +function _default_mangler_options(options) { + options = defaults(options, { eval : false, - sort : false, + ie : false, + keep_fargs : false, + keep_fnames : false, + reserved : [], toplevel : false, - screw_ie8 : false, - keep_fnames : false + v8 : false, + webkit : false, }); -}); - -AST_Toplevel.DEFMETHOD("mangle_names", function(options){ - options = this._default_mangler_options(options); - // We only need to mangle declaration nodes. Special logic wired - // into the code generator will display the mangled name if it's - // present (and for AST_SymbolRef-s it'll use the mangled name of - // the AST_SymbolDeclaration that it points to). + if (!Array.isArray(options.reserved)) options.reserved = []; + // Never mangle `arguments` + push_uniq(options.reserved, "arguments"); + options.reserved.has = makePredicate(options.reserved); + return options; +} + +// We only need to mangle declaration nodes. Special logic wired into the code +// generator will display the mangled name if it is present (and for +// `AST_SymbolRef`s it will use the mangled name of the `AST_SymbolDeclaration` +// that it points to). +AST_Toplevel.DEFMETHOD("mangle_names", function(options) { + options = _default_mangler_options(options); + if (options.cache && options.cache.props) { + var mangled_names = names_in_use(this, options); + options.cache.props.each(function(mangled_name) { + mangled_names.set(mangled_name, true); + }); + } + var cutoff = 36; var lname = -1; - var to_mangle = []; - var tw = new TreeWalker(function(node, descend){ - if (node instanceof AST_LabeledStatement) { - // lname is incremented when we get to the AST_Label - var save_nesting = lname; - descend(); - lname = save_nesting; - return true; // don't descend again in TreeWalker - } - if (node instanceof AST_Scope) { - var p = tw.parent(), a = []; - node.variables.each(function(symbol){ - if (options.except.indexOf(symbol.name) < 0) { - a.push(symbol); + var redefined = []; + var tw = new TreeWalker(function(node, descend) { + var save_nesting; + if (node instanceof AST_BlockScope) { + // `lname` is incremented when we get to the `AST_Label` + if (node instanceof AST_LabeledStatement) save_nesting = lname; + if (options.webkit && node instanceof AST_IterationStatement && node.init instanceof AST_Let) { + node.init.definitions.forEach(function(defn) { + defn.name.match_symbol(function(sym) { + if (!(sym instanceof AST_SymbolLet)) return; + var def = sym.definition(); + var scope = sym.scope.parent_scope; + var redef = scope.def_variable(sym); + sym.thedef = def; + scope.to_mangle.push(redef); + def.redefined = function() { + return redef; + }; + }); + }, true); + } + var to_mangle = node.to_mangle = []; + node.variables.each(function(def, name) { + if (def.unmangleable(options)) { + names_in_use(node, options).set(name, true); + } else if (!defer_redef(def)) { + to_mangle.push(def); } }); - if (options.sort) a.sort(function(a, b){ - return b.references.length - a.references.length; - }); - to_mangle.push.apply(to_mangle, a); - return; + descend(); + if (options.cache && node instanceof AST_Toplevel) { + node.globals.each(mangle); + } + if (node instanceof AST_Defun && tw.has_directive("use asm")) { + var sym = new AST_SymbolRef(node.name); + sym.scope = node; + sym.reference(options); + } + if (to_mangle.length > cutoff) { + var indices = to_mangle.map(function(def, index) { + return index; + }).sort(function(i, j) { + return to_mangle[j].references.length - to_mangle[i].references.length || i - j; + }); + to_mangle = indices.slice(0, cutoff).sort(function(i, j) { + return i - j; + }).map(function(index) { + return to_mangle[index]; + }).concat(indices.slice(cutoff).sort(function(i, j) { + return i - j; + }).map(function(index) { + return to_mangle[index]; + })); + } + to_mangle.forEach(mangle); + if (node instanceof AST_LabeledStatement && !(options.v8 && in_label(tw))) lname = save_nesting; + return true; } if (node instanceof AST_Label) { var name; - do name = base54(++lname); while (!is_identifier(name)); + do { + name = base54(++lname); + } while (RESERVED_WORDS[name]); node.mangled_name = name; return true; } - if (options.screw_ie8 && node instanceof AST_SymbolCatch) { - to_mangle.push(node.definition()); - return; - } }); this.walk(tw); - to_mangle.forEach(function(def){ def.mangle(options) }); + redefined.forEach(mangle); + + function mangle(def) { + if (options.reserved.has[def.name]) return; + def.mangle(options); + } + + function defer_redef(def) { + var sym = def.orig[0]; + var redef = def.redefined(); + if (!redef) { + if (!(sym instanceof AST_SymbolConst)) return false; + var scope = def.scope.resolve(); + if (def.scope === scope) return false; + if (def.scope.parent_scope.find_variable(sym.name)) return false; + redef = scope.def_variable(sym); + scope.to_mangle.push(redef); + } + redefined.push(def); + def.references.forEach(reference); + if (sym instanceof AST_SymbolCatch || sym instanceof AST_SymbolConst) { + reference(sym); + def.redefined = function() { + return redef; + }; + } + return true; + + function reference(sym) { + sym.thedef = redef; + sym.reference(options); + sym.thedef = def; + } + } + + function in_label(tw) { + var level = 0, parent; + while (parent = tw.parent(level++)) { + if (parent instanceof AST_Block) return parent instanceof AST_Toplevel && !options.toplevel; + if (parent instanceof AST_LabeledStatement) return true; + } + } }); -AST_Toplevel.DEFMETHOD("compute_char_frequency", function(options){ - options = this._default_mangler_options(options); - var tw = new TreeWalker(function(node){ - if (node instanceof AST_Constant) - base54.consider(node.print_to_string()); - else if (node instanceof AST_Return) - base54.consider("return"); - else if (node instanceof AST_Throw) - base54.consider("throw"); - else if (node instanceof AST_Continue) - base54.consider("continue"); - else if (node instanceof AST_Break) - base54.consider("break"); - else if (node instanceof AST_Debugger) - base54.consider("debugger"); - else if (node instanceof AST_Directive) - base54.consider(node.value); - else if (node instanceof AST_While) - base54.consider("while"); - else if (node instanceof AST_Do) - base54.consider("do while"); - else if (node instanceof AST_If) { - base54.consider("if"); - if (node.alternative) base54.consider("else"); - } - else if (node instanceof AST_Var) - base54.consider("var"); - else if (node instanceof AST_Const) - base54.consider("const"); - else if (node instanceof AST_Lambda) - base54.consider("function"); - else if (node instanceof AST_For) - base54.consider("for"); - else if (node instanceof AST_ForIn) - base54.consider("for in"); - else if (node instanceof AST_Switch) - base54.consider("switch"); - else if (node instanceof AST_Case) - base54.consider("case"); - else if (node instanceof AST_Default) - base54.consider("default"); - else if (node instanceof AST_With) - base54.consider("with"); - else if (node instanceof AST_ObjectSetter) - base54.consider("set" + node.key); - else if (node instanceof AST_ObjectGetter) - base54.consider("get" + node.key); - else if (node instanceof AST_ObjectKeyVal) - base54.consider(node.key); - else if (node instanceof AST_New) - base54.consider("new"); - else if (node instanceof AST_This) - base54.consider("this"); - else if (node instanceof AST_Try) - base54.consider("try"); - else if (node instanceof AST_Catch) - base54.consider("catch"); - else if (node instanceof AST_Finally) - base54.consider("finally"); - else if (node instanceof AST_Symbol && node.unmangleable(options)) - base54.consider(node.name); - else if (node instanceof AST_Unary || node instanceof AST_Binary) - base54.consider(node.operator); - else if (node instanceof AST_Dot) - base54.consider(node.property); - }); - this.walk(tw); +AST_Toplevel.DEFMETHOD("find_colliding_names", function(options) { + var cache = options.cache && options.cache.props; + var avoid = Object.create(RESERVED_WORDS); + options.reserved.forEach(to_avoid); + this.globals.each(add_def); + this.walk(new TreeWalker(function(node) { + if (node instanceof AST_BlockScope) node.variables.each(add_def); + })); + return avoid; + + function to_avoid(name) { + avoid[name] = true; + } + + function add_def(def) { + var name = def.name; + if (def.global && cache && cache.has(name)) name = cache.get(name); + else if (!def.unmangleable(options)) return; + to_avoid(name); + } +}); + +AST_Toplevel.DEFMETHOD("expand_names", function(options) { + base54.reset(); + base54.sort(); + options = _default_mangler_options(options); + var avoid = this.find_colliding_names(options); + var cname = 0; + this.globals.each(rename); + this.walk(new TreeWalker(function(node) { + if (node instanceof AST_BlockScope) node.variables.each(rename); + })); + + function next_name() { + var name; + do { + name = base54(cname++); + } while (avoid[name]); + return name; + } + + function rename(def) { + if (def.global && options.cache) return; + if (def.unmangleable(options)) return; + if (options.reserved.has[def.name]) return; + var redef = def.redefined(); + var name = redef ? redef.rename || redef.name : next_name(); + def.rename = name; + def.forEach(function(sym) { + if (sym.definition() === def) sym.name = name; + }); + } +}); + +AST_Node.DEFMETHOD("tail_node", return_this); +AST_Sequence.DEFMETHOD("tail_node", function() { + return this.expressions[this.expressions.length - 1]; +}); + +AST_Toplevel.DEFMETHOD("compute_char_frequency", function(options) { + options = _default_mangler_options(options); + base54.reset(); + var fn = AST_Symbol.prototype.add_source_map; + try { + AST_Symbol.prototype.add_source_map = function() { + if (!this.unmangleable(options)) base54.consider(this.name, -1); + }; + if (options.properties) { + AST_Dot.prototype.add_source_map = function() { + base54.consider(this.property, -1); + }; + AST_Sub.prototype.add_source_map = function() { + skip_string(this.property); + }; + } + base54.consider(this.print_to_string(), 1); + } finally { + AST_Symbol.prototype.add_source_map = fn; + delete AST_Dot.prototype.add_source_map; + delete AST_Sub.prototype.add_source_map; + } base54.sort(); + + function skip_string(node) { + if (node instanceof AST_String) { + base54.consider(node.value, -1); + } else if (node instanceof AST_Conditional) { + skip_string(node.consequent); + skip_string(node.alternative); + } else if (node instanceof AST_Sequence) { + skip_string(node.tail_node()); + } + } }); var base54 = (function() { - var string = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ$_0123456789"; + var freq = Object.create(null); + function init(chars) { + var array = []; + for (var i = 0; i < chars.length; i++) { + var ch = chars[i]; + array.push(ch); + freq[ch] = -1e-2 * i; + } + return array; + } + var digits = init("0123456789"); + var leading = init("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ$_"); var chars, frequency; function reset() { - frequency = Object.create(null); - chars = string.split("").map(function(ch){ return ch.charCodeAt(0) }); - chars.forEach(function(ch){ frequency[ch] = 0 }); + chars = null; + frequency = Object.create(freq); } - base54.consider = function(str){ + base54.consider = function(str, delta) { for (var i = str.length; --i >= 0;) { - var code = str.charCodeAt(i); - if (code in frequency) ++frequency[code]; + frequency[str[i]] += delta; } }; + function compare(a, b) { + return frequency[b] - frequency[a]; + } base54.sort = function() { - chars = mergeSort(chars, function(a, b){ - if (is_digit(a) && !is_digit(b)) return 1; - if (is_digit(b) && !is_digit(a)) return -1; - return frequency[b] - frequency[a]; - }); + chars = leading.sort(compare).concat(digits).sort(compare); }; base54.reset = reset; reset(); - base54.get = function(){ return chars }; - base54.freq = function(){ return frequency }; function base54(num) { - var ret = "", base = 54; - num++; - do { - num--; - ret += String.fromCharCode(chars[num % base]); - num = Math.floor(num / base); - base = 64; - } while (num > 0); + var ret = leading[num % 54]; + for (num = Math.floor(num / 54); --num >= 0; num >>= 6) { + ret += chars[num & 0x3F]; + } return ret; - }; + } return base54; })(); - -AST_Toplevel.DEFMETHOD("scope_warnings", function(options){ - options = defaults(options, { - undeclared : false, // this makes a lot of noise - unreferenced : true, - assign_to_global : true, - func_arguments : true, - nested_defuns : true, - eval : true - }); - var tw = new TreeWalker(function(node){ - if (options.undeclared - && node instanceof AST_SymbolRef - && node.undeclared()) - { - // XXX: this also warns about JS standard names, - // i.e. Object, Array, parseInt etc. Should add a list of - // exceptions. - AST_Node.warn("Undeclared symbol: {name} [{file}:{line},{col}]", { - name: node.name, - file: node.start.file, - line: node.start.line, - col: node.start.col - }); - } - if (options.assign_to_global) - { - var sym = null; - if (node instanceof AST_Assign && node.left instanceof AST_SymbolRef) - sym = node.left; - else if (node instanceof AST_ForIn && node.init instanceof AST_SymbolRef) - sym = node.init; - if (sym - && (sym.undeclared() - || (sym.global() && sym.scope !== sym.definition().scope))) { - AST_Node.warn("{msg}: {name} [{file}:{line},{col}]", { - msg: sym.undeclared() ? "Accidental global?" : "Assignment to global", - name: sym.name, - file: sym.start.file, - line: sym.start.line, - col: sym.start.col - }); - } - } - if (options.eval - && node instanceof AST_SymbolRef - && node.undeclared() - && node.name == "eval") { - AST_Node.warn("Eval is used [{file}:{line},{col}]", node.start); - } - if (options.unreferenced - && (node instanceof AST_SymbolDeclaration || node instanceof AST_Label) - && !(node instanceof AST_SymbolCatch) - && node.unreferenced()) { - AST_Node.warn("{type} {name} is declared but not referenced [{file}:{line},{col}]", { - type: node instanceof AST_Label ? "Label" : "Symbol", - name: node.name, - file: node.start.file, - line: node.start.line, - col: node.start.col - }); - } - if (options.func_arguments - && node instanceof AST_Lambda - && node.uses_arguments) { - AST_Node.warn("arguments used in function {name} [{file}:{line},{col}]", { - name: node.name ? node.name.name : "anonymous", - file: node.start.file, - line: node.start.line, - col: node.start.col - }); - } - if (options.nested_defuns - && node instanceof AST_Defun - && !(tw.parent() instanceof AST_Scope)) { - AST_Node.warn("Function {name} declared in nested statement \"{type}\" [{file}:{line},{col}]", { - name: node.name.name, - type: tw.parent().TYPE, - file: node.start.file, - line: node.start.line, - col: node.start.col - }); - } - }); - this.walk(tw); -}); diff --git a/lib/sourcemap.js b/lib/sourcemap.js index 3998e4050ba..a230a44ced2 100644 --- a/lib/sourcemap.js +++ b/lib/sourcemap.js @@ -1,7 +1,7 @@ /*********************************************************************** A JavaScript tokenizer / parser / beautifier / compressor. - https://github.com/mishoo/UglifyJS2 + https://github.com/mishoo/UglifyJS -------------------------------- (C) --------------------------------- @@ -43,50 +43,153 @@ "use strict"; -// a small wrapper around fitzgen's source-map library -function SourceMap(options) { - options = defaults(options, { - file : null, - root : null, - orig : null, +var vlq_char = characters("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"); +var vlq_bits = vlq_char.reduce(function(map, ch, bits) { + map[ch] = bits; + return map; +}, Object.create(null)); - orig_line_diff : 0, - dest_line_diff : 0, - }); - var orig_map = options.orig && new MOZ_SourceMap.SourceMapConsumer(options.orig); - var generator; - if (orig_map) { - generator = MOZ_SourceMap.SourceMapGenerator.fromSourceMap(orig_map); - } else { - generator = new MOZ_SourceMap.SourceMapGenerator({ - file : options.file, - sourceRoot : options.root - }); - } - function add(source, gen_line, gen_col, orig_line, orig_col, name) { - if (orig_map) { - var info = orig_map.originalPositionFor({ - line: orig_line, - column: orig_col - }); - if (info.source === null) { - return; - } - source = info.source; - orig_line = info.line; - orig_col = info.column; - name = info.name || name; +function vlq_decode(indices, str) { + var value = 0; + var shift = 0; + for (var i = 0, j = 0; i < str.length; i++) { + var bits = vlq_bits[str[i]]; + value += (bits & 31) << shift; + if (bits & 32) { + shift += 5; + } else { + indices[j++] += value & 1 ? 0x80000000 | -(value >> 1) : value >> 1; + value = shift = 0; } - generator.addMapping({ - generated : { line: gen_line + options.dest_line_diff, column: gen_col }, - original : { line: orig_line + options.orig_line_diff, column: orig_col }, - source : source, - name : name - }); } + return j; +} + +function vlq_encode(num) { + var result = ""; + num = Math.abs(num) << 1 | num >>> 31; + do { + var bits = num & 31; + if (num >>>= 5) bits |= 32; + result += vlq_char[bits]; + } while (num); + return result; +} + +function create_array_map() { + var map = new Dictionary(); + var array = []; + array.index = function(name) { + var index = map.get(name); + if (!(index >= 0)) { + index = array.length; + array.push(name); + map.set(name, index); + } + return index; + }; + return array; +} + +function SourceMap(options) { + var sources = create_array_map(); + var sources_content = options.includeSources && new Dictionary(); + var names = create_array_map(); + var mappings = ""; + if (options.orig) Object.keys(options.orig).forEach(function(name) { + var map = options.orig[name]; + var indices = [ 0, 0, 1, 0, 0 ]; + options.orig[name] = { + names: map.names, + mappings: map.mappings.split(/;/).map(function(line) { + indices[0] = 0; + return line.split(/,/).map(function(segment) { + return indices.slice(0, vlq_decode(indices, segment)); + }); + }), + sources: map.sources, + }; + if (!sources_content || !map.sourcesContent) return; + for (var i = 0; i < map.sources.length; i++) { + var content = map.sourcesContent[i]; + if (content) sources_content.set(map.sources[i], content); + } + }); + var prev_source; + var generated_line = 1; + var generated_column = 0; + var source_index = 0; + var original_line = 1; + var original_column = 0; + var name_index = 0; return { - add : add, - get : function() { return generator }, - toString : function() { return generator.toString() } + add: options.orig ? function(source, gen_line, gen_col, orig_line, orig_col, name) { + var map = options.orig[source]; + if (map) { + var segments = map.mappings[orig_line - 1]; + if (!segments) return; + var indices; + for (var i = 0; i < segments.length; i++) { + var col = segments[i][0]; + if (orig_col >= col) indices = segments[i]; + if (orig_col <= col) break; + } + if (!indices || indices.length < 4) { + source = null; + } else { + source = map.sources[indices[1]]; + orig_line = indices[2]; + orig_col = indices[3]; + if (indices.length > 4) name = map.names[indices[4]]; + } + } + add(source, gen_line, gen_col, orig_line, orig_col, name); + } : add, + setSourceContent: sources_content ? function(source, content) { + if (!sources_content.has(source)) { + sources_content.set(source, content); + } + } : noop, + toString: function() { + return JSON.stringify({ + version: 3, + file: options.filename || undefined, + sourceRoot: options.root || undefined, + sources: sources, + sourcesContent: sources_content ? sources.map(function(source) { + return sources_content.get(source) || null; + }) : undefined, + names: names, + mappings: mappings, + }); + } }; -}; + + function add(source, gen_line, gen_col, orig_line, orig_col, name) { + if (prev_source == null && source == null) return; + prev_source = source; + if (generated_line < gen_line) { + generated_column = 0; + do { + mappings += ";"; + } while (++generated_line < gen_line); + } else if (mappings) { + mappings += ","; + } + mappings += vlq_encode(gen_col - generated_column); + generated_column = gen_col; + if (source == null) return; + var src_idx = sources.index(source); + mappings += vlq_encode(src_idx - source_index); + source_index = src_idx; + mappings += vlq_encode(orig_line - original_line); + original_line = orig_line; + mappings += vlq_encode(orig_col - original_column); + original_column = orig_col; + if (options.names && name != null) { + var name_idx = names.index(name); + mappings += vlq_encode(name_idx - name_index); + name_index = name_idx; + } + } +} diff --git a/lib/transform.js b/lib/transform.js index c3c34f58918..dcf90dfad58 100644 --- a/lib/transform.js +++ b/lib/transform.js @@ -1,7 +1,7 @@ /*********************************************************************** A JavaScript tokenizer / parser / beautifier / compressor. - https://github.com/mishoo/UglifyJS2 + https://github.com/mishoo/UglifyJS -------------------------------- (C) --------------------------------- @@ -43,8 +43,6 @@ "use strict"; -// Tree transformer helpers. - function TreeTransformer(before, after) { TreeWalker.call(this); this.before = before; @@ -52,167 +50,201 @@ function TreeTransformer(before, after) { } TreeTransformer.prototype = new TreeWalker; -(function(undefined){ - - function _(node, descend) { - node.DEFMETHOD("transform", function(tw, in_list){ - var x, y; - tw.push(this); - if (tw.before) x = tw.before(this, descend, in_list); - if (x === undefined) { - if (!tw.after) { - x = this; - descend(x, tw); - } else { - tw.stack[tw.stack.length - 1] = x = this.clone(); - descend(x, tw); - y = tw.after(x, in_list); - if (y !== undefined) x = y; - } - } - tw.pop(); - return x; - }); - }; - +(function(DEF) { function do_list(list, tw) { - return MAP(list, function(node){ + return List(list, function(node) { return node.transform(tw, true); }); - }; - - _(AST_Node, noop); + } - _(AST_LabeledStatement, function(self, tw){ + DEF(AST_Node, noop); + DEF(AST_LabeledStatement, function(self, tw) { self.label = self.label.transform(tw); self.body = self.body.transform(tw); }); - - _(AST_SimpleStatement, function(self, tw){ + DEF(AST_SimpleStatement, function(self, tw) { self.body = self.body.transform(tw); }); - - _(AST_Block, function(self, tw){ + DEF(AST_Block, function(self, tw) { self.body = do_list(self.body, tw); }); - - _(AST_DWLoop, function(self, tw){ + DEF(AST_Do, function(self, tw) { + self.body = self.body.transform(tw); + self.condition = self.condition.transform(tw); + }); + DEF(AST_While, function(self, tw) { self.condition = self.condition.transform(tw); self.body = self.body.transform(tw); }); - - _(AST_For, function(self, tw){ + DEF(AST_For, function(self, tw) { if (self.init) self.init = self.init.transform(tw); if (self.condition) self.condition = self.condition.transform(tw); if (self.step) self.step = self.step.transform(tw); self.body = self.body.transform(tw); }); - - _(AST_ForIn, function(self, tw){ + DEF(AST_ForEnumeration, function(self, tw) { self.init = self.init.transform(tw); self.object = self.object.transform(tw); self.body = self.body.transform(tw); }); - - _(AST_With, function(self, tw){ + DEF(AST_With, function(self, tw) { self.expression = self.expression.transform(tw); self.body = self.body.transform(tw); }); - - _(AST_Exit, function(self, tw){ + DEF(AST_Exit, function(self, tw) { if (self.value) self.value = self.value.transform(tw); }); - - _(AST_LoopControl, function(self, tw){ + DEF(AST_LoopControl, function(self, tw) { if (self.label) self.label = self.label.transform(tw); }); - - _(AST_If, function(self, tw){ + DEF(AST_If, function(self, tw) { self.condition = self.condition.transform(tw); self.body = self.body.transform(tw); if (self.alternative) self.alternative = self.alternative.transform(tw); }); - - _(AST_Switch, function(self, tw){ + DEF(AST_Switch, function(self, tw) { self.expression = self.expression.transform(tw); self.body = do_list(self.body, tw); }); - - _(AST_Case, function(self, tw){ + DEF(AST_Case, function(self, tw) { self.expression = self.expression.transform(tw); self.body = do_list(self.body, tw); }); - - _(AST_Try, function(self, tw){ + DEF(AST_Try, function(self, tw) { self.body = do_list(self.body, tw); if (self.bcatch) self.bcatch = self.bcatch.transform(tw); if (self.bfinally) self.bfinally = self.bfinally.transform(tw); }); - - _(AST_Catch, function(self, tw){ - self.argname = self.argname.transform(tw); + DEF(AST_Catch, function(self, tw) { + if (self.argname) self.argname = self.argname.transform(tw); self.body = do_list(self.body, tw); }); - - _(AST_Definitions, function(self, tw){ + DEF(AST_Definitions, function(self, tw) { self.definitions = do_list(self.definitions, tw); }); - - _(AST_VarDef, function(self, tw){ + DEF(AST_VarDef, function(self, tw) { self.name = self.name.transform(tw); if (self.value) self.value = self.value.transform(tw); }); - - _(AST_Lambda, function(self, tw){ + DEF(AST_DefaultValue, function(self, tw) { + self.name = self.name.transform(tw); + self.value = self.value.transform(tw); + }); + DEF(AST_Lambda, function(self, tw) { if (self.name) self.name = self.name.transform(tw); self.argnames = do_list(self.argnames, tw); + if (self.rest) self.rest = self.rest.transform(tw); self.body = do_list(self.body, tw); }); - - _(AST_Call, function(self, tw){ + function transform_arrow(self, tw) { + self.argnames = do_list(self.argnames, tw); + if (self.rest) self.rest = self.rest.transform(tw); + if (self.value) { + self.value = self.value.transform(tw); + } else { + self.body = do_list(self.body, tw); + } + } + DEF(AST_Arrow, transform_arrow); + DEF(AST_AsyncArrow, transform_arrow); + DEF(AST_Class, function(self, tw) { + if (self.name) self.name = self.name.transform(tw); + if (self.extends) self.extends = self.extends.transform(tw); + self.properties = do_list(self.properties, tw); + }); + DEF(AST_ClassProperty, function(self, tw) { + if (self.key instanceof AST_Node) self.key = self.key.transform(tw); + if (self.value) self.value = self.value.transform(tw); + }); + DEF(AST_Call, function(self, tw) { self.expression = self.expression.transform(tw); self.args = do_list(self.args, tw); }); - - _(AST_Seq, function(self, tw){ - self.car = self.car.transform(tw); - self.cdr = self.cdr.transform(tw); + DEF(AST_Sequence, function(self, tw) { + self.expressions = do_list(self.expressions, tw); }); - - _(AST_Dot, function(self, tw){ + DEF(AST_Await, function(self, tw) { self.expression = self.expression.transform(tw); }); - - _(AST_Sub, function(self, tw){ + DEF(AST_Yield, function(self, tw) { + if (self.expression) self.expression = self.expression.transform(tw); + }); + DEF(AST_Dot, function(self, tw) { + self.expression = self.expression.transform(tw); + }); + DEF(AST_Sub, function(self, tw) { self.expression = self.expression.transform(tw); self.property = self.property.transform(tw); }); - - _(AST_Unary, function(self, tw){ + DEF(AST_Spread, function(self, tw) { self.expression = self.expression.transform(tw); }); - - _(AST_Binary, function(self, tw){ + DEF(AST_Unary, function(self, tw) { + self.expression = self.expression.transform(tw); + }); + DEF(AST_Binary, function(self, tw) { self.left = self.left.transform(tw); self.right = self.right.transform(tw); }); - - _(AST_Conditional, function(self, tw){ + DEF(AST_Conditional, function(self, tw) { self.condition = self.condition.transform(tw); self.consequent = self.consequent.transform(tw); self.alternative = self.alternative.transform(tw); }); - - _(AST_Array, function(self, tw){ + DEF(AST_Array, function(self, tw) { self.elements = do_list(self.elements, tw); }); - - _(AST_Object, function(self, tw){ + DEF(AST_DestructuredArray, function(self, tw) { + self.elements = do_list(self.elements, tw); + if (self.rest) self.rest = self.rest.transform(tw); + }); + DEF(AST_DestructuredKeyVal, function(self, tw) { + if (self.key instanceof AST_Node) self.key = self.key.transform(tw); + self.value = self.value.transform(tw); + }); + DEF(AST_DestructuredObject, function(self, tw) { self.properties = do_list(self.properties, tw); + if (self.rest) self.rest = self.rest.transform(tw); }); - - _(AST_ObjectProperty, function(self, tw){ + DEF(AST_Object, function(self, tw) { + self.properties = do_list(self.properties, tw); + }); + DEF(AST_ObjectProperty, function(self, tw) { + if (self.key instanceof AST_Node) self.key = self.key.transform(tw); self.value = self.value.transform(tw); }); - -})(); + DEF(AST_ExportDeclaration, function(self, tw) { + self.body = self.body.transform(tw); + }); + DEF(AST_ExportDefault, function(self, tw) { + self.body = self.body.transform(tw); + }); + DEF(AST_ExportReferences, function(self, tw) { + self.properties = do_list(self.properties, tw); + }); + DEF(AST_Import, function(self, tw) { + if (self.all) self.all = self.all.transform(tw); + if (self.default) self.default = self.default.transform(tw); + if (self.properties) self.properties = do_list(self.properties, tw); + }); + DEF(AST_Template, function(self, tw) { + if (self.tag) self.tag = self.tag.transform(tw); + self.expressions = do_list(self.expressions, tw); + }); +})(function(node, descend) { + node.DEFMETHOD("transform", function(tw, in_list) { + var x, y; + tw.push(this); + if (tw.before) x = tw.before(this, descend, in_list); + if (typeof x === "undefined") { + x = this; + descend(x, tw); + if (tw.after) { + y = tw.after(x, in_list); + if (typeof y !== "undefined") x = y; + } + } + tw.pop(); + return x; + }); +}); diff --git a/lib/utils.js b/lib/utils.js index 7c6a1563185..779be75ee12 100644 --- a/lib/utils.js +++ b/lib/utils.js @@ -1,7 +1,7 @@ /*********************************************************************** A JavaScript tokenizer / parser / beautifier / compressor. - https://github.com/mishoo/UglifyJS2 + https://github.com/mishoo/UglifyJS -------------------------------- (C) --------------------------------- @@ -43,260 +43,258 @@ "use strict"; -function array_to_hash(a) { - var ret = Object.create(null); - for (var i = 0; i < a.length; ++i) - ret[a[i]] = true; - return ret; -}; - -function slice(a, start) { - return Array.prototype.slice.call(a, start || 0); -}; - function characters(str) { return str.split(""); -}; +} function member(name, array) { - for (var i = array.length; --i >= 0;) - if (array[i] == name) - return true; - return false; -}; + return array.indexOf(name) >= 0; +} function find_if(func, array) { - for (var i = 0, n = array.length; i < n; ++i) { - if (func(array[i])) - return array[i]; - } -}; - -function repeat_string(str, i) { - if (i <= 0) return ""; - if (i == 1) return str; - var d = repeat_string(str, i >> 1); - d += d; - if (i & 1) d += str; - return d; -}; + for (var i = array.length; --i >= 0;) if (func(array[i])) return array[i]; +} + +function configure_error_stack(ex, cause) { + var stack = ex.name + ": " + ex.message; + Object.defineProperty(ex, "stack", { + get: function() { + if (cause) { + cause.name = "" + ex.name; + stack = "" + cause.stack; + var msg = "" + cause.message; + cause = null; + var index = stack.indexOf(msg); + if (index < 0) { + index = 0; + } else { + index += msg.length; + index = stack.indexOf("\n", index) + 1; + } + stack = stack.slice(0, index) + stack.slice(stack.indexOf("\n", index) + 1); + } + return stack; + }, + }); +} function DefaultsError(msg, defs) { - Error.call(this, msg); - this.msg = msg; + this.message = msg; this.defs = defs; -}; + try { + throw new Error(msg); + } catch (cause) { + configure_error_stack(this, cause); + } +} DefaultsError.prototype = Object.create(Error.prototype); DefaultsError.prototype.constructor = DefaultsError; - -DefaultsError.croak = function(msg, defs) { - throw new DefaultsError(msg, defs); -}; +DefaultsError.prototype.name = "DefaultsError"; function defaults(args, defs, croak) { - if (args === true) - args = {}; - var ret = args || {}; - if (croak) for (var i in ret) if (ret.hasOwnProperty(i) && !defs.hasOwnProperty(i)) - DefaultsError.croak("`" + i + "` is not a supported option", defs); - for (var i in defs) if (defs.hasOwnProperty(i)) { - ret[i] = (args && args.hasOwnProperty(i)) ? args[i] : defs[i]; + if (croak) for (var i in args) { + if (HOP(args, i) && !HOP(defs, i)) throw new DefaultsError("`" + i + "` is not a supported option", defs); } - return ret; -}; - -function merge(obj, ext) { - for (var i in ext) if (ext.hasOwnProperty(i)) { - obj[i] = ext[i]; + for (var i in args) { + if (HOP(args, i)) defs[i] = args[i]; } - return obj; -}; + return defs; +} -function noop() {}; +function noop() {} +function return_false() { return false; } +function return_true() { return true; } +function return_this() { return this; } +function return_null() { return null; } -var MAP = (function(){ - function MAP(a, f, backwards) { - var ret = [], top = [], i; - function doit() { +var List = (function() { + function List(a, f) { + var ret = []; + for (var i = 0; i < a.length; i++) { var val = f(a[i], i); - var is_last = val instanceof Last; - if (is_last) val = val.v; - if (val instanceof AtTop) { - val = val.v; - if (val instanceof Splice) { - top.push.apply(top, backwards ? val.v.slice().reverse() : val.v); - } else { - top.push(val); - } - } - else if (val !== skip) { - if (val instanceof Splice) { - ret.push.apply(ret, backwards ? val.v.slice().reverse() : val.v); - } else { - ret.push(val); - } - } - return is_last; - }; - if (a instanceof Array) { - if (backwards) { - for (i = a.length; --i >= 0;) if (doit()) break; - ret.reverse(); - top.reverse(); + if (val === skip) continue; + if (val instanceof Splice) { + ret.push.apply(ret, val.v); } else { - for (i = 0; i < a.length; ++i) if (doit()) break; + ret.push(val); } } - else { - for (i in a) if (a.hasOwnProperty(i)) if (doit()) break; - } - return top.concat(ret); + return ret; + } + List.is_op = function(val) { + return val === skip || val instanceof Splice; + }; + List.splice = function(val) { + return new Splice(val); }; - MAP.at_top = function(val) { return new AtTop(val) }; - MAP.splice = function(val) { return new Splice(val) }; - MAP.last = function(val) { return new Last(val) }; - var skip = MAP.skip = {}; - function AtTop(val) { this.v = val }; - function Splice(val) { this.v = val }; - function Last(val) { this.v = val }; - return MAP; + var skip = List.skip = {}; + function Splice(val) { + this.v = val; + } + return List; })(); function push_uniq(array, el) { - if (array.indexOf(el) < 0) - array.push(el); -}; + if (array.indexOf(el) < 0) return array.push(el); +} function string_template(text, props) { - return text.replace(/\{(.+?)\}/g, function(str, p){ - return props[p]; + return text.replace(/\{([^{}]+)\}/g, function(str, p) { + var value = p == "this" ? props : props[p]; + if (value instanceof AST_Node) return value.print_to_string(); + if (value instanceof AST_Token) return value.file + ":" + value.line + "," + value.col; + return value; }); -}; +} function remove(array, el) { - for (var i = array.length; --i >= 0;) { - if (array[i] === el) array.splice(i, 1); - } -}; - -function mergeSort(array, cmp) { - if (array.length < 2) return array.slice(); - function merge(a, b) { - var r = [], ai = 0, bi = 0, i = 0; - while (ai < a.length && bi < b.length) { - cmp(a[ai], b[bi]) <= 0 - ? r[i++] = a[ai++] - : r[i++] = b[bi++]; - } - if (ai < a.length) r.push.apply(r, a.slice(ai)); - if (bi < b.length) r.push.apply(r, b.slice(bi)); - return r; - }; - function _ms(a) { - if (a.length <= 1) - return a; - var m = Math.floor(a.length / 2), left = a.slice(0, m), right = a.slice(m); - left = _ms(left); - right = _ms(right); - return merge(left, right); - }; - return _ms(array); -}; + var index = array.indexOf(el); + if (index >= 0) array.splice(index, 1); +} -function set_difference(a, b) { - return a.filter(function(el){ - return b.indexOf(el) < 0; - }); -}; - -function set_intersection(a, b) { - return a.filter(function(el){ - return b.indexOf(el) >= 0; - }); -}; - -// this function is taken from Acorn [1], written by Marijn Haverbeke -// [1] https://github.com/marijnh/acorn function makePredicate(words) { - if (!(words instanceof Array)) words = words.split(" "); - var f = "", cats = []; - out: for (var i = 0; i < words.length; ++i) { - for (var j = 0; j < cats.length; ++j) - if (cats[j][0].length == words[i].length) { - cats[j].push(words[i]); - continue out; - } - cats.push([words[i]]); - } - function compareTo(arr) { - if (arr.length == 1) return f += "return str === " + JSON.stringify(arr[0]) + ";"; - f += "switch(str){"; - for (var i = 0; i < arr.length; ++i) f += "case " + JSON.stringify(arr[i]) + ":"; - f += "return true}return false;"; - } - // When there are more than three length categories, an outer - // switch first dispatches on the lengths, to save on comparisons. - if (cats.length > 3) { - cats.sort(function(a, b) {return b.length - a.length;}); - f += "switch(str.length){"; - for (var i = 0; i < cats.length; ++i) { - var cat = cats[i]; - f += "case " + cat[0].length + ":"; - compareTo(cat); - } - f += "}"; - // Otherwise, simply generate a flat `switch` statement. - } else { - compareTo(words); - } - return new Function("str", f); -}; + if (!Array.isArray(words)) words = words.split(" "); + var map = Object.create(null); + words.forEach(function(word) { + map[word] = true; + }); + return map; +} function all(array, predicate) { for (var i = array.length; --i >= 0;) - if (!predicate(array[i])) + if (!predicate(array[i], i)) return false; return true; -}; +} function Dictionary() { - this._values = Object.create(null); - this._size = 0; -}; + this.values = Object.create(null); +} Dictionary.prototype = { set: function(key, val) { - if (!this.has(key)) ++this._size; - this._values["$" + key] = val; + if (key == "__proto__") { + this.proto_value = val; + } else { + this.values[key] = val; + } return this; }, add: function(key, val) { - if (this.has(key)) { - this.get(key).push(val); + var list = this.get(key); + if (list) { + list.push(val); } else { this.set(key, [ val ]); } return this; }, - get: function(key) { return this._values["$" + key] }, + get: function(key) { + return key == "__proto__" ? this.proto_value : this.values[key]; + }, del: function(key) { - if (this.has(key)) { - --this._size; - delete this._values["$" + key]; + if (key == "__proto__") { + delete this.proto_value; + } else { + delete this.values[key]; } return this; }, - has: function(key) { return ("$" + key) in this._values }, + has: function(key) { + return key == "__proto__" ? "proto_value" in this : key in this.values; + }, + all: function(predicate) { + for (var i in this.values) + if (!predicate(this.values[i], i)) return false; + if ("proto_value" in this && !predicate(this.proto_value, "__proto__")) return false; + return true; + }, each: function(f) { - for (var i in this._values) - f(this._values[i], i.substr(1)); + for (var i in this.values) + f(this.values[i], i); + if ("proto_value" in this) f(this.proto_value, "__proto__"); }, size: function() { - return this._size; + return Object.keys(this.values).length + ("proto_value" in this); }, map: function(f) { var ret = []; - for (var i in this._values) - ret.push(f(this._values[i], i.substr(1))); + for (var i in this.values) + ret.push(f(this.values[i], i)); + if ("proto_value" in this) ret.push(f(this.proto_value, "__proto__")); return ret; - } + }, + clone: function() { + var ret = new Dictionary(); + this.each(function(value, i) { + ret.set(i, value); + }); + return ret; + }, + toObject: function() { + var obj = {}; + this.each(function(value, i) { + obj["$" + i] = value; + }); + return obj; + }, }; +Dictionary.fromObject = function(obj) { + var dict = new Dictionary(); + for (var i in obj) + if (HOP(obj, i)) dict.set(i.slice(1), obj[i]); + return dict; +}; + +function HOP(obj, prop) { + return Object.prototype.hasOwnProperty.call(obj, prop); +} + +// return true if the node at the top of the stack (that means the +// innermost node in the current output) is lexically the first in +// a statement. +function first_in_statement(stack, arrow, export_default) { + var node = stack.parent(-1); + for (var i = 0, p; p = stack.parent(i++); node = p) { + if (is_arrow(p)) { + return arrow && p.value === node; + } else if (p instanceof AST_Binary) { + if (p.left === node) continue; + } else if (p.TYPE == "Call") { + if (p.expression === node) continue; + } else if (p instanceof AST_Conditional) { + if (p.condition === node) continue; + } else if (p instanceof AST_ExportDefault) { + return export_default; + } else if (p instanceof AST_PropAccess) { + if (p.expression === node) continue; + } else if (p instanceof AST_Sequence) { + if (p.expressions[0] === node) continue; + } else if (p instanceof AST_SimpleStatement) { + return true; + } else if (p instanceof AST_Template) { + if (p.tag === node) continue; + } else if (p instanceof AST_UnaryPostfix) { + if (p.expression === node) continue; + } + return false; + } +} + +function DEF_BITPROPS(ctor, props) { + if (props.length > 31) throw new Error("Too many properties: " + props.length + "\n" + props.join(", ")); + props.forEach(function(name, pos) { + var mask = 1 << pos; + Object.defineProperty(ctor.prototype, name, { + get: function() { + return !!(this._bits & mask); + }, + set: function(val) { + if (val) + this._bits |= mask; + else + this._bits &= ~mask; + }, + }); + }); +} diff --git a/package.json b/package.json index 949fec10e5c..c826d257d00 100644 --- a/package.json +++ b/package.json @@ -1,32 +1,28 @@ { "name": "unuglify-js", "description": "JavaScript tool that renames variables and parameters to names based on statistical model learnt from thousands of open source projects", - "homepage": "https://github.com/eth-srl/UnuglifyJS", - "main": "tools/node.js", - "version": "0.9.5", - "engines": { "node" : ">=0.4.0" }, - "maintainers": [{ - "name": "Pavol Bielik", - "email": "pavol.bielik@inf.ethz.ch" - }], - "repository": { - "type": "git", - "url": "https://github.com/eth-srl/UnuglifyJS" - }, - "dependencies": { - "source-map" : "0.1.34", - "yargs": "~1.3.3", - "colors": "~1.0.3" - }, - "devDependencies": { - "acorn": "~0.6.0", - "escodegen": "~1.3.3", - "esfuzz": "~0.3.1", - "estraverse": "~1.5.1" - }, - "bin": { - "unuglifyjs" : "bin/unuglifyjs" - }, - "license": "BSD", - "scripts": {"test": "node test/run-tests.js"} + "homepage": "https://github.com/eth-srl/UnuglifyJS", + "license": "BSD", + "version": "0.9.5", + "engines": { "node" : ">=0.8.0" }, + "maintainers": [{ + "name": "Pavol Bielik", + "email": "pavol.bielik@inf.ethz.ch" + }], + "repository": "/eth-srl/UnuglifyJS", + "main": "tools/node.js", + "bin": { + "unuglifyjs" : "bin/unuglifyjs" + }, + "dependencies": { + "yargs": "~1.3.3", + "colors": "~1.0.3" + }, + "devDependencies": { + "acorn": "~8.7.1", + "semver": "~6.3.0" + }, + "scripts": { + "test": "node test/feature_extraction.js" + } } diff --git a/test/benchmark.js b/test/benchmark.js new file mode 100644 index 00000000000..da29558c1b1 --- /dev/null +++ b/test/benchmark.js @@ -0,0 +1,99 @@ +#! /usr/bin/env node +// -*- js -*- + +"use strict"; + +require("../tools/tty"); +var createHash = require("crypto").createHash; +var fetch = require("./fetch"); +var spawn = require("child_process").spawn; +var zlib = require("zlib"); +var args = process.argv.slice(2); +if (!args.length) args.push("-mc"); +args.unshift("bin/uglifyjs"); +args.push("--timings"); +var urls = [ + "https://code.jquery.com/jquery-3.4.1.js", + "https://code.angularjs.org/1.7.8/angular.js", + "https://unpkg.com/mathjs@6.2.3/dist/math.js", + "https://unpkg.com/react@15.3.2/dist/react.js", + "https://cdnjs.cloudflare.com/ajax/libs/d3/6.7.0/d3.js", + "https://cdnjs.cloudflare.com/ajax/libs/antd/4.18.7/antd.js", + "https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.js", + "https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.15/lodash.js", + "https://cdnjs.cloudflare.com/ajax/libs/ember.js/2.12.2/ember.prod.js", + "https://raw.githubusercontent.com/kangax/html-minifier/v4.0.0/dist/htmlminifier.js", +]; +var results = {}; +var remaining = 2 * urls.length; +function done() { + if (!--remaining) { + var failures = []; + var sum = { input: 0, output: 0, gzip: 0 }; + urls.forEach(function(url) { + var info = results[url]; + console.log(); + console.log(url); + console.log(info.log); + console.log("Original:", info.input, "bytes"); + console.log("Uglified:", info.output, "bytes"); + console.log("GZipped: ", info.gzip, "bytes"); + console.log("SHA1 sum:", info.sha1); + if (info.code) { + failures.push(url); + } + sum.input += info.input; + sum.output += info.output; + sum.gzip += info.gzip; + }); + if (failures.length) { + console.error("Benchmark failed:"); + failures.forEach(function(url) { + console.error(url); + }); + process.exit(1); + } else { + console.log(); + console.log("Subtotal"); + console.log(); + console.log("Original:", sum.input, "bytes"); + console.log("Uglified:", sum.output, "bytes"); + console.log("GZipped: ", sum.gzip, "bytes"); + } + } +} +urls.forEach(function(url) { + results[url] = { + input: 0, + output: 0, + gzip: 0, + log: "" + }; + fetch(url, function(err, res) { + if (err) throw err; + var uglifyjs = spawn(process.argv[0], args, { silent: true }); + res.on("data", function(data) { + results[url].input += data.length; + }).pipe(uglifyjs.stdin); + var sha1 = createHash("sha1"); + uglifyjs.stdout.on("data", function(data) { + results[url].output += data.length; + }).pipe(zlib.createGzip({ + level: zlib.Z_BEST_COMPRESSION + })).on("data", function(data) { + results[url].gzip += data.length; + sha1.update(data); + }).on("end", function() { + results[url].sha1 = sha1.digest("hex"); + done(); + }); + uglifyjs.stderr.setEncoding("utf8"); + uglifyjs.stderr.on("data", function(data) { + results[url].log += data; + }); + uglifyjs.on("exit", function(code) { + results[url].code = code; + done(); + }); + }); +}); diff --git a/test/compress.js b/test/compress.js new file mode 100644 index 00000000000..ebef61f038d --- /dev/null +++ b/test/compress.js @@ -0,0 +1,497 @@ +"use strict"; + +require("../tools/tty"); + +var assert = require("assert"); +var child_process = require("child_process"); +var fs = require("fs"); +var path = require("path"); +var sandbox = require("./sandbox"); +var semver = require("semver"); +var U = require("./node"); + +var batch = 50; +var dir = path.resolve(path.dirname(module.filename), "compress"); +if (process.argv.length > 3) { + var file = process.argv[2]; + var start = process.argv[3] | 0; + var minify_options = require("./ufuzz/options.json").map(JSON.stringify); + var tests = parse_test(path.resolve(dir, file)); + process.exit(Object.keys(tests).slice(start, start + batch).filter(function(name) { + return !test_case(tests[name]); + }).length); +} else { + var files = process.argv.length == 3 ? [ process.argv[2] ] : fs.readdirSync(dir).filter(function(name) { + return /\.js$/i.test(name); + }); + var failures = 0; + var failed_files = Object.create(null); + (function next(file, start, length) { + if (start < length) { + child_process.spawn(process.argv[0], [ process.argv[1], file, start, batch ], { + stdio: [ "ignore", 1, 2 ] + }).on("exit", function(code) { + if (code) { + failures += code; + failed_files[file] = true; + } + next(file, start + batch, length); + }); + } else if (file = files.shift()) { + log("--- {file}", { file: file }); + start = 0; + length = Object.keys(parse_test(path.resolve(dir, file))).length; + next(file, start, length); + } else if (failures) { + console.error(); + console.error("!!! Failed " + failures + " test case(s)."); + console.error("!!! " + Object.keys(failed_files).join(", ")); + process.exit(1); + } + })(); +} + +function evaluate(code) { + if (code instanceof U.AST_Node) code = make_code(code, { beautify: true }); + return new Function("return(" + code + ")")(); +} + +function log() { + console.log("%s", tmpl.apply(null, arguments)); +} + +function make_code(ast, options, expression) { + var stream = U.OutputStream(options); + if (expression) ast = ast.clone(true).unwrap_expression(); + ast.print(stream); + return stream.get(); +} + +function parse_test(file) { + var script = fs.readFileSync(file, "utf8"); + try { + var ast = U.parse(script, { filename: file, module: "" }); + } catch (e) { + console.error("Caught error while parsing tests in " + file); + console.error(e); + process.exit(1); + } + var tests = Object.create(null); + var tw = new U.TreeWalker(function(node, descend) { + if (node instanceof U.AST_LabeledStatement + && tw.parent() instanceof U.AST_Toplevel) { + var name = node.label.name; + if (name in tests) { + throw new Error('Duplicated test name "' + name + '" in ' + file); + } + tests[name] = get_one_test(name, node.body); + return true; + } + if (!(node instanceof U.AST_Toplevel)) croak(node); + }); + ast.walk(tw); + return tests; + + function croak(node) { + throw new Error(tmpl("Can't understand test file {file} [{line},{col}]\n{code}", { + file: file, + line: node.start.line, + col: node.start.col, + code: make_code(node, { beautify: false }), + })); + } + + function read_string(stat) { + if (stat.TYPE == "SimpleStatement") { + var body = stat.body; + switch (body.TYPE) { + case "String": + return body.value; + case "Array": + return body.elements.map(function(element) { + if (element.TYPE !== "String") + throw new Error("Should be array of strings"); + return element.value; + }).join("\n"); + } + } + throw new Error("Should be string or array of strings"); + } + + function get_one_test(name, block) { + var test = { name: name, options: {} }; + var tw = new U.TreeWalker(function(node, descend) { + if (node instanceof U.AST_Assign) { + if (!(node.left instanceof U.AST_SymbolRef)) { + croak(node); + } + var name = node.left.name; + assert.ok([ + "beautify", + "expression", + "mangle", + "options", + "rename", + ].indexOf(name) >= 0, tmpl("Unsupported setting {name} [{line},{col}]", { + name: name, + line: node.start.line, + col: node.start.col, + })); + test[name] = evaluate(node.right); + return true; + } + if (node instanceof U.AST_LabeledStatement) { + var label = node.label; + assert.ok([ + "input", + "expect", + "expect_exact", + "expect_warnings", + "expect_stdout", + "node_version", + ].indexOf(label.name) >= 0, tmpl("Unsupported label {name} [{line},{col}]", { + name: label.name, + line: label.start.line, + col: label.start.col, + })); + var stat = node.body; + if (label.name == "expect_exact" || label.name == "node_version") { + test[label.name] = read_string(stat); + } else if (label.name == "expect_stdout") { + var body = stat.body; + if (body instanceof U.AST_Boolean) { + test[label.name] = body.value; + } else if (body instanceof U.AST_Call) { + var ctor = global[body.expression.name]; + assert.ok(ctor === Error || ctor.prototype instanceof Error, tmpl("Unsupported expect_stdout format [{line},{col}]", { + line: label.start.line, + col: label.start.col, + })); + test[label.name] = ctor.apply(null, body.args.map(function(node) { + assert.ok(node instanceof U.AST_Constant, tmpl("Unsupported expect_stdout format [{line},{col}]", { + line: label.start.line, + col: label.start.col, + })); + return node.value; + })); + } else { + test[label.name] = read_string(stat) + "\n"; + } + } else { + test[label.name] = stat; + } + return true; + } + }); + block.walk(tw); + return test; + } +} + +// Try to reminify original input with standard options +// to see if it matches expect_stdout. +function reminify(expression, orig_options, input_code, input_formatted, stdout) { + for (var i = 0; i < minify_options.length; i++) { + var options = JSON.parse(minify_options[i]); + if (expression) { + if (!options.parse || typeof options.parse != "object") options.parse = {}; + options.parse.expression = true; + if (options.compress == null) options.compress = {}; + if (options.compress) { + if (typeof options.compress != "object") options.compress = {}; + options.compress.expression = true; + } + } + [ + "keep_fargs", + "keep_fnames", + ].forEach(function(name) { + if (name in orig_options) options[name] = orig_options[name]; + }); + var options_formatted = JSON.stringify(options, null, 4); + options.validate = true; + var result = U.minify(input_code, options); + if (result.error) { + log([ + "!!! failed input reminify", + "---INPUT---", + "{input}", + "---OPTIONS---", + "{options}", + "--ERROR---", + "{error}", + "", + "", + ].join("\n"), { + input: input_formatted, + options: options_formatted, + error: result.error, + }); + return false; + } else { + var toplevel = sandbox.has_toplevel(options); + var expected = stdout[toplevel ? 1 : 0]; + var actual = run_code(expression, result.code, toplevel); + if (typeof expected != "string" && typeof actual != "string" && expected.name == actual.name) { + actual = expected; + } + if (!sandbox.same_stdout(expected, actual)) { + log([ + "!!! failed running reminified input", + "---INPUT---", + "{input}", + "---OPTIONS---", + "{options}", + "---OUTPUT---", + "{output}", + "---EXPECTED {expected_type}---", + "{expected}", + "---ACTUAL {actual_type}---", + "{actual}", + "", + "", + ].join("\n"), { + input: input_formatted, + options: options_formatted, + output: result.code, + expected_type: typeof expected == "string" ? "STDOUT" : "ERROR", + expected: expected, + actual_type: typeof actual == "string" ? "STDOUT" : "ERROR", + actual: actual, + }); + return false; + } + } + } + return true; +} + +function run_code(expression, code, toplevel) { + return sandbox.run_code(expression ? "console.log(" + code + ");" : code, toplevel); +} + +function test_case(test) { + log(" Running test [{name}]", { name: test.name }); + U.AST_Node.enable_validation(); + var output_options = test.beautify || {}; + var expect; + if (test.expect) { + expect = to_toplevel(test.expect, test.mangle, test.expression); + expect = make_code(expect, output_options, test.expression); + } else { + expect = test.expect_exact; + } + var input = to_toplevel(test.input, test.mangle, test.expression); + var input_code = make_code(input, { + keep_quoted_props: true, + }, test.expression); + var input_formatted = make_code(test.input, { + annotations: true, + beautify: true, + comments: "all", + keep_quoted_props: true, + quote_style: 3, + }); + try { + input.validate_ast(); + U.parse(input_code, { expression: test.expression }); + } catch (ex) { + log([ + "!!! Cannot parse input", + "---INPUT---", + "{input}", + "--PARSE ERROR--", + "{error}", + "", + "", + ].join("\n"), { + input: input_formatted, + error: ex, + }); + return false; + } + var warnings_emitted = []; + if (test.expect_warnings) { + var expected_warnings = make_code(test.expect_warnings, { + beautify: false, + quote_style: 2, // force double quote to match JSON + }); + U.AST_Node.log_function(function(text) { + warnings_emitted.push(text); + }, /"INFO: /.test(expected_warnings)); + } + var quoted_props; + if (test.mangle && test.mangle.properties && test.mangle.properties.keep_quoted) { + quoted_props = test.mangle.properties.reserved; + if (!Array.isArray(quoted_props)) quoted_props = []; + test.mangle.properties.reserved = quoted_props; + U.reserve_quoted_keys(input, quoted_props); + } + if (test.rename) { + input.figure_out_scope(test.mangle); + input.expand_names(test.mangle); + } + var cmp = new U.Compressor(test.options, true); + var output = cmp.compress(input); + output.figure_out_scope(test.mangle); + if (test.mangle) { + output.compute_char_frequency(test.mangle); + output.mangle_names(test.mangle); + if (quoted_props) U.reserve_quoted_keys(input, quoted_props); + if (test.mangle.properties) U.mangle_properties(output, test.mangle.properties); + } + var output_code = make_code(output, output_options, test.expression); + U.AST_Node.log_function(); + if (expect != output_code) { + log([ + "!!! failed", + "---INPUT---", + "{input}", + "---OUTPUT---", + "{output}", + "---EXPECTED---", + "{expected}", + "", + "", + ].join("\n"), { + input: input_formatted, + output: output_code, + expected: expect + }); + return false; + } + // expect == output + try { + output.validate_ast(); + U.parse(output_code, { expression: test.expression }); + } catch (ex) { + log([ + "!!! Test matched expected result but cannot parse output", + "---INPUT---", + "{input}", + "---OUTPUT---", + "{output}", + "--REPARSE ERROR--", + "{error}", + "", + "", + ].join("\n"), { + input: input_formatted, + output: output_code, + error: ex, + }); + return false; + } + if (test.expect_warnings) { + warnings_emitted = warnings_emitted.map(function(input) { + return input.split(process.cwd() + path.sep).join("").split(path.sep).join("/"); + }); + var actual_warnings = JSON.stringify(warnings_emitted); + if (expected_warnings != actual_warnings) { + log([ + "!!! failed", + "---INPUT---", + "{input}", + "---EXPECTED WARNINGS---", + "{expected_warnings}", + "---ACTUAL WARNINGS---", + "{actual_warnings}", + "", + "", + ].join("\n"), { + input: input_formatted, + expected_warnings: expected_warnings, + actual_warnings: actual_warnings, + }); + return false; + } + } + if (test.expect_stdout && (!test.node_version || semver.satisfies(process.version, test.node_version))) { + var stdout = [ run_code(test.expression, input_code), run_code(test.expression, input_code, true) ]; + var toplevel = sandbox.has_toplevel({ + compress: test.options, + mangle: test.mangle + }); + var actual = stdout[toplevel ? 1 : 0]; + if (test.expect_stdout === true || test.expect_stdout instanceof Error && test.expect_stdout.name === actual.name) { + test.expect_stdout = actual; + } + if (!sandbox.same_stdout(test.expect_stdout, actual)) { + log([ + "!!! Invalid input or expected stdout", + "---INPUT---", + "{input}", + "---EXPECTED {expected_type}---", + "{expected}", + "---ACTUAL {actual_type}---", + "{actual}", + "", + "", + ].join("\n"), { + input: input_formatted, + expected_type: typeof test.expect_stdout == "string" ? "STDOUT" : "ERROR", + expected: test.expect_stdout, + actual_type: typeof actual == "string" ? "STDOUT" : "ERROR", + actual: actual, + }); + return false; + } + actual = run_code(test.expression, output_code, toplevel); + if (!sandbox.same_stdout(test.expect_stdout, actual)) { + log([ + "!!! failed", + "---INPUT---", + "{input}", + "---EXPECTED {expected_type}---", + "{expected}", + "---ACTUAL {actual_type}---", + "{actual}", + "", + "", + ].join("\n"), { + input: input_formatted, + expected_type: typeof test.expect_stdout == "string" ? "STDOUT" : "ERROR", + expected: test.expect_stdout, + actual_type: typeof actual == "string" ? "STDOUT" : "ERROR", + actual: actual, + }); + return false; + } + if (!reminify(test.expression, test.options, input_code, input_formatted, stdout)) { + return false; + } + } + return true; +} + +function tmpl() { + return U.string_template.apply(null, arguments); +} + +function to_toplevel(input, mangle_options, expression) { + if (!(input instanceof U.AST_BlockStatement)) throw new Error("Unsupported input syntax"); + var offset = input.start.line; + var tokens = []; + input.walk(new U.TreeWalker(function(node) { + if (U.push_uniq(tokens, node.start)) node.start.line -= offset; + if (U.push_uniq(tokens, node.end)) node.end.line -= offset; + })); + var toplevel; + if (!expression) { + var directive = true; + toplevel = new U.AST_Toplevel(input.transform(new U.TreeTransformer(function(node) { + if (!directive) return node; + if (node === input) return; + if (node instanceof U.AST_SimpleStatement && node.body instanceof U.AST_String) { + return new U.AST_Directive(node.body); + } + directive = false; + }))); + } else if (input.body.length == 1) { + toplevel = input.body[0].wrap_expression(); + } else { + throw new Error("Invalid expression"); + } + toplevel.figure_out_scope(mangle_options); + return toplevel; +} diff --git a/test/compress/angular-inject.js b/test/compress/angular-inject.js deleted file mode 100644 index 8b24c846458..00000000000 --- a/test/compress/angular-inject.js +++ /dev/null @@ -1,67 +0,0 @@ -ng_inject_defun: { - options = { - angular: true - }; - input: { - /*@ngInject*/ - function Controller(dependency) { - return dependency; - } - } - expect: { - function Controller(dependency) { - return dependency; - } - Controller.$inject=['dependency'] - } -} - -ng_inject_assignment: { - options = { - angular: true - }; - input: { - /*@ngInject*/ - var Controller = function(dependency) { - return dependency; - } - } - expect: { - var Controller = function(dependency) { - return dependency; - } - Controller.$inject=['dependency'] - } -} - -ng_inject_inline: { - options = { - angular: true - }; - input: { - angular.module('a'). - factory('b', - /*@ngInject*/ - function(dependency) { - return dependency; - }). - directive('c', - /*@ngInject*/ - function(anotherDependency) { - return anotherDependency; - }) - } - expect: { - angular.module('a'). - factory('b',[ - 'dependency', - function(dependency) { - return dependency; - }]). - directive('c',[ - 'anotherDependency', - function(anotherDependency) { - return anotherDependency; - }]) - } -} diff --git a/test/compress/annotations.js b/test/compress/annotations.js new file mode 100644 index 00000000000..4fdbbd8af5a --- /dev/null +++ b/test/compress/annotations.js @@ -0,0 +1,474 @@ +issue_2629_1: { + options = { + annotations: true, + side_effects: true, + } + beautify = { + comments: "all", + } + input: { + /*@__PURE__*/ a(); + /*@__PURE__*/ (b()); + (/*@__PURE__*/ c)(); + (/*@__PURE__*/ d()); + } + expect_exact: "c();" +} + +issue_2629_2: { + options = { + annotations: true, + side_effects: true, + } + beautify = { + comments: "all", + } + input: { + /*@__PURE__*/ a(1)(2)(3); + /*@__PURE__*/ (b(1))(2)(3); + /*@__PURE__*/ (c(1)(2))(3); + /*@__PURE__*/ (d(1)(2)(3)); + (/*@__PURE__*/ e)(1)(2)(3); + (/*@__PURE__*/ f(1))(2)(3); + (/*@__PURE__*/ g(1)(2))(3); + (/*@__PURE__*/ h(1)(2)(3)); + } + expect_exact: [ + "e(1)(2)(3);", + "f(1)(2)(3);", + "g(1)(2)(3);", + ] +} + +issue_2629_3: { + options = { + annotations: true, + side_effects: true, + } + beautify = { + comments: "all", + } + input: { + /*@__PURE__*/ a.x(1).y(2).z(3); + /*@__PURE__*/ (b.x)(1).y(2).z(3); + /*@__PURE__*/ (c.x(1)).y(2).z(3); + /*@__PURE__*/ (d.x(1).y)(2).z(3); + /*@__PURE__*/ (e.x(1).y(2)).z(3); + /*@__PURE__*/ (f.x(1).y(2).z)(3); + /*@__PURE__*/ (g.x(1).y(2).z(3)); + (/*@__PURE__*/ h).x(1).y(2).z(3); + (/*@__PURE__*/ i.x)(1).y(2).z(3); + (/*@__PURE__*/ j.x(1)).y(2).z(3); + (/*@__PURE__*/ k.x(1).y)(2).z(3); + (/*@__PURE__*/ l.x(1).y(2)).z(3); + (/*@__PURE__*/ m.x(1).y(2).z)(3); + (/*@__PURE__*/ n.x(1).y(2).z(3)); + } + expect_exact: [ + "h.x(1).y(2).z(3);", + "i.x(1).y(2).z(3);", + "j.x(1).y(2).z(3);", + "k.x(1).y(2).z(3);", + "l.x(1).y(2).z(3);", + "m.x(1).y(2).z(3);", + ] +} + +issue_2629_4: { + options = { + annotations: true, + side_effects: true, + } + input: { + (/*@__PURE__*/ x(), y()); + (w(), /*@__PURE__*/ x(), y()); + } + expect: { + y(); + w(), y(); + } +} + +issue_2629_5: { + options = { + annotations: true, + side_effects: true, + } + input: { + [ /*@__PURE__*/ x() ]; + [ /*@__PURE__*/ x(), y() ]; + [ w(), /*@__PURE__*/ x(), y() ]; + } + expect: { + y(); + w(), y(); + } +} + +issue_2638: { + options = { + annotations: true, + side_effects: true, + } + beautify = { + comments: "all", + } + input: { + /*@__PURE__*/(g() || h())(x(), y()); + (/*@__PURE__*/ (a() || b()))(c(), d()); + } + expect_exact: [ + "x(),y();", + "(a()||b())(c(),d());", + ] +} + +issue_2705_1: { + options = { + annotations: true, + side_effects: true, + } + beautify = { + comments: "all", + } + input: { + /*@__PURE__*/ new a(); + /*@__PURE__*/ (new b()); + new (/*@__PURE__*/ c)(); + (/*@__PURE__*/ new d()); + } + expect_exact: [ + "new c;", + ] +} + +issue_2705_2: { + options = { + annotations: true, + side_effects: true, + } + beautify = { + comments: "all", + } + input: { + /*@__PURE__*/ new a(1)(2)(3); + /*@__PURE__*/ new (b(1))(2)(3); + /*@__PURE__*/ new (c(1)(2))(3); + /*@__PURE__*/ new (d(1)(2)(3)); + new (/*@__PURE__*/ e)(1)(2)(3); + (/*@__PURE__*/ new f(1))(2)(3); + (/*@__PURE__*/ new g(1)(2))(3); + (/*@__PURE__*/ new h(1)(2)(3)); + } + expect_exact: [ + "new e(1)(2)(3);", + "new f(1)(2)(3);", + "new g(1)(2)(3);", + ] +} + +issue_2705_3: { + options = { + annotations: true, + side_effects: true, + } + beautify = { + comments: "all", + } + input: { + /*@__PURE__*/ new a.x(1).y(2).z(3); + /*@__PURE__*/ new (b.x)(1).y(2).z(3); + /*@__PURE__*/ new (c.x(1)).y(2).z(3); + /*@__PURE__*/ new (d.x(1).y)(2).z(3); + /*@__PURE__*/ new (e.x(1).y(2)).z(3); + /*@__PURE__*/ new (f.x(1).y(2).z)(3); + /*@__PURE__*/ new (g.x(1).y(2).z(3)); + new (/*@__PURE__*/ h).x(1).y(2).z(3); + /* */ new (/*@__PURE__*/ i.x)(1).y(2).z(3); + (/*@__PURE__*/ new j.x(1)).y(2).z(3); + (/*@__PURE__*/ new k.x(1).y)(2).z(3); + (/*@__PURE__*/ new l.x(1).y(2)).z(3); + (/*@__PURE__*/ new m.x(1).y(2).z)(3); + (/*@__PURE__*/ new n.x(1).y(2).z(3)); + } + expect_exact: [ + "new h.x(1).y(2).z(3);", + "/* */new i.x(1).y(2).z(3);", + "new j.x(1).y(2).z(3);", + "new k.x(1).y(2).z(3);", + "new l.x(1).y(2).z(3);", + "new m.x(1).y(2).z(3);", + ] +} + +issue_2705_4: { + options = { + annotations: true, + side_effects: true, + } + input: { + (/*@__PURE__*/ new x(), y()); + (w(), /*@__PURE__*/ new x(), y()); + } + expect: { + y(); + w(), y(); + } +} + +issue_2705_5: { + options = { + annotations: true, + side_effects: true, + } + input: { + [ /*@__PURE__*/ new x() ]; + [ /*@__PURE__*/ new x(), y() ]; + [ w(), /*@__PURE__*/ new x(), y() ]; + } + expect: { + y(); + w(), y(); + } +} + +issue_2705_6: { + options = { + annotations: true, + side_effects: true, + } + beautify = { + comments: "all", + } + input: { + /*@__PURE__*/new (g() || h())(x(), y()); + /* */ new (/*@__PURE__*/ (a() || b()))(c(), d()); + } + expect_exact: [ + "x(),y();", + "/* */new(a()||b())(c(),d());", + ] +} + +issue_3858: { + options = { + annotations: true, + collapse_vars: true, + inline: true, + keep_fargs: false, + unsafe: true, + unused: true, + } + input: { + var f = function(a) { + return /*@__PURE__*/ function(b) { + console.log(b); + }(a); + }; + f("PASS"); + } + expect: { + var f = function(a) { + return function() { + console.log(a); + }(); + }; + f("PASS"); + } + expect_stdout: "PASS" +} + +inline_pure_call_1: { + options = { + annotations: true, + collapse_vars: true, + inline: true, + keep_fargs: false, + reduce_vars: true, + sequences: true, + side_effects: true, + toplevel: true, + unused: true, + } + input: { + var f = function(a) { + return /*@__PURE__*/ function(b) { + console.log(b); + }(a); + }; + f("PASS"); + } + expect: {} +} + +inline_pure_call_2: { + options = { + annotations: true, + collapse_vars: true, + inline: true, + keep_fargs: false, + reduce_vars: true, + sequences: true, + side_effects: true, + toplevel: true, + unused: true, + } + input: { + var f = function(a) { + return /*@__PURE__*/ function(b) { + console.log(b); + }(a); + }; + var a = f("PASS"); + } + expect: {} +} + +inline_pure_call_3: { + options = { + annotations: true, + collapse_vars: true, + evaluate: true, + inline: true, + keep_fargs: false, + passes: 2, + reduce_vars: true, + toplevel: true, + unused: true, + } + input: { + var f = function(a) { + return /*@__PURE__*/ function(b) { + console.log(b); + }(a); + }; + var a = f("PASS"); + console.log(a); + } + expect: { + var a = function() { + console.log("PASS"); + }(); + console.log(a); + } + expect_stdout: [ + "PASS", + "undefined", + ] +} + +inline_pure_call_4: { + options = { + annotations: true, + evaluate: true, + reduce_vars: true, + toplevel: true, + unused: true, + } + input: { + var a = /*@__PURE__*/ function() { + return console.log("PASS"), 42; + }(); + console.log(a); + } + expect: { + var a = function() { + return console.log("PASS"), 42; + }(); + console.log(a); + } + expect_stdout: [ + "PASS", + "42", + ] +} + +compress_and_output_annotations_enabled: { + options = { + annotations: true, + side_effects: true, + } + beautify = { + annotations: true, + beautify: true, + comments: false, + } + input: { + var top = /*@__PURE__*/ foo(); + /*@__PURE__*/ a(1)(2)(3); + /*@__PURE__*/ (b(1))(2)(3); + /*@__PURE__*/ (c(1)(2))(3); + /*@__PURE__*/ (d(1)(2)(3)); + (/*@__PURE__*/ e)(1)(2)(3); + (/*@__PURE__*/ f(1))(2)(3); + (/*@__PURE__*/ g(1)(2))(3); + (/*@__PURE__*/ h(1)(2)(3)); + /*@__PURE__*/ l(1).p(2); + (/*@__PURE__*/ m(1)).p(2); + (/*@__PURE__*/ n(1).p)(2); + (/*@__PURE__*/ o(1).p(2)); + } + expect_exact: [ + "var top = /*@__PURE__*/foo();", + "", + "e(1)(2)(3);", + "", + "f(1)(2)(3);", + "", + "g(1)(2)(3);", + "", + "m(1).p(2);", + "", + "n(1).p(2);", + ] +} + +compress_annotations_disabled_output_annotations_enabled: { + options = { + annotations: false, + evaluate: true, + sequences: true, + side_effects: true, + } + beautify = { + annotations: true, + comments: true, + } + input: { + /*@__PURE__*/ a(1+2); + /*#__PURE__*/ (b(2+3)); + (/*@__PURE__*/ c)(side_effect); + (/*#__PURE__*/ d(effect())); + } + expect_exact: [ + "/*@__PURE__*/a(3),", + "/*@__PURE__*/b(5),", + "c(side_effect),", + "/*@__PURE__*/d(effect());", + ] +} + +compress_and_output_annotations_disabled: { + options = { + annotations: false, + evaluate: true, + sequences: true, + side_effects: true, + } + beautify = { + annotations: false, + comments: true, + } + input: { + /*@__PURE__*/ a(1+2); + /*@__PURE__*/ (b(2+3)); + (/*@__PURE__*/ c)(side_effect); + (/*@__PURE__*/ d(effect())); + } + expect_exact: [ + "a(3),", + "b(5),", + "c(side_effect),", + "d(effect());", + ] +} diff --git a/test/compress/arguments.js b/test/compress/arguments.js new file mode 100644 index 00000000000..a59b869f061 --- /dev/null +++ b/test/compress/arguments.js @@ -0,0 +1,1054 @@ +replace_index: { + options = { + arguments: true, + evaluate: true, + properties: true, + } + input: { + var arguments = []; + console.log(arguments[0]); + (function() { + console.log(arguments[1], arguments["1"], arguments["foo"]); + })("bar", 42); + (function(a, b) { + console.log(arguments[1], arguments["1"], arguments["foo"]); + })("bar", 42); + (function(arguments) { + console.log(arguments[1], arguments["1"], arguments["foo"]); + })("bar", 42); + (function() { + var arguments; + console.log(arguments[1], arguments["1"], arguments["foo"]); + })("bar", 42); + } + expect: { + var arguments = []; + console.log(arguments[0]); + (function() { + console.log(arguments[1], arguments[1], arguments.foo); + })("bar", 42); + (function(a, b) { + console.log(b, b, arguments.foo); + })("bar", 42); + (function(arguments) { + console.log(arguments[1], arguments[1], arguments.foo); + })("bar", 42); + (function() { + var arguments; + console.log(arguments[1], arguments[1], arguments.foo); + })("bar", 42); + } + expect_stdout: [ + "undefined", + "42 42 undefined", + "42 42 undefined", + "a a undefined", + "42 42 undefined", + ] +} + +replace_index_strict: { + options = { + arguments: true, + evaluate: true, + properties: true, + reduce_vars: true, + } + input: { + "use strict"; + (function() { + console.log(arguments[1], arguments["1"], arguments["foo"]); + })("bar", 42); + (function(a, b) { + console.log(arguments[1], arguments["1"], arguments["foo"]); + })("bar", 42); + } + expect: { + "use strict"; + (function() { + console.log(arguments[1], arguments[1], arguments.foo); + })("bar", 42); + (function(a, b) { + console.log(b, b, arguments.foo); + })("bar", 42); + } + expect_stdout: [ + "42 42 undefined", + "42 42 undefined", + ] +} + +replace_index_drop_fargs_1: { + options = { + arguments: true, + evaluate: true, + keep_fargs: false, + properties: true, + reduce_vars: true, + } + input: { + var arguments = []; + console.log(arguments[0]); + (function() { + console.log(arguments[1], arguments["1"], arguments["foo"]); + })("bar", 42); + (function(a, b) { + console.log(arguments[1], arguments["1"], arguments["foo"]); + })("bar", 42); + (function(arguments) { + console.log(arguments[1], arguments["1"], arguments["foo"]); + })("bar", 42); + (function() { + var arguments; + console.log(arguments[1], arguments["1"], arguments["foo"]); + })("bar", 42); + (function() { + var arguments = { + 1: "foo", + foo: "bar", + }; + console.log(arguments[1], arguments["1"], arguments["foo"]); + })("bar", 42); + } + expect: { + var arguments = []; + console.log(arguments[0]); + (function(argument_0, argument_1) { + console.log(argument_1, argument_1, arguments.foo); + })("bar", 42); + (function(a, b) { + console.log(b, b, arguments.foo); + })("bar", 42); + (function(arguments) { + console.log("bar"[1], "bar"[1], "bar".foo); + })("bar", 42); + (function(argument_0, argument_1) { + var arguments; + console.log(argument_1, argument_1, arguments.foo); + })("bar", 42); + (function() { + var arguments = { + 1: "foo", + foo: "bar", + }; + console.log(arguments[1], arguments[1], arguments.foo); + })("bar", 42); + } + expect_stdout: [ + "undefined", + "42 42 undefined", + "42 42 undefined", + "a a undefined", + "42 42 undefined", + "foo foo bar", + ] +} + +replace_index_drop_fargs_2: { + options = { + arguments: true, + evaluate: true, + keep_fargs: false, + properties: true, + reduce_vars: true, + } + input: { + "use strict"; + (function() { + console.log(arguments[1], arguments["1"], arguments["foo"]); + })("bar", 42); + (function(a, b) { + console.log(arguments[1], arguments["1"], arguments["foo"]); + })("bar", 42); + } + expect: { + "use strict"; + (function(argument_0, argument_1) { + console.log(argument_1, argument_1, arguments.foo); + })("bar", 42); + (function(a, b) { + console.log(b, b, arguments.foo); + })("bar", 42); + } + expect_stdout: [ + "42 42 undefined", + "42 42 undefined", + ] +} + +modified: { + options = { + arguments: true, + } + input: { + (function(a, b) { + var c = arguments[0]; + var d = arguments[1]; + var a = "foo"; + b++; + arguments[0] = "moo"; + arguments[1] *= 2; + console.log(a, b, c, d, arguments[0], arguments[1]); + })("bar", 42); + } + expect: { + (function(a, b) { + var c = a; + var d = b; + var a = "foo"; + b++; + a = "moo"; + b *= 2; + console.log(a, b, c, d, a, b); + })("bar", 42); + } + expect_stdout: "moo 86 bar 42 moo 86" +} + +modified_strict: { + options = { + arguments: true, + reduce_vars: true, + } + input: { + "use strict"; + (function(a, b) { + var c = arguments[0]; + var d = arguments[1]; + var a = "foo"; + b++; + arguments[0] = "moo"; + arguments[1] *= 2; + console.log(a, b, c, d, arguments[0], arguments[1]); + })("bar", 42); + } + expect: { + "use strict"; + (function(a, b) { + var c = arguments[0]; + var d = arguments[1]; + var a = "foo"; + b++; + arguments[0] = "moo"; + arguments[1] *= 2; + console.log(a, b, c, d, arguments[0], arguments[1]); + })("bar", 42); + } + expect_stdout: "foo 43 bar 42 moo 84" +} + +duplicate_argname: { + options = { + arguments: true, + } + input: { + (function(a, b, a) { + console.log(a, b, arguments[0], arguments[1], arguments[2]); + })("foo", 42, "bar"); + } + expect: { + (function(a, b, a) { + console.log(a, b, arguments[0], b, a); + })("foo", 42, "bar"); + } + expect_stdout: "bar 42 foo 42 bar" +} + +fraction: { + options = { + arguments: true, + keep_fargs: false, + reduce_vars: true, + } + input: { + console.log(function() { + return arguments[0.3]; + }("FAIL") || "PASS"); + } + expect: { + console.log(function() { + return arguments[0.3]; + }("FAIL") || "PASS"); + } + expect_stdout: "PASS" +} + +issue_3273: { + options = { + arguments: true, + } + input: { + (function(a) { + console.log(arguments[0], a); + arguments[0]++; + console.log(arguments[0], a); + })(0); + } + expect: { + (function(a) { + console.log(a, a); + a++; + console.log(a, a); + })(0); + } + expect_stdout: [ + "0 0", + "1 1", + ] +} + +issue_3273_no_call_arg: { + options = { + arguments: true, + } + input: { + (function(a) { + arguments[0] = "FAIL"; + console.log(a); + })(); + } + expect: { + (function(a) { + arguments[0] = "FAIL"; + console.log(a); + })(); + } + expect_stdout: "undefined" +} + +issue_3273_reduce_vars: { + options = { + arguments: true, + reduce_vars: true, + } + input: { + (function(a) { + console.log(arguments[0], a); + arguments[0]++; + console.log(arguments[0], a); + })(0); + } + expect: { + (function(a) { + console.log(a, a); + a++; + console.log(a, a); + })(0); + } + expect_stdout: [ + "0 0", + "1 1", + ] +} + +issue_3273_local_strict: { + options = { + arguments: true, + } + input: { + (function(a) { + "use strict"; + console.log(arguments[0], a); + arguments[0]++; + console.log(arguments[0], a); + })(0); + } + expect: { + (function(a) { + "use strict"; + console.log(arguments[0], a); + arguments[0]++; + console.log(arguments[0], a); + })(0); + } + expect_stdout: [ + "0 0", + "1 0", + ] +} + +issue_3273_local_strict_reduce_vars: { + options = { + arguments: true, + reduce_vars: true, + } + input: { + (function(a) { + "use strict"; + console.log(arguments[0], a); + arguments[0]++; + console.log(arguments[0], a); + })(0); + } + expect: { + (function(a) { + "use strict"; + console.log(arguments[0], a); + arguments[0]++; + console.log(arguments[0], a); + })(0); + } + expect_stdout: [ + "0 0", + "1 0", + ] +} + +issue_3273_global_strict: { + options = { + arguments: true, + } + input: { + "use strict"; + (function(a) { + console.log(arguments[0], a); + arguments[0]++; + console.log(arguments[0], a); + })(0); + } + expect: { + "use strict"; + (function(a) { + console.log(arguments[0], a); + arguments[0]++; + console.log(arguments[0], a); + })(0); + } + expect_stdout: [ + "0 0", + "1 0", + ] +} + +issue_3273_global_strict_reduce_vars: { + options = { + arguments: true, + reduce_vars: true, + } + input: { + "use strict"; + (function(a) { + console.log(arguments[0], a); + arguments[0]++; + console.log(arguments[0], a); + })(0); + } + expect: { + "use strict"; + (function(a) { + console.log(arguments[0], a); + arguments[0]++; + console.log(arguments[0], a); + })(0); + } + expect_stdout: [ + "0 0", + "1 0", + ] +} + +issue_3273_drop_fargs_1: { + options = { + arguments: true, + keep_fargs: false, + reduce_vars: true, + } + input: { + (function() { + "use strict"; + arguments[0]++; + console.log(arguments[0]); + })(0); + } + expect: { + (function(argument_0) { + "use strict"; + argument_0++; + console.log(argument_0); + })(0); + } + expect_stdout: "1" +} + +issue_3273_drop_fargs_2: { + options = { + arguments: true, + keep_fargs: false, + reduce_vars: true, + } + input: { + (function() { + "use strict"; + arguments[0]++; + console.log(arguments[0]); + })(0); + } + expect: { + (function(argument_0) { + "use strict"; + argument_0++; + console.log(argument_0); + })(0); + } + expect_stdout: "1" +} + +issue_3282_1: { + options = { + arguments: true, + reduce_funcs: true, + reduce_vars: true, + keep_fargs: false, + unused: true, + } + input: { + (function(t) { + return function() { + t(); + }; + })(function() { + 'use strict'; + function e() { + return arguments[0]; + } + e(); + e(); + })(); + } + expect: { + (function() { + return function() { + (function() { + "use strict"; + function e() { + return arguments[0]; + } + e(); + e(); + })(); + }; + })()(); + } + expect_stdout: true +} + +issue_3282_1_passes: { + options = { + arguments: true, + passes: 2, + reduce_funcs: true, + reduce_vars: true, + keep_fargs: false, + unused: true, + } + input: { + (function(t) { + return function() { + t(); + }; + })(function() { + 'use strict'; + function e() { + return arguments[0]; + } + e(); + e(); + })(); + } + expect: { + (function() { + return function() { + (function() { + "use strict"; + function e(argument_0) { + return argument_0; + } + e(); + e(); + })(); + }; + })()(); + } + expect_stdout: true +} + +issue_3282_2: { + options = { + arguments: true, + reduce_vars: true, + keep_fargs: false, + unused: true, + } + input: { + (function(f) { + f(); + })(function() { + return (function(t) { + return function() { + t(); + }; + })(function() { + 'use strict'; + function e() { + return arguments[0]; + } + e(); + e(); + })(); + }); + } + expect: { + (function() { + (function() { + return function(t) { + return function() { + t(); + }; + }(function() { + "use strict"; + function e() { + return arguments[0]; + } + e(); + e(); + })(); + })(); + })(); + } + expect_stdout: true +} + +issue_3282_2_passes: { + options = { + arguments: true, + passes: 2, + reduce_vars: true, + keep_fargs: false, + unused: true, + } + input: { + (function(f) { + f(); + })(function() { + return (function(t) { + return function() { + t(); + }; + })(function() { + 'use strict'; + function e() { + return arguments[0]; + } + e(); + e(); + })(); + }); + } + expect: { + (function() { + (function() { + return function(t) { + return function() { + t(); + }; + }(function() { + "use strict"; + function e(argument_0) { + return argument_0; + } + e(); + e(); + })(); + })(); + })(); + } + expect_stdout: true +} + +issue_3420_1: { + options = { + arguments: true, + keep_fargs: false, + reduce_vars: true, + } + input: { + console.log(function() { + return function() { + return arguments[0]; + }; + }().length); + } + expect: { + console.log(function() { + return function() { + return arguments[0]; + }; + }().length); + } + expect_stdout: "0" +} + +issue_3420_2: { + options = { + arguments: true, + keep_fargs: false, + reduce_vars: true, + } + input: { + var foo = function() { + delete arguments[0]; + }; + foo(); + } + expect: { + var foo = function() { + delete arguments[0]; + }; + foo(); + } + expect_stdout: true +} + +issue_3420_3: { + options = { + arguments: true, + keep_fargs: false, + reduce_vars: true, + } + input: { + "use strict"; + var foo = function() { + delete arguments[0]; + }; + foo(); + } + expect: { + "use strict"; + var foo = function() { + delete arguments[0]; + }; + foo(); + } + expect_stdout: true +} + +issue_3420_4: { + options = { + arguments: true, + keep_fargs: false, + reduce_vars: true, + } + input: { + !function() { + console.log(arguments[0]); + delete arguments[0]; + console.log(arguments[0]); + }(42); + } + expect: { + !function(argument_0) { + console.log(argument_0); + delete arguments[0]; + console.log(arguments[0]); + }(42); + } + expect_stdout: [ + "42", + "undefined", + ] +} + +issue_3420_5: { + options = { + arguments: true, + keep_fargs: false, + reduce_vars: true, + } + input: { + "use strict"; + !function() { + console.log(arguments[0]); + delete arguments[0]; + console.log(arguments[0]); + }(42); + } + expect: { + "use strict"; + !function(argument_0) { + console.log(argument_0); + delete arguments[0]; + console.log(arguments[0]); + }(42); + } + expect_stdout: [ + "42", + "undefined", + ] +} + +issue_3420_6: { + options = { + arguments: true, + keep_fargs: false, + reduce_vars: true, + } + input: { + console.log(function() { + return delete arguments[0]; + }()); + } + expect: { + console.log(function() { + return delete arguments[0]; + }()); + } + expect_stdout: "true" +} + +issue_3420_7: { + options = { + arguments: true, + keep_fargs: false, + reduce_vars: true, + } + input: { + "use strict"; + console.log(function() { + return delete arguments[0]; + }()); + } + expect: { + "use strict"; + console.log(function() { + return delete arguments[0]; + }()); + } + expect_stdout: "true" +} + +issue_4200: { + options = { + arguments: true, + keep_fargs: false, + } + input: { + var o = { + get p() { + return arguments[0]; + }, + }; + console.log(o.p); + } + expect: { + var o = { + get p() { + return arguments[0]; + }, + }; + console.log(o.p); + } + expect_stdout: "undefined" +} + +issue_4291_1: { + options = { + arguments: true, + keep_fargs: false, + reduce_vars: true, + } + input: { + console.log(function() { + arguments[0] = "PASS"; + return arguments; + }()[0]); + } + expect: { + console.log(function() { + arguments[0] = "PASS"; + return arguments; + }()[0]); + } + expect_stdout: "PASS" +} + +issue_4291_2: { + options = { + arguments: true, + keep_fargs: false, + reduce_vars: true, + } + input: { + var a = function() { + if (arguments[0]) + arguments[1] = "PASS"; + return arguments; + }(42); + console.log(a[1], a[0], a.length); + } + expect: { + var a = function() { + if (arguments[0]) + arguments[1] = "PASS"; + return arguments; + }(42); + console.log(a[1], a[0], a.length); + } + expect_stdout: "PASS 42 1" +} + +issue_4397: { + options = { + arguments: true, + keep_fargs: false, + reduce_vars: true, + } + input: { + console.log(typeof function() { + arguments += 0; + return arguments[0]; + }()); + } + expect: { + console.log(typeof function() { + arguments += 0; + return arguments[0]; + }()); + } + expect_stdout: "string" +} + +issue_4410_1: { + options = { + arguments: true, + conditionals: true, + evaluate: true, + reduce_vars: true, + } + input: { + (function(a) { + console.log(arguments[0] === (a = 0) ? "FAIL" : "PASS"); + })(1); + } + expect: { + (function(a) { + console.log(a === (a = 0) ? "FAIL" : "PASS"); + })(1); + } + expect_stdout: "PASS" +} + +issue_4410_2: { + options = { + arguments: true, + conditionals: true, + evaluate: true, + reduce_vars: true, + } + input: { + (function f(a) { + console.log(arguments[0] === (a = 0) ? "FAIL" : "PASS"); + })(1); + } + expect: { + (function f(a) { + console.log(arguments[0] === (a = 0) ? "FAIL" : "PASS"); + })(1); + } + expect_stdout: "PASS" +} + +issue_4410_3: { + options = { + arguments: true, + } + input: { + var a = 1; + (function f(b) { + a-- && f(); + for (var c = 2; c--;) + switch (arguments[0]) { + case b = 42: + case 42: + console.log("PASS"); + } + })(null); + } + expect: { + var a = 1; + (function f(b) { + a-- && f(); + for (var c = 2; c--;) + switch (arguments[0]) { + case b = 42: + case 42: + console.log("PASS"); + } + })(null); + } + expect_stdout: "PASS" +} + +issue_4432: { + options = { + arguments: true, + reduce_vars: true, + } + input: { + console.log(function(a) { + for (a in { FAIL: 42 }); + return arguments[0]; + }() || "PASS"); + } + expect: { + console.log(function(a) { + for (a in { FAIL: 42 }); + return arguments[0]; + }() || "PASS"); + } + expect_stdout: "PASS" +} + +issue_4696: { + options = { + arguments: true, + keep_fargs: false, + } + input: { + console.log(function() { + for (arguments in [ 42 ]); + for (var a in arguments[0]) + return "PASS"; + }()); + } + expect: { + console.log(function() { + for (arguments in [ 42 ]); + for (var a in arguments[0]) + return "PASS"; + }()); + } + expect_stdout: "PASS" +} + +issue_4809: { + options = { + arguments: true, + keep_fargs: false, + reduce_vars: true, + } + input: { + A = 0; + (function() { + arguments[A] = "PASS"; + console.log(arguments[0]); + })(); + } + expect: { + A = 0; + (function() { + arguments[A] = "PASS"; + console.log(arguments[0]); + })(); + } + expect_stdout: "PASS" +} diff --git a/test/compress/arrays.js b/test/compress/arrays.js index e636347f457..4767a9a53f4 100644 --- a/test/compress/arrays.js +++ b/test/compress/arrays.js @@ -13,18 +13,29 @@ holes_and_undefined: { } } -constant_join: { +constant_join_1: { options = { - unsafe : true, - evaluate : true - }; + evaluate: true, + side_effects: true, + strings: true, + unsafe: true, + } input: { var a = [ "foo", "bar", "baz" ].join(""); var a1 = [ "foo", "bar", "baz" ].join(); + var a2 = [ "foo", "bar", "baz" ].join(null); + var a3 = [ "foo", "bar", "baz" ].join(void 0); + var a4 = [ "foo", , "baz" ].join(); + var a5 = [ "foo", null, "baz" ].join(); + var a6 = [ "foo", void 0, "baz" ].join(); var b = [ "foo", 1, 2, 3, "bar" ].join(""); var c = [ boo(), "foo", 1, 2, 3, "bar", bar() ].join(""); var c1 = [ boo(), bar(), "foo", 1, 2, 3, "bar", bar() ].join(""); var c2 = [ 1, 2, "foo", "bar", baz() ].join(""); + var c3 = [ boo() + bar() + "foo", 1, 2, 3, "bar", bar() + "foo" ].join(""); + var c4 = [ 1, 2, null, undefined, "foo", "bar", baz() ].join(""); + var c5 = [ boo() + bar() + "foo", 1, 2, 3, "bar", bar() + "foo" ].join(); + var c6 = [ 1, 2, null, undefined, "foo", "bar", baz() ].join(); var d = [ "foo", 1 + 2 + "bar", "baz" ].join("-"); var e = [].join(foo + bar); var f = [].join(""); @@ -33,12 +44,21 @@ constant_join: { expect: { var a = "foobarbaz"; var a1 = "foo,bar,baz"; + var a2 = "foonullbarnullbaz"; + var a3 = "foo,bar,baz"; + var a4 = "foo,,baz"; + var a5 = "foo,,baz"; + var a6 = "foo,,baz"; var b = "foo123bar"; var c = boo() + "foo123bar" + bar(); var c1 = "" + boo() + bar() + "foo123bar" + bar(); var c2 = "12foobar" + baz(); + var c3 = boo() + bar() + "foo123bar" + bar() + "foo"; + var c4 = "12foobar" + baz(); + var c5 = [ boo() + bar() + "foo", 1, 2, 3, "bar", bar() + "foo" ].join(); + var c6 = [ "1,2,,,foo,bar", baz() ].join(); var d = "foo-3bar-baz"; - var e = [].join(foo + bar); + var e = (foo, bar, ""); var f = ""; var g = ""; } @@ -46,9 +66,10 @@ constant_join: { constant_join_2: { options = { - unsafe : true, - evaluate : true - }; + evaluate: true, + strings: true, + unsafe: true, + } input: { var a = [ "foo", "bar", boo(), "baz", "x", "y" ].join(""); var b = [ "foo", "bar", boo(), "baz", "x", "y" ].join("-"); @@ -72,3 +93,334 @@ constant_join_2: { var f = "strstr" + variable + "foobarmoo" + foo; } } + +constant_join_3: { + options = { + evaluate: true, + strings: true, + unsafe: true, + } + input: { + var foo, bar, baz; + var a = [ null ].join(); + var b = [ , ].join(); + var c = [ , 1, , 3 ].join(); + var d = [ foo ].join(); + var e = [ foo, null, undefined, bar ].join("-"); + var f = [ foo, bar ].join(""); + var g = [ null, "foo", null, bar + "baz" ].join(""); + var h = [ null, "foo", null, bar + "baz" ].join("-"); + var i = [ "foo" + bar, null, baz + "moo" ].join(""); + var j = [ foo + "bar", baz ].join(""); + var k = [ foo, "bar" + baz ].join(""); + var l = [ foo, bar + "baz" ].join(""); + } + expect: { + var foo, bar, baz; + var a = ""; + var b = ""; + var c = ",1,,3"; + var d = "" + foo; + var e = [ foo, "-", bar ].join("-"); + var f = "" + foo + bar; + var g = "foo" + bar + "baz"; + var h = [ "-foo-", bar + "baz" ].join("-"); + var i = "foo" + bar + baz + "moo"; + var j = foo + "bar" + baz; + var k = foo + "bar" + baz; + var l = foo + (bar + "baz"); + } +} + +for_loop: { + options = { + evaluate: true, + reduce_funcs: true, + reduce_vars: true, + unsafe: true, + unused: true, + } + input: { + function f0() { + var a = [1, 2, 3]; + var b = 0; + for (var i = 0; i < a.length; i++) + b += a[i]; + return b; + } + function f1() { + var a = [1, 2, 3]; + var b = 0; + for (var i = 0, len = a.length; i < len; i++) + b += a[i]; + return b; + } + function f2() { + var a = [1, 2, 3]; + for (var i = 0; i < a.length; i++) + a[i]++; + return a[2]; + } + console.log(f0(), f1(), f2()); + } + expect: { + function f0() { + var a = [1, 2, 3]; + var b = 0; + for (var i = 0; i < 3; i++) + b += a[i]; + return b; + } + function f1() { + var a = [1, 2, 3]; + var b = 0; + for (var i = 0; i < 3; i++) + b += a[i]; + return b; + } + function f2() { + var a = [1, 2, 3]; + for (var i = 0; i < a.length; i++) + a[i]++; + return a[2]; + } + console.log(f0(), f1(), f2()); + } + expect_stdout: "6 6 4" +} + +index: { + options = { + evaluate: true, + reduce_funcs: true, + reduce_vars: true, + toplevel: true, + unsafe: true, + unused: true, + } + input: { + var a = [ 1, 2 ]; + console.log(a[0], a[1]); + } + expect: { + console.log(1, 2); + } + expect_stdout: "1 2" +} + +length: { + options = { + evaluate: true, + reduce_funcs: true, + reduce_vars: true, + toplevel: true, + unsafe: true, + unused: true, + } + input: { + var a = [ 1, 2 ]; + console.log(a.length); + } + expect: { + console.log(2); + } + expect_stdout: "2" +} + +index_length: { + options = { + evaluate: true, + reduce_funcs: true, + reduce_vars: true, + toplevel: true, + unsafe: true, + unused: true, + } + input: { + var a = [ 1, 2 ]; + console.log(a[0], a.length); + } + expect: { + console.log(1, 2); + } + expect_stdout: "1 2" +} + +constructor_bad: { + options = { + unsafe: true + } + input: { + try { + Array(NaN); + console.log("FAIL1"); + } catch (ex) { + try { + new Array(NaN); + console.log("FAIL2"); + } catch (ex) { + console.log("PASS"); + } + } + try { + Array(3.14); + console.log("FAIL1"); + } catch (ex) { + try { + new Array(3.14); + console.log("FAIL2"); + } catch (ex) { + console.log("PASS"); + } + } + } + expect: { + try { + Array(NaN); + console.log("FAIL1"); + } catch (ex) { + try { + Array(NaN); + console.log("FAIL2"); + } catch (ex) { + console.log("PASS"); + } + } + try { + Array(3.14); + console.log("FAIL1"); + } catch (ex) { + try { + Array(3.14); + console.log("FAIL2"); + } catch (ex) { + console.log("PASS"); + } + } + } + expect_stdout: [ + "PASS", + "PASS", + ] + expect_warnings: [ + "WARN: Invalid array length: 3.14 [test/compress/arrays.js:13,12]", + "WARN: Invalid array length: 3.14 [test/compress/arrays.js:17,16]", + ] +} + +constructor_good: { + options = { + unsafe: true + } + input: { + console.log(Array()); + console.log(Array(0)); + console.log(Array(1)); + console.log(Array(6)); + console.log(Array(7)); + console.log(Array(1, 2)); + console.log(Array(false)); + console.log(Array("foo")); + console.log(Array(Array)); + console.log(new Array()); + console.log(new Array(0)); + console.log(new Array(1)); + console.log(new Array(6)); + console.log(new Array(7)); + console.log(new Array(1, 2)); + console.log(new Array(false)); + console.log(new Array("foo")); + console.log(new Array(Array)); + } + expect: { + console.log([]); + console.log([]); + console.log([,]); + console.log([,,,,,,]); + console.log(Array(7)); + console.log([ 1, 2 ]); + console.log([ false ]); + console.log([ "foo" ]); + console.log(Array(Array)); + console.log([]); + console.log([]); + console.log([,]); + console.log([,,,,,,]); + console.log(Array(7)); + console.log([ 1, 2 ]); + console.log([ false ]); + console.log([ "foo" ]); + console.log(Array(Array)); + } + expect_stdout: true + expect_warnings: [] +} + +unsafe_evaluate_modified_binary: { + options = { + evaluate: true, + reduce_vars: true, + unsafe: true, + } + input: { + (function(a) { + (console && a).push(1); + if (a.length) + console.log("PASS"); + })([]); + } + expect: { + (function(a) { + (console && a).push(1); + if (a.length) + console.log("PASS"); + })([]); + } + expect_stdout: "PASS" +} + +unsafe_evaluate_modified_conditional: { + options = { + evaluate: true, + reduce_vars: true, + unsafe: true, + } + input: { + (function(a) { + (console ? a : []).push(1); + if (a.length) + console.log("PASS"); + })([]); + } + expect: { + (function(a) { + (console ? a : []).push(1); + if (a.length) + console.log("PASS"); + })([]); + } + expect_stdout: "PASS" +} + +unsafe_evaluate_modified_sequence: { + options = { + evaluate: true, + reduce_vars: true, + unsafe: true, + } + input: { + (function(a) { + (0, a).push(1); + if (a.length) + console.log("PASS"); + })([]); + } + expect: { + (function(a) { + (0, a).push(1); + if (a.length) + console.log("PASS"); + })([]); + } + expect_stdout: "PASS" +} diff --git a/test/compress/arrows.js b/test/compress/arrows.js new file mode 100644 index 00000000000..86ac8d494ca --- /dev/null +++ b/test/compress/arrows.js @@ -0,0 +1,1320 @@ +no_funarg: { + input: { + (() => console.log(42))(); + } + expect_exact: "(()=>console.log(42))();" + expect_stdout: "42" + node_version: ">=4" +} + +single_funarg: { + input: { + (a => console.log(a))(42); + } + expect_exact: "(a=>console.log(a))(42);" + expect_stdout: "42" + node_version: ">=4" +} + +multiple_funargs: { + input: { + ((a, b) => console.log(a, b))("foo", "bar"); + } + expect_exact: '((a,b)=>console.log(a,b))("foo","bar");' + expect_stdout: "foo bar" + node_version: ">=4" +} + +destructured_funarg: { + input: { + (([ a, b, c ]) => console.log(a, b, c))("foo"); + } + expect_exact: '(([a,b,c])=>console.log(a,b,c))("foo");' + expect_stdout: "f o o" + node_version: ">=6" +} + +await_parentheses: { + input: { + async function f() { + await (a => a); + } + } + expect_exact: "async function f(){await(a=>a)}" +} + +for_parentheses_init: { + input: { + for (a => (a in a); console.log(42);); + } + expect_exact: "for((a=>a in a);console.log(42););" + expect_stdout: "42" + node_version: ">=4" +} + +for_parentheses_condition: { + input: { + for (console.log(42); a => (a in a);) + break; + } + expect_exact: "for(console.log(42);a=>a in a;)break;" + expect_stdout: "42" + node_version: ">=4" +} + +for_parentheses_step: { + input: { + for (; console.log(42); a => (a in a)); + } + expect_exact: "for(;console.log(42);a=>a in a);" + expect_stdout: "42" + node_version: ">=4" +} + +for_assign_parentheses_init: { + input: { + for (f = a => (a in a); console.log(42);); + } + expect_exact: "for((f=a=>a in a);console.log(42););" + expect_stdout: "42" + node_version: ">=4" +} + +for_assign_parentheses_condition: { + input: { + for (console.log(42); f = a => (a in a);) + break; + } + expect_exact: "for(console.log(42);f=a=>a in a;)break;" + expect_stdout: "42" + node_version: ">=4" +} + +for_assign_parentheses_step: { + input: { + for (; console.log(42); f = a => (a in a)); + } + expect_exact: "for(;console.log(42);f=a=>a in a);" + expect_stdout: "42" + node_version: ">=4" +} + +for_declaration_parentheses_init: { + input: { + for (var f = a => (a in a); console.log(42);); + } + expect_exact: "for(var f=(a=>a in a);console.log(42););" + expect_stdout: "42" + node_version: ">=4" +} + +for_statement_parentheses_init: { + input: { + for (a => { + a in a; + }; console.log(42);); + } + expect_exact: "for(a=>{a in a};console.log(42););" + expect_stdout: "42" + node_version: ">=4" +} + +body_call: { + input: { + (() => { + console.log("foo"); + console.log("bar"); + })(); + } + expect_exact: '(()=>{console.log("foo");console.log("bar")})();' + expect_stdout: [ + "foo", + "bar", + ] + node_version: ">=4" +} + +body_conditional: { + input: { + console.log((a => {}) ? "PASS" : "FAIL"); + } + expect_exact: 'console.log((a=>{})?"PASS":"FAIL");' + expect_stdout: "PASS" + node_version: ">=4" +} + +destructured_object_value: { + input: { + console.log((a => ({} = a))(42)); + } + expect_exact: "console.log((a=>({}=a))(42));" + expect_stdout: "42" + node_version: ">=6" +} + +function_value: { + input: { + console.log((a => function() { + return a; + })(42)()); + } + expect_exact: "console.log((a=>function(){return a})(42)());" + expect_stdout: "42" + node_version: ">=4" +} + +in_value: { + input: { + console.log((a => a in { + foo: 42, + })("foo")); + } + expect_exact: 'console.log((a=>a in{foo:42})("foo"));' + expect_stdout: "true" + node_version: ">=4" +} + +object_value: { + input: { + console.log((() => ({ + 4: 2, + }))()[4]); + } + expect_exact: "console.log((()=>({4:2}))()[4]);" + expect_stdout: "2" + node_version: ">=4" +} + +object_first_in_value: { + input: { + console.log((a => ({ + p: a, + }.p ? "FAIL" : "PASS"))()); + } + expect_exact: 'console.log((a=>({p:a}).p?"FAIL":"PASS")());' + expect_stdout: "PASS" + node_version: ">=4" +} + +sequence_value: { + input: { + console.log((a => (console.log("foo"), a))("bar")); + } + expect_exact: 'console.log((a=>(console.log("foo"),a))("bar"));' + expect_stdout: [ + "foo", + "bar", + ] + node_version: ">=4" +} + +side_effects_value: { + options = { + side_effects: true, + } + input: { + console.log((a => function() { + return a; + })(42)()); + } + expect: { + console.log((a => function() { + return a; + })(42)()); + } + expect_stdout: "42" + node_version: ">=4" +} + +arrow_property: { + input: { + console.log((a => 42).prototype); + } + expect_exact: "console.log((a=>42).prototype);" + expect_stdout: "undefined" + node_version: ">=4" +} + +assign_arrow: { + input: { + var f = a => a; + console.log(f(42)); + } + expect_exact: "var f=a=>a;console.log(f(42));" + expect_stdout: "42" + node_version: ">=4" +} + +binary_arrow: { + input: { + console.log(4 || (() => 2)); + } + expect_exact: "console.log(4||(()=>2));" + expect_stdout: "4" + node_version: ">=4" +} + +unary_arrow: { + input: { + console.log(+(() => 42)); + } + expect_exact: "console.log(+(()=>42));" + expect_stdout: "NaN" + node_version: ">=4" +} + +trailing_comma: { + input: { + ((a,) => console.log(a))(42); + } + expect_exact: "(a=>console.log(a))(42);" + expect_stdout: "42" + node_version: ">=4" +} + +drop_arguments: { + options = { + arguments: true, + keep_fargs: false, + reduce_vars: true, + } + input: { + console.log(function() { + return () => arguments[0]; + }("PASS")("FAIL")); + } + expect: { + console.log(function(argument_0) { + return () => argument_0; + }("PASS")("FAIL")); + } + expect_stdout: "PASS" + node_version: ">=4" +} + +funarg_arguments: { + options = { + inline: true, + } + input: { + console.log((arguments => arguments)(42)); + } + expect: { + console.log(42); + } + expect_stdout: "42" + node_version: ">=4" +} + +inline_arguments: { + options = { + inline: true, + } + input: { + console.log(function() { + return () => arguments[0]; + }("PASS")("FAIL")); + } + expect: { + console.log(function() { + return () => arguments[0]; + }("PASS")("FAIL")); + } + expect_stdout: "PASS" + node_version: ">=4" +} + +var_arguments: { + options = { + inline: true, + properties: true, + reduce_vars: true, + side_effects: true, + unused: true, + } + input: { + console.log(function() { + return () => { + var arguments = [ "PASS" ]; + return arguments; + }; + }("FAIL 1")("FAIL 2")[0]); + } + expect: { + console.log("PASS"); + } + expect_stdout: "PASS" + node_version: ">=4" +} + +negate: { + options = { + conditionals: true, + } + input: { + if (!console ? 0 : () => 1) + console.log("PASS"); + } + expect: { + (console ? () => 1 : 0) && console.log("PASS"); + } + expect_stdout: "PASS" + node_version: ">=4" +} + +inline_this: { + options = { + inline: true, + } + input: { + var p = "PASS"; + console.log({ + p: "FAIL", + q: (() => this.p)(), + }.q); + } + expect: { + var p = "PASS"; + console.log({ + p: "FAIL", + q: this.p, + }.q); + } + expect_stdout: "PASS" + node_version: ">=4" +} + +dont_inline_this: { + options = { + inline: true, + } + input: { + var o = { + p: function() { + return function() { + return () => this.q; + }(); + }, + q: "FAIL", + }; + q = "PASS"; + console.log(o.p()()); + } + expect: { + var o = { + p: function() { + return function() { + return () => this.q; + }(); + }, + q: "FAIL", + }; + q = "PASS"; + console.log(o.p()()); + } + expect_stdout: "PASS" + node_version: ">=4" +} + +trim_body: { + options = { + arrows: true, + if_return: true, + side_effects: true, + } + input: { + var f = a => { + return a; + }; + var g = b => void b; + console.log(f("PASS"), g("FAIL")); + } + expect: { + var f = a => a; + var g = b => {}; + console.log(f("PASS"), g("FAIL")); + } + expect_stdout: "PASS undefined" + node_version: ">=4" +} + +collapse_value: { + options = { + arrows: true, + collapse_vars: true, + keep_fargs: false, + unsafe: true, + unused: true, + } + input: { + var a = 42; + console.log((b => Math.floor(b))(a)); + } + expect: { + var a = 42; + console.log((() => Math.floor(a))()); + } + expect_stdout: "42" + node_version: ">=4" +} + +collapse_property_lambda: { + options = { + collapse_vars: true, + pure_getters: "strict", + } + input: { + console.log(function f() { + f.g = () => 42; + return f.g(); + }()); + } + expect: { + console.log(function f() { + return (f.g = () => 42)(); + }()); + } + expect_stdout: "42" + node_version: ">=4" +} + +drop_return: { + options = { + arrows: true, + side_effects: true, + } + input: { + (a => { + while (!console); + return console.log(a); + })(42); + } + expect: { + (a => { + while (!console); + console.log(a); + })(42); + } + expect_stdout: "42" + node_version: ">=4" +} + +drop_value: { + options = { + arrows: true, + side_effects: true, + } + input: { + ((a, b) => a + b)(console.log(42)); + } + expect: { + void console.log(42); + } + expect_stdout: "42" + node_version: ">=4" +} + +reduce_iife_1: { + options = { + evaluate: true, + keep_fargs: false, + reduce_vars: true, + unused: true, + } + input: { + (a => console.log(a + a))(21); + } + expect: { + (() => console.log(42))(); + } + expect_stdout: "42" + node_version: ">=4" +} + +reduce_iife_2: { + options = { + evaluate: true, + reduce_vars: true, + toplevel: true, + unused: true, + } + input: { + var a = 21; + (() => console.log(a + a))(); + } + expect: { + (() => console.log(42))(); + } + expect_stdout: "42" + node_version: ">=4" +} + +reduce_iife_3: { + options = { + evaluate: true, + reduce_vars: true, + side_effects: true, + toplevel: true, + unused: true, + } + input: { + var a = "foo"; + (() => { + console.log(a); + console.log(a); + })(); + a = "bar"; + } + expect: { + (() => { + console.log("foo"); + console.log("foo"); + })(); + } + expect_stdout: [ + "foo", + "foo", + ] + node_version: ">=4" +} + +reduce_lambda_1: { + options = { + evaluate: true, + reduce_vars: true, + toplevel: true, + unused: true, + } + input: { + var f = () => { + console.log(a, b); + }; + var a = "foo", b = 42; + f(); + b = "bar"; + f(); + } + expect: { + var f = () => { + console.log("foo", b); + }; + var b = 42; + f(); + b = "bar"; + f(); + } + expect_stdout: [ + "foo 42", + "foo bar", + ] + node_version: ">=4" +} + +reduce_lambda_2: { + options = { + evaluate: true, + passes: 2, + reduce_vars: true, + side_effects: true, + unused: true, + } + input: { + (function(f, a, b) { + f = () => { + console.log(a, b); + }; + a = "foo", b = 42; + f(); + b = "bar"; + f(); + })(); + } + expect: { + (function(f, a, b) { + f = () => { + console.log("foo", b); + }; + b = 42; + f(); + b = "bar"; + f(); + })(); + } + expect_stdout: [ + "foo 42", + "foo bar", + ] + node_version: ">=4" +} + +single_use_recursive: { + options = { + reduce_vars: true, + toplevel: true, + unused: true, + } + input: { + function f() { + return (() => f)(); + } + console.log(typeof f()); + } + expect: { + console.log(typeof function f() { + return (() => f)(); + }()); + } + expect_stdout: "function" + node_version: ">=4" +} + +inline_iife_within_arrow: { + options = { + arrows: true, + inline: true, + } + input: { + var f = () => console.log(function(a) { + return Math.ceil(a); + }(Math.random())); + f(); + } + expect: { + var f = () => { + return console.log((a = Math.random(), Math.ceil(a))); + var a; + }; + f(); + } + expect_stdout: "1" + node_version: ">=4" +} + +instanceof_lambda_1: { + options = { + evaluate: true, + side_effects: true, + } + input: { + console.log(42 instanceof (() => {})); + } + expect: { + console.log(false); + } + expect_stdout: "false" + node_version: ">=4" +} + +instanceof_lambda_2: { + options = { + evaluate: true, + side_effects: false, + } + input: { + console.log(null instanceof (() => {})); + } + expect: { + console.log((null, () => {}, false)); + } + expect_stdout: "false" + node_version: ">=4" +} + +instanceof_lambda_3: { + options = { + evaluate: true, + side_effects: true, + } + input: { + console.log({} instanceof (() => {})); + } + expect: { + console.log({} instanceof (() => {})); + } + expect_stdout: TypeError("Function has non-object prototype 'undefined' in instanceof check") + node_version: ">=4" +} + +instanceof_lambda_4: { + options = { + side_effects: true, + } + input: { + ({ p: "foo" }) instanceof (() => {}); + } + expect: { + [] instanceof (() => {}); + } + expect_stdout: TypeError("Function has non-object prototype 'undefined' in instanceof check") + node_version: ">=4" +} + +issue_4388: { + options = { + inline: true, + toplevel: true, + } + input: { + (arguments => console.log(arguments && arguments))(); + } + expect: { + (arguments => console.log(arguments && arguments))(); + } + expect_stdout: "undefined" + node_version: ">=4" +} + +issue_4390: { + options = { + collapse_vars: true, + } + input: { + function log() { + console.log.apply(console, arguments); + } + var a = 42, b = "FAIL"; + b = "PASS"; + (c => log(b, c))(a); + log(b); + } + expect: { + function log() { + console.log.apply(console, arguments); + } + var a = 42, b = "FAIL"; + b = "PASS"; + (c => log(b, c))(a); + log(b); + } + expect_stdout: [ + "PASS 42", + "PASS", + ] + node_version: ">=4" +} + +issue_4401: { + options = { + merge_vars: true, + } + input: { + (function() { + var a = (b => b(a))(console.log || a); + var c = console.log; + c && c(typeof b); + })(); + } + expect: { + (function() { + var a = (b => b(a))(console.log || a); + var c = console.log; + c && c(typeof b); + })(); + } + expect_stdout: [ + "undefined", + "undefined", + ] + node_version: ">=4" +} + +issue_4448: { + options = { + pure_getters: "strict", + side_effects: true, + } + input: { + var A; + try { + (arguments => { + arguments[0]; + })(A); + } catch (e) { + console.log("PASS"); + } + } + expect: { + var A; + try { + (arguments => { + arguments[0]; + })(A); + } catch (e) { + console.log("PASS"); + } + } + expect_stdout: "PASS" + node_version: ">=4" +} + +issue_4476: { + options = { + arguments: true, + } + input: { + (function(a, b) { + (a => { + console.log(arguments[0], a); + })(b); + })("foo", "bar"); + } + expect: { + (function(a, b) { + (a => { + console.log(arguments[0], a); + })(b); + })("foo", "bar"); + } + expect_stdout: "foo bar" + node_version: ">=4" +} + +issue_4666: { + input: { + console.log((a => /[0-9]/.test(a))(42)); + } + expect_exact: "console.log((a=>/[0-9]/.test(a))(42));" + expect_stdout: "true" + node_version: ">=4" +} + +issue_4685_1: { + options = { + collapse_vars: true, + unused: true, + } + input: { + new function(f) { + if (f() !== this) + console.log("PASS"); + }(() => this); + } + expect: { + new function(f) { + if (f() !== this) + console.log("PASS"); + }(() => this); + } + expect_stdout: "PASS" + node_version: ">=4" +} + +issue_4685_2: { + options = { + reduce_vars: true, + unused: true, + } + input: { + new function(f) { + if (f() !== this) + console.log("PASS"); + }(() => { + if (console) + return this; + }); + } + expect: { + new function(f) { + if (f() !== this) + console.log("PASS"); + }(() => { + if (console) + return this; + }); + } + expect_stdout: "PASS" + node_version: ">=4" +} + +issue_4687_1: { + options = { + collapse_vars: true, + unused: true, + } + input: { + new function() { + console.log(function(f) { + return f() === this; + }(() => this) || "PASS"); + } + } + expect: { + new function() { + console.log(function(f) { + return f() === this; + }(() => this) || "PASS"); + } + } + expect_stdout: "PASS" + node_version: ">=4" +} + +issue_4687_2: { + options = { + reduce_vars: true, + unused: true, + } + input: { + new function() { + console.log(function(f) { + return f() === this; + }(() => { + if (console) + return this; + }) || "PASS"); + } + } + expect: { + new function() { + console.log(function(f) { + return f() === this; + }(() => { + if (console) + return this; + }) || "PASS"); + } + } + expect_stdout: "PASS" + node_version: ">=4" +} + +issue_4772: { + input: { + var f = a => (a) + /**/ console.log(f("PASS")); + } + expect_exact: 'var f=a=>a;console.log(f("PASS"));' + expect_stdout: "PASS" + node_version: ">=4" +} + +issue_5251: { + options = { + inline: true, + toplevel: true, + } + input: { + (() => { + while (console.log(arguments)) + var arguments = "FAIL"; + })(); + } + expect: { + (() => { + while (console.log(arguments)) + var arguments = "FAIL"; + })(); + } + expect_stdout: true + node_version: ">=4" +} + +issue_5342_1: { + options = { + dead_code: true, + inline: true, + toplevel: true, + unused: true, + } + input: { + for (var a in 0) { + (() => { + while (1); + })(new function(NaN) { + a.p; + }()); + } + console.log(function() { + return b; + try { + b; + } catch (e) { + var b; + } + }()); + } + expect: { + for (var a in 0) { + (function(NaN) { + a.p; + })(); + while (1); + } + console.log(b); + var b; + } + expect_stdout: "undefined" + node_version: ">=4" +} + +issue_5342_2: { + rename = true + options = { + dead_code: true, + inline: true, + toplevel: true, + unused: true, + } + input: { + for (var a in 0) { + (() => { + while (1); + })(new function(NaN) { + a.p; + }()); + } + console.log(function() { + return b; + try { + b; + } catch (e) { + var b; + } + }()); + } + expect: { + for (var a in 0) { + a.p; + while (1); + } + console.log(c); + var c; + } + expect_stdout: "undefined" + node_version: ">=4" +} + +issue_5356: { + options = { + evaluate: true, + reduce_vars: true, + unused: true, + } + input: { + console.log((a => a++)(console)); + } + expect: { + console.log((a => +a)(console)); + } + expect_stdout: "NaN" + node_version: ">=4" +} + +issue_5414_1: { + options = { + arrows: true, + if_return: true, + inline: true, + toplevel: true, + } + input: { + (() => { + (() => { + if (!console) + var arguments = 42; + while (console.log(arguments)); + })(); + })(); + } + expect: { + (() => { + if (!console) + var arguments = 42; + while (console.log(arguments)); + })(); + } + expect_stdout: true + node_version: ">=4" +} + +issue_5414_2: { + options = { + arrows: true, + inline: true, + side_effects: true, + toplevel: true, + } + input: { + (() => { + (() => { + if (!console) + var arguments = 42; + while (console.log(arguments)); + })(); + })(); + } + expect: { + (() => { + if (!console) + var arguments = 42; + while (console.log(arguments)); + })(); + } + expect_stdout: true + node_version: ">=4" +} + +issue_5416_1: { + options = { + dead_code: true, + evaluate: true, + inline: true, + loops: true, + unused: true, + } + input: { + var f = () => { + while ((() => { + console; + var a = function g(arguments) { + console.log(arguments); + }(); + })()); + }; + f(); + } + expect: { + var f = () => { + { + console; + arguments = void 0, + console.log(arguments); + var arguments; + return; + } + }; + f(); + } + expect_stdout: "undefined" + node_version: ">=4" +} + +issue_5416_2: { + options = { + dead_code: true, + evaluate: true, + inline: true, + loops: true, + unused: true, + } + input: { + var f = () => { + while ((() => { + console; + var a = function g(arguments) { + while (console.log(arguments)); + }(); + })()); + }; + f(); + } + expect: { + var f = () => { + { + console; + var arguments = void 0; + for (; console.log(arguments);); + return; + } + }; + f(); + } + expect_stdout: "undefined" + node_version: ">=4" +} + +issue_5416_3: { + options = { + inline: true, + side_effects: true, + unused: true, + } + input: { + var f = () => { + (() => { + var a = function g(arguments) { + console.log(arguments); + }(); + })(); + }; + f(); + } + expect: { + var f = () => { + arguments = void 0, + console.log(arguments); + var arguments; + }; + f(); + } + expect_stdout: "undefined" + node_version: ">=4" +} + +issue_5416_4: { + options = { + arrows: true, + inline: true, + side_effects: true, + unused: true, + } + input: { + var f = () => { + (() => { + var a = function g(arguments) { + while (console.log(arguments)); + }(); + })(); + }; + f(); + } + expect: { + var f = () => { + var arguments = void 0; + while (console.log(arguments)); + return; + }; + f(); + } + expect_stdout: "undefined" + node_version: ">=4" +} + +issue_5495: { + input: { + console.log((() => { + "use strict"; + return function() { + return this; + }(); + })()); + } + expect_exact: 'console.log((()=>{"use strict";return function(){return this}()})());' + expect_stdout: "undefined" + node_version: ">=4" +} + +issue_5653: { + options = { + arrows: true, + hoist_props: true, + passes: 2, + reduce_vars: true, + sequences: true, + side_effects: true, + unused: true, + } + input: { + console.log((a => { + a = { p: console }; + return a++; + })()); + } + expect: { + console.log((a => { + return console, +{}; + })()); + } + expect_stdout: "NaN" + node_version: ">=4" +} diff --git a/test/compress/asm.js b/test/compress/asm.js new file mode 100644 index 00000000000..331eb426e4c --- /dev/null +++ b/test/compress/asm.js @@ -0,0 +1,233 @@ +asm_mixed: { + options = { + assignments: true, + booleans: true, + comparisons: true, + conditionals: true, + dead_code: true, + drop_debugger: true, + evaluate: true, + hoist_funs: true, + hoist_vars: true, + if_return: true, + join_vars: true, + keep_fargs: true, + keep_fnames: false, + loops: true, + negate_iife: true, + properties: true, + sequences: true, + side_effects: true, + unused: true, + } + input: { + // adapted from http://asmjs.org/spec/latest/ + function asm_GeometricMean(stdlib, foreign, buffer) { + "use asm"; + var exp = stdlib.Math.exp; + var log = stdlib.Math.log; + var values = new stdlib.Float64Array(buffer); + function logSum(start, end) { + start = start|0; + end = end|0; + var sum = 0.0, p = 0, q = 0; + // asm.js forces byte addressing of the heap by requiring shifting by 3 + for (p = start << 3, q = end << 3; (p|0) < (q|0); p = (p + 8)|0) { + sum = sum + +log(values[p>>3]); + } + return +sum; + } + function geometricMean(start, end) { + start = start|0; + end = end|0; + return +exp(+logSum(start, end) / +((end - start)|0)); + } + return { geometricMean: geometricMean }; + } + function no_asm_GeometricMean(stdlib, foreign, buffer) { + var exp = stdlib.Math.exp; + var log = stdlib.Math.log; + var values = new stdlib.Float64Array(buffer); + function logSum(start, end) { + start = start|0; + end = end|0; + var sum = 0.0, p = 0, q = 0; + // asm.js forces byte addressing of the heap by requiring shifting by 3 + for (p = start << 3, q = end << 3; (p|0) < (q|0); p = (p + 8)|0) { + sum = sum + +log(values[p>>3]); + } + return +sum; + } + function geometricMean(start, end) { + start = start|0; + end = end|0; + return +exp(+logSum(start, end) / +((end - start)|0)); + } + return { geometricMean: geometricMean }; + } + } + expect: { + function asm_GeometricMean(stdlib, foreign, buffer) { + "use asm"; + var exp = stdlib.Math.exp; + var log = stdlib.Math.log; + var values = new stdlib.Float64Array(buffer); + function logSum(start, end) { + start = start | 0; + end = end | 0; + var sum = 0.0, p = 0, q = 0; + for (p = start << 3, q = end << 3; (p | 0) < (q | 0); p = p + 8 | 0) + sum = sum + +log(values[p >> 3]); + return +sum; + } + function geometricMean(start, end) { + start = start | 0; + end = end | 0; + return +exp(+logSum(start, end) / +(end - start | 0)); + } + return { geometricMean: geometricMean }; + } + function no_asm_GeometricMean(stdlib, foreign, buffer) { + function logSum(start, end) { + start |= 0, end |= 0; + for (var sum = 0, p = 0, q = 0, p = start << 3, q = end << 3; (0 | p) < (0 | q); p = p + 8 | 0) + sum += +log(values[p >> 3]); + return +sum; + } + function geometricMean(start, end) { + return start |= 0, end |= 0, +exp(+logSum(start, end) / (end - start | 0)); + } + var exp = stdlib.Math.exp, log = stdlib.Math.log, values = new stdlib.Float64Array(buffer); + return { geometricMean: geometricMean }; + } + } +} + +asm_toplevel: { + options = {} + input: { + "use asm"; + 0.0; + function f() { + 0.0; + (function(){ + 0.0; + }); + } + 0.0; + } + expect_exact: '"use asm";0.0;function f(){0.0;(function(){0.0})}0.0;' +} + +asm_function_expression: { + options = {} + input: { + 0.0; + var a = function() { + "use asm"; + 0.0; + } + function f() { + 0.0; + return function(){ + "use asm"; + 0.0; + } + 0.0; + } + 0.0; + } + expect_exact: '0;var a=function(){"use asm";0.0};function f(){0;return function(){"use asm";0.0};0}0;' +} + +asm_nested_functions: { + options = {} + input: { + 0.0; + function a() { + "use asm"; + 0.0; + } + 0.0; + function b() { + 0.0; + function c(){ + "use asm"; + 0.0; + } + 0.0; + function d(){ + 0.0; + } + 0.0; + } + 0.0; + } + expect_exact: '0;function a(){"use asm";0.0}0;function b(){0;function c(){"use asm";0.0}0;function d(){0}0}0;' +} + +issue_3636_1: { + mangle = {} + input: { + function n(stdlib, foreign, buffer) { + "use asm"; + function add(x, y) { + x = x | 0; + y = y | 0; + return x + y | 0; + } + return { + add: add + }; + } + console.log(new n().add("foo", 42)); + } + expect: { + function n(o, e, u) { + "use asm"; + function d(n, o) { + n = n | 0; + o = o | 0; + return n + o | 0; + } + return { + add: d + }; + } + console.log(new n().add("foo", 42)); + } + expect_stdout: "42" +} + +issue_3636_2: { + mangle = {} + input: { + var n = function(stdlib, foreign, buffer) { + "use asm"; + function add(x, y) { + x = x | 0; + y = y | 0; + return x + y | 0; + } + return { + add: add + }; + }; + console.log(new n().add("foo", 42)); + } + expect: { + var n = function(n, o, e) { + "use asm"; + function r(n, o) { + n = n | 0; + o = o | 0; + return n + o | 0; + } + return { + add: r + }; + }; + console.log(new n().add("foo", 42)); + } + expect_stdout: "42" +} diff --git a/test/compress/assignments.js b/test/compress/assignments.js new file mode 100644 index 00000000000..beae0230734 --- /dev/null +++ b/test/compress/assignments.js @@ -0,0 +1,864 @@ +op_equals_left_local_var: { + options = { + assignments: true, + evaluate: true, + } + input: { + var x; + + x = x + 3; + x = x - 3; + x = x / 3; + x = x * 3; + x = x >> 3; + x = x << 3; + x = x >>> 3; + x = x | 3; + x = x ^ 3; + x = x % 3; + x = x & 3; + + x = x + g(); + x = x - g(); + x = x / g(); + x = x * g(); + x = x >> g(); + x = x << g(); + x = x >>> g(); + x = x | g(); + x = x ^ g(); + x = x % g(); + x = x & g(); + } + expect: { + var x; + + x += 3; + x -= 3; + x /= 3; + x *= 3; + x >>= 3; + x <<= 3; + x >>>= 3; + x |= 3; + x ^= 3; + x %= 3; + x &= 3; + + x += g(); + x -= g(); + x /= g(); + x *= g(); + x >>= g(); + x <<= g(); + x >>>= g(); + x |= g(); + x ^= g(); + x %= g(); + x &= g(); + } +} + +op_equals_right_local_var: { + options = { + assignments: true, + evaluate: true, + } + input: { + var x; + + x = (x -= 2) ^ x; + + x = 3 + x; + x = 3 - x; + x = 3 / x; + x = 3 * x; + x = 3 >> x; + x = 3 << x; + x = 3 >>> x; + x = 3 | x; + x = 3 ^ x; + x = 3 % x; + x = 3 & x; + + x = g() + x; + x = g() - x; + x = g() / x; + x = g() * x; + x = g() >> x; + x = g() << x; + x = g() >>> x; + x = g() | x; + x = g() ^ x; + x = g() % x; + x = g() & x; + } + expect: { + var x; + + x = (x -= 2) ^ x; + + x = 3 + x; + x = 3 - x; + x = 3 / x; + x *= 3; + x = 3 >> x; + x = 3 << x; + x = 3 >>> x; + x |= 3; + x ^= 3; + x = 3 % x; + x &= 3; + + x = g() + x; + x = g() - x; + x = g() / x; + x = g() * x; + x = g() >> x; + x = g() << x; + x = g() >>> x; + x = g() | x; + x = g() ^ x; + x = g() % x; + x = g() & x; + } +} +op_equals_left_global_var: { + options = { + assignments: true, + evaluate: true, + } + input: { + x = x + 3; + x = x - 3; + x = x / 3; + x = x * 3; + x = x >> 3; + x = x << 3; + x = x >>> 3; + x = x | 3; + x = x ^ 3; + x = x % 3; + x = x & 3; + + x = x + g(); + x = x - g(); + x = x / g(); + x = x * g(); + x = x >> g(); + x = x << g(); + x = x >>> g(); + x = x | g(); + x = x ^ g(); + x = x % g(); + x = x & g(); + } + expect: { + x += 3; + x -= 3; + x /= 3; + x *= 3; + x >>= 3; + x <<= 3; + x >>>= 3; + x |= 3; + x ^= 3; + x %= 3; + x &= 3; + + x += g(); + x -= g(); + x /= g(); + x *= g(); + x >>= g(); + x <<= g(); + x >>>= g(); + x |= g(); + x ^= g(); + x %= g(); + x &= g(); + } +} + +op_equals_right_global_var: { + options = { + assignments: true, + evaluate: true, + } + input: { + x = (x -= 2) ^ x; + + x = 3 + x; + x = 3 - x; + x = 3 / x; + x = 3 * x; + x = 3 >> x; + x = 3 << x; + x = 3 >>> x; + x = 3 | x; + x = 3 ^ x; + x = 3 % x; + x = 3 & x; + + x = g() + x; + x = g() - x; + x = g() / x; + x = g() * x; + x = g() >> x; + x = g() << x; + x = g() >>> x; + x = g() | x; + x = g() ^ x; + x = g() % x; + x = g() & x; + } + expect: { + x = (x -= 2) ^ x; + + x = 3 + x; + x = 3 - x; + x = 3 / x; + x *= 3; + x = 3 >> x; + x = 3 << x; + x = 3 >>> x; + x |= 3; + x ^= 3; + x = 3 % x; + x &= 3; + + x = g() + x; + x = g() - x; + x = g() / x; + x = g() * x; + x = g() >> x; + x = g() << x; + x = g() >>> x; + x = g() | x; + x = g() ^ x; + x = g() % x; + x = g() & x; + } +} + +increment_decrement_1: { + options = { + assignments: true, + reduce_vars: true, + } + input: { + console.log(function(a) { + a += 1; + a -= 1; + return a; + }(42)); + } + expect: { + console.log(function(a){ + ++a; + --a; + return a; + }(42)); + } + expect_stdout: "42" +} + +increment_decrement_2: { + options = { + assignments: true, + passes: 2, + reduce_vars: true, + } + input: { + console.log(function(a) { + a = a + 1; + a = a - 1; + a += 1; + a -= 1; + return a; + }(42)); + } + expect: { + console.log(function(a){ + ++a; + --a; + ++a; + --a; + return a; + }(42)); + } + expect_stdout: "42" +} + +lazily_chained_assignments: { + options = { + assignments: true, + collapse_vars: true, + conditionals: true, + unused: true, + } + input: { + function f(a) { + if (a = console.log("foo")) + a = console.log("bar"); + return a; + } + function g(b) { + if (b = console.log("baz")) + ; + else + b = console.log("moo"); + return b; + } + console.log(f(), g()); + } + expect: { + function f(a) { + return console.log("foo") && console.log("bar"); + } + function g(b) { + return console.log("baz") || console.log("moo"); + } + console.log(f(), g()); + } + expect_stdout: [ + "foo", + "baz", + "moo", + "undefined undefined", + ] +} + +issue_3375_1: { + options = { + assignments: true, + reduce_vars: true, + } + input: { + function p(o) { + console.log(typeof o, o); + } + p(function(b) { + var a = b += 1; + --b; + return a; + }("object")); + } + expect: { + function p(o) { + console.log(typeof o, o); + } + p(function(b) { + var a = b += 1; + --b; + return a; + }("object")); + } + expect_stdout: "string object1" +} + +issue_3375_2: { + options = { + assignments: true, + reduce_vars: true, + } + input: { + function p(o) { + console.log(typeof o, o); + } + p(function(b) { + var a = b -= 1; + --b; + return a; + }("object")); + } + expect: { + function p(o) { + console.log(typeof o, o); + } + p(function(b) { + var a = --b; + --b; + return a; + }("object")); + } + expect_stdout: "number NaN" +} + +issue_3427: { + options = { + assignments: true, + sequences: true, + side_effects: true, + unused: true, + } + input: { + (function() { + var a; + a || (a = {}); + })(); + } + expect: {} +} + +issue_3429_1: { + options = { + assignments: true, + side_effects: true, + unused: true, + } + input: { + var a = "PASS"; + (function(b) { + b && (b = a = "FAIL"); + })(); + console.log(a); + } + expect: { + var a = "PASS"; + (function(b) { + b = b && (a = "FAIL"); + })(); + console.log(a); + } + expect_stdout: "PASS" +} + +issue_3429_2: { + options = { + assignments: true, + side_effects: true, + unused: true, + } + input: { + var a; + (function(b) { + b || (b = a = "FAIL"); + })(42); + console.log(a); + } + expect: { + var a; + (function(b) { + b = b || (a = "FAIL"); + })(42); + console.log(a); + } + expect_stdout: "undefined" +} + +issue_3949_1: { + options = { + assignments: true, + evaluate: true, + reduce_vars: true, + } + input: { + var a = 42; + function f() { + var b = a; + b = b >> 2; + return 100 + b; + } + console.log(f()); + } + expect: { + var a = 42; + function f() { + var b = a; + b >>= 2; + return 100 + b; + } + console.log(f()); + } + expect_stdout: "110" +} + +issue_3949_2: { + options = { + assignments: true, + evaluate: true, + reduce_vars: true, + } + input: { + var a = 42; + function f() { + var b = a; + b = 5 & b; + return 100 + b; + } + console.log(f()); + } + expect: { + var a = 42; + function f() { + var b = a; + b &= 5; + return 100 + b; + } + console.log(f()); + } + expect_stdout: "100" +} + +issue_4521: { + options = { + assignments: true, + dead_code: true, + } + input: { + var a = (a = 42 | a) ? console.log(a) : 0; + } + expect: { + var a = (a |= 42) ? console.log(a) : 0; + } + expect_stdout: "42" +} + +logical_assignments: { + input: { + var a = 42, b = null, c; + a &&= "foo"; + b ||= "bar"; + c ??= "baz"; + console.log(a, b, c); + } + expect_exact: 'var a=42,b=null,c;a&&="foo";b||="bar";c??="baz";console.log(a,b,c);' + expect_stdout: "foo bar baz" + node_version: ">=15" +} + +logical_collapse_vars_1: { + options = { + collapse_vars: true, + } + input: { + var a = "FAIL", b = false; + a = "PASS"; + b ??= a; + console.log(a); + } + expect: { + var a = "FAIL", b = false; + a = "PASS"; + b ??= a; + console.log(a); + } + expect_stdout: "PASS" + node_version: ">=15" +} + +logical_collapse_vars_2: { + options = { + collapse_vars: true, + } + input: { + var a = "PASS"; + (function(b) { + b ||= (a = "FAIL", {}); + return b; + })(console).log(a); + } + expect: { + var a = "PASS"; + (function(b) { + return b ||= (a = "FAIL", {}); + })(console).log(a); + } + expect_stdout: "PASS" + node_version: ">=15" +} + +logical_collapse_vars_3: { + options = { + collapse_vars: true, + } + input: { + var a = 6; + a *= 7; + a ??= "FAIL"; + console.log(a); + } + expect: { + var a = 6; + a = a * 7 ?? "FAIL"; + console.log(a); + } + expect_stdout: "42" + node_version: ">=15" +} + +logical_reduce_vars: { + options = { + evaluate: true, + reduce_vars: true, + toplevel: true, + unused: true, + } + input: { + var a = "PASS", b = 42; + b ??= a = "FAIL"; + console.log(a); + } + expect: { + var a = "PASS", b = 42; + b ??= a = "FAIL"; + console.log(a); + } + expect_stdout: "PASS" + node_version: ">=15" +} + +logical_side_effects: { + options = { + side_effects: true, + toplevel: true, + unused: true, + } + input: { + var a = "PASS", b = 42; + b ??= a = "FAIL"; + console.log(a); + } + expect: { + var a = "PASS", b = 42; + b ??= a = "FAIL"; + console.log(a); + } + expect_stdout: "PASS" + node_version: ">=15" +} + +evaluate_lazy_assignment: { + options = { + evaluate: true, + reduce_vars: true, + toplevel: true, + unused: true, + } + input: { + var a = 42; + console.log(a &&= "PASS"); + } + expect: { + console.log("PASS"); + } + expect_stdout: "PASS" + node_version: ">=15" +} + +issue_4815_1: { + options = { + evaluate: true, + reduce_vars: true, + toplevel: true, + unused: true, + } + input: { + var a = "PASS"; + 42..p &&= a = "FAIL"; + console.log(a); + } + expect: { + var a = "PASS"; + 42..p &&= a = "FAIL"; + console.log(a); + } + expect_stdout: "PASS" + node_version: ">=15" +} + +issue_4815_2: { + options = { + pure_getters: "strict", + side_effects: true, + } + input: { + var a = "PASS"; + 42..p &&= a = "FAIL"; + console.log(a); + } + expect: { + var a = "PASS"; + 42..p &&= a = "FAIL"; + console.log(a); + } + expect_stdout: "PASS" + node_version: ">=15" +} + +issue_4819: { + options = { + comparisons: true, + } + input: { + console.log(void 0 === ([].p &&= 42)); + } + expect: { + console.log(void 0 === ([].p &&= 42)); + } + expect_stdout: "true" + node_version: ">=15" +} + +issue_4827_1: { + options = { + collapse_vars: true, + toplevel: true, + } + input: { + A = "FAIL"; + var a = A, b = "PASS", c; + c &&= b = a, console.log(b); + } + expect: { + var a = A = "FAIL", b = "PASS", c; + c &&= b = a, console.log(b); + } + expect_stdout: "PASS" + node_version: ">=15" +} + +issue_4827_2: { + options = { + collapse_vars: true, + inline: true, + reduce_vars: true, + side_effects: true, + toplevel: true, + unused: true, + } + input: { + var a = 0, b = "PASS"; + function f(c) { + a++, + c &&= b = a; + } + f(); + console.log(b); + } + expect: { + var a = 0, b = "PASS"; + a++, + c &&= b = a; + var c; + console.log(b); + } + expect_stdout: "PASS" + node_version: ">=15" +} + +issue_4827_3: { + options = { + merge_vars: true, + toplevel: true, + } + input: { + var a = 0, b, c; + a++; + c &&= b = a; + console.log(b); + } + expect: { + var a = 0, b, c; + a++; + c &&= b = a; + console.log(b); + } + expect_stdout: "undefined" + node_version: ">=15" +} + +issue_4876: { + options = { + pure_getters: "strict", + reduce_vars: true, + side_effects: true, + toplevel: true, + } + input: { + try { + var a = null; + var b = a &&= 42; + b.p; + } catch (e) { + console.log("PASS"); + } + } + expect: { + try { + var a = null; + var b = a &&= 42; + b.p; + } catch (e) { + console.log("PASS"); + } + } + expect_stdout: "PASS" + node_version: ">=15" +} + +issue_4924_1: { + options = { + collapse_vars: true, + side_effects: true, + toplevel: true, + unused: true, + } + input: { + var a, b; + console.log("PASS"); + a = function() {}; + b = function() {}(b ||= a); + } + expect: { + var b; + console.log("PASS"); + b = void (b ||= function() {}); + } + expect_stdout: "PASS" + node_version: ">=15" +} + +issue_4924_2: { + options = { + collapse_vars: true, + dead_code: true, + passes: 2, + sequences: true, + side_effects: true, + toplevel: true, + unused: true, + } + input: { + var a, b; + console.log("PASS"); + a = function() {}; + b = function() {}(b ||= a); + } + expect: { + console.log("PASS"); + } + expect_stdout: "PASS" + node_version: ">=15" +} + +issue_5670: { + options = { + assignments: true, + evaluate: true, + reduce_vars: true, + } + input: { + (function(a, b) { + a && a && (a = b += "") || console.log("PASS"); + })(); + } + expect: { + (function(a, b) { + a = a, + console.log("PASS"); + })(); + } + expect_stdout: "PASS" +} diff --git a/test/compress/awaits.js b/test/compress/awaits.js new file mode 100644 index 00000000000..42e86c8033a --- /dev/null +++ b/test/compress/awaits.js @@ -0,0 +1,3644 @@ +async_arrow: { + input: { + (async a => console.log(a))("PASS"); + console.log(typeof (async () => 42)()); + } + expect_exact: '(async a=>console.log(a))("PASS");console.log(typeof(async()=>42)());' + expect_stdout: [ + "PASS", + "object", + ] + node_version: ">=8" +} + +async_computed: { + input: { + var o = { + async [42]() { + return this.p; + }, + p: "PASS", + }; + o[42]().then(console.log); + } + expect_exact: 'var o={async[42](){return this.p},p:"PASS"};o[42]().then(console.log);' + expect_stdout: "PASS" + node_version: ">=8" +} + +async_label: { + input: { + (async function() { + async: console.log("PASS"); + })(); + } + expect_exact: '(async function(){async:console.log("PASS")})();' + expect_stdout: "PASS" + node_version: ">=8" +} + +await_await: { + options = { + awaits: true, + side_effects: true, + } + input: { + (async function() { + await await { + then(resolve) { + resolve({ + then() { + console.log("PASS"); + }, + }); + }, + }; + })(); + } + expect: { + (async function() { + await { + then(resolve) { + resolve({ + then() { + console.log("PASS"); + }, + }); + }, + }; + })(); + } + expect_stdout: "PASS" + node_version: ">=8" +} + +defun_name: { + input: { + async function await() { + console.log("PASS"); + } + await(); + } + expect: { + async function await() { + console.log("PASS"); + } + await(); + } + expect_stdout: "PASS" + node_version: ">=8" +} + +drop_fname: { + rename = true + options = { + reduce_vars: true, + toplevel: true, + unused: true, + } + mangle = { + toplevel: true, + } + input: { + async function await() { + console.log("PASS"); + } + await(); + } + expect: { + (async function() { + console.log("PASS"); + })(); + } + expect_stdout: "PASS" + node_version: ">=8" +} + +keep_fname: { + options = { + keep_fnames: true, + reduce_vars: true, + toplevel: true, + unused: true, + } + input: { + async function await() { + console.log("PASS"); + } + await(); + } + expect: { + async function await() { + console.log("PASS"); + } + await(); + } + expect_stdout: "PASS" + node_version: ">=8" +} + +nested_await: { + input: { + (async function() { + console.log(function(await) { + return await; + }("PASS")); + })(); + } + expect: { + (async function() { + console.log(function(await) { + return await; + }("PASS")); + })(); + } + expect_stdout: "PASS" + node_version: ">=8" +} + +reduce_single_use_defun: { + options = { + reduce_vars: true, + toplevel: true, + unused: true, + } + input: { + async function f(a) { + console.log(a); + } + f("PASS"); + } + expect: { + (async function(a) { + console.log(a); + })("PASS"); + } + expect_stdout: "PASS" + node_version: ">=8" +} + +dont_inline: { + options = { + inline: true, + } + input: { + (async function() { + A; + })().catch(function() {}); + console.log("PASS"); + } + expect: { + (async function() { + A; + })().catch(function() {}); + console.log("PASS"); + } + expect_stdout: "PASS" + node_version: ">=8" +} + +dont_inline_nested: { + options = { + inline: true, + } + input: { + function await() { + return "PASS"; + } + (async function() { + (function() { + console.log(await("FAIL")); + })(); + })(); + } + expect: { + function await() { + return "PASS"; + } + (async function() { + (function() { + console.log(await("FAIL")); + })(); + })(); + } + expect_stdout: "PASS" + node_version: ">=8" +} + +inline_await_1: { + options = { + awaits: true, + inline: true, + reduce_vars: true, + unused: true, + } + input: { + (async function() { + async function f() { + await 42; + } + return await f(); + })(); + console.log("PASS"); + } + expect: { + (async function() { + return await void await 42; + })(); + console.log("PASS"); + } + expect_stdout: "PASS" + node_version: ">=8" +} + +inline_await_1_trim: { + options = { + awaits: true, + if_return: true, + inline: true, + reduce_vars: true, + side_effects: true, + unused: true, + } + input: { + (async function() { + async function f() { + await 42; + } + return await f(); + })(); + console.log("PASS"); + } + expect: { + 0; + console.log("PASS"); + } + expect_stdout: "PASS" + node_version: ">=8" +} + +inline_await_2: { + options = { + awaits: true, + inline: true, + reduce_vars: true, + unused: true, + } + input: { + (async function() { + async function f(a) { + await a; + } + return await f(console); + })(); + console.log("PASS"); + } + expect: { + (async function() { + return await void await console; + })(); + console.log("PASS"); + } + expect_stdout: "PASS" + node_version: ">=8" +} + +inline_await_2_trim: { + options = { + awaits: true, + if_return: true, + inline: true, + reduce_vars: true, + side_effects: true, + unused: true, + } + input: { + (async function() { + async function f(a) { + await a.log; + } + return await f(console); + })(); + console.log("PASS"); + } + expect: { + (async function() { + await console.log; + })(); + console.log("PASS"); + } + expect_stdout: "PASS" + node_version: ">=8" +} + +inline_await_3: { + options = { + awaits: true, + inline: true, + reduce_vars: true, + unused: true, + } + input: { + (async function() { + async function f(a, b) { + return await b(a); + } + return await f("PASS", console.log); + })(); + } + expect: { + (async function() { + return await (a = "PASS", b = console.log, await b(a)); + var a, b; + })(); + } + expect_stdout: "PASS" + node_version: ">=8" +} + +inline_await_3_trim: { + options = { + awaits: true, + inline: true, + reduce_vars: true, + sequences: true, + side_effects: true, + unused: true, + } + input: { + (async function() { + async function f(a, b) { + return await b(a); + } + return await f("PASS", console.log); + })(); + } + expect: { + (async function() { + return a = "PASS", b = console.log, b(a); + var a, b; + })(); + } + expect_stdout: "PASS" + node_version: ">=8" +} + +inline_await_this: { + options = { + awaits: true, + inline: true, + } + input: { + var p = "FAIL"; + ({ + p: "PASS", + async f() { + return await (async () => this.p)(); + }, + }).f().then(console.log); + } + expect: { + var p = "FAIL"; + ({ + p: "PASS", + async f() { + return await this.p; + }, + }).f().then(console.log); + } + expect_stdout: "PASS" + node_version: ">=8" +} + +inline_block: { + options = { + awaits: true, + if_return: true, + inline: true, + } + input: { + console.log("foo"); + (async function() { + console.log("bar"); + (async function() { + for (var a of [ "baz" ]) + return a; + })(); + })().then(console.log); + console.log("moo"); + } + expect: { + console.log("foo"); + (async function() { + console.log("bar"); + for (var a of [ "baz" ]) + return void await a; + })().then(console.log); + console.log("moo"); + } + expect_stdout: [ + "foo", + "bar", + "moo", + "undefined", + ] + node_version: ">=8" +} + +inline_block_async: { + options = { + awaits: true, + if_return: true, + inline: true, + } + input: { + console.log("foo"); + (async function() { + console.log("bar"); + (async function() { + for (var a of [ "baz" ]) + return { + then(r) { + console.log("moo"); + r(a); + }, + }; + })(); + })().then(console.log); + console.log("moz"); + } + expect: { + console.log("foo"); + (async function() { + console.log("bar"); + for (var a of [ "baz" ]) + return void await { + then(r) { + console.log("moo"); + r(a); + }, + }; + })().then(console.log); + console.log("moz"); + } + expect_stdout: [ + "foo", + "bar", + "moz", + "moo", + "undefined", + ] + node_version: ">=8" +} + +inline_block_await: { + options = { + awaits: true, + if_return: true, + inline: true, + side_effects: true, + } + input: { + console.log("foo"); + (async function() { + console.log("bar"); + await async function() { + for (var a of [ "baz" ]) + return a; + }(); + })().then(console.log); + console.log("moo"); + } + expect: { + console.log("foo"); + (async function() { + console.log("bar"); + for (var a of [ "baz" ]) + return void await a; + })().then(console.log); + console.log("moo"); + } + expect_stdout: [ + "foo", + "bar", + "moo", + "undefined", + ] + node_version: ">=8" +} + +inline_block_await_async: { + options = { + awaits: true, + inline: true, + } + input: { + (async function() { + console.log("foo"); + await (async function() { + while (await console.log("bar")); + console.log("baz"); + })(); + console.log("moo"); + })().then(console.log); + console.log("moz"); + } + expect: { + (async function() { + console.log("foo"); + while (await console.log("bar")); + console.log("baz"); + await 0; + console.log("moo"); + })().then(console.log); + console.log("moz"); + } + expect_stdout: [ + "foo", + "bar", + "moz", + "baz", + "moo", + "undefined", + ] + node_version: ">=8" +} + +inline_block_await_async_return: { + options = { + awaits: true, + if_return: true, + inline: true, + side_effects: true, + } + input: { + console.log("foo"); + (async function() { + console.log("bar"); + await async function() { + for (var a of [ "baz" ]) + return { + then(r) { + console.log("moo"); + r(a); + }, + }; + }(); + })().then(console.log); + console.log("moz"); + } + expect: { + console.log("foo"); + (async function() { + console.log("bar"); + for (var a of [ "baz" ]) + return void await { + then(r) { + console.log("moo"); + r(a); + }, + };; + })().then(console.log); + console.log("moz"); + } + expect_stdout: [ + "foo", + "bar", + "moz", + "moo", + "undefined", + ] + node_version: ">=8" +} + +inline_block_return: { + options = { + awaits: true, + if_return: true, + inline: true, + passes: 2, + side_effects: true, + } + input: { + console.log("foo"); + (async function() { + console.log("bar"); + return async function() { + for (var a of [ "baz" ]) + return a; + }(); + })().then(console.log); + console.log("moo"); + } + expect: { + console.log("foo"); + (async function() { + console.log("bar"); + for (var a of [ "baz" ]) + return a; + })().then(console.log); + console.log("moo"); + } + expect_stdout: [ + "foo", + "bar", + "moo", + "baz", + ] + node_version: ">=8" +} + +inline_block_return_async: { + options = { + awaits: true, + if_return: true, + inline: true, + passes: 2, + side_effects: true, + } + input: { + console.log("foo"); + (async function() { + console.log("bar"); + return async function() { + for (var a of [ "baz" ]) + return { + then(r) { + console.log("moo"); + r(a); + }, + }; + }(); + })().then(console.log); + console.log("moz"); + } + expect: { + console.log("foo"); + (async function() { + console.log("bar"); + for (var a of [ "baz" ]) + return { + then(r) { + console.log("moo"); + r(a); + }, + }; + })().then(console.log); + console.log("moz"); + } + expect_stdout: [ + "foo", + "bar", + "moz", + "moo", + "baz", + ] + node_version: ">=8" +} + +await_then: { + options = { + awaits: true, + side_effects: true, + } + input: { + var a = "PASS"; + function f() { + return { + then: function(r) { + a = "FAIL"; + r(); + }, + }; + } + (async function() { + f(), await 42; + while (console.log(a)); + })(); + } + expect: { + var a = "PASS"; + function f() { + return { + then: function(r) { + a = "FAIL"; + r(); + }, + }; + } + (async function() { + await !f(); + while (console.log(a)); + })(); + } + expect_stdout: "PASS" + node_version: ">=8" +} + +await_unary_1: { + options = { + awaits: true, + side_effects: true, + } + input: { + var a = "PASS"; + function f() { + return { + then: function(r) { + a = "FAIL"; + r(); + }, + }; + } + (async function() { + await !f(); + while (console.log(a)); + })(); + } + expect: { + var a = "PASS"; + function f() { + return { + then: function(r) { + a = "FAIL"; + r(); + }, + }; + } + (async function() { + await !f(); + while (console.log(a)); + })(); + } + expect_stdout: "PASS" + node_version: ">=8" +} + +await_unary_2: { + options = { + awaits: true, + side_effects: true, + } + input: { + var a = "PASS"; + function f() { + return { + then: function(r) { + a = "FAIL"; + r(); + }, + }; + } + (async function() { + await ~f(); + while (console.log(a)); + })(); + } + expect: { + var a = "PASS"; + function f() { + return { + then: function(r) { + a = "FAIL"; + r(); + }, + }; + } + (async function() { + await !f(); + while (console.log(a)); + })(); + } + expect_stdout: "PASS" + node_version: ">=8" +} + +await_unary_3: { + options = { + awaits: true, + side_effects: true, + } + input: { + var a; + (async function() { + a = "PASS"; + await delete a.p; + a = "FAIL"; + })(); + console.log(a); + } + expect: { + var a; + (async function() { + a = "PASS"; + await delete a.p; + a = "FAIL"; + })(); + console.log(a); + } + expect_stdout: "PASS" + node_version: ">=8" +} + +await_void_1: { + options = { + awaits: true, + side_effects: true, + } + input: { + var a = "PASS"; + function f() { + return { + then: function(r) { + a = "FAIL"; + r(); + }, + }; + } + (async function() { + await void f(); + while (console.log(a)); + })(); + } + expect: { + var a = "PASS"; + function f() { + return { + then: function(r) { + a = "FAIL"; + r(); + }, + }; + } + (async function() { + await !f(); + while (console.log(a)); + })(); + } + expect_stdout: "PASS" + node_version: ">=8" +} + +await_void_2: { + options = { + awaits: true, + if_return: true, + sequences: true, + side_effects: true, + } + input: { + (async function() { + console.log("PASS"); + return await void 42; + })(); + } + expect: { + (async function() { + console.log("PASS"); + })(); + } + expect_stdout: "PASS" + node_version: ">=8" +} + +evaluate: { + options = { + evaluate: true, + reduce_vars: true, + toplevel: true, + unused: true, + } + input: { + var a = async function() {}(); + console.log(typeof a); + } + expect: { + var a = async function() {}(); + console.log(typeof a); + } + expect_stdout: "object" + node_version: ">=8" +} + +negate: { + options = { + side_effects: true, + } + input: { + console && async function() {} && console.log("PASS"); + } + expect: { + console && async function() {} && console.log("PASS"); + } + expect_stdout: "PASS" + node_version: ">=8" +} + +negate_iife: { + options = { + negate_iife: true, + } + input: { + (async function() { + console.log("PASS"); + })(); + } + expect: { + !async function() { + console.log("PASS"); + }(); + } + expect_stdout: "PASS" + node_version: ">=8" +} + +object_function: { + options = { + properties: true, + side_effects: true, + } + input: { + ({ + async f() { + console.log("PASS"); + }, + }).f(); + } + expect: { + (async () => { + console.log("PASS"); + })(); + } + expect_stdout: "PASS" + node_version: ">=8" +} + +collapse_vars_1: { + options = { + collapse_vars: true, + } + input: { + var a = "FAIL"; + (async function() { + a = "PASS"; + await 42; + return "PASS"; + })(); + console.log(a); + } + expect: { + var a = "FAIL"; + (async function() { + a = "PASS"; + await 42; + return "PASS"; + })(); + console.log(a); + } + expect_stdout: "PASS" + node_version: ">=8" +} + +collapse_vars_2: { + options = { + collapse_vars: true, + } + input: { + var a = "FAIL"; + (async function() { + await (a = "PASS"); + return "PASS"; + })(); + console.log(a); + } + expect: { + var a = "FAIL"; + (async function() { + await (a = "PASS"); + return "PASS"; + })(); + console.log(a); + } + expect_stdout: "PASS" + node_version: ">=8" +} + +collapse_vars_3: { + options = { + collapse_vars: true, + } + input: { + var a = "FAIL"; + (async function() { + await (a = "PASS", 42); + return "PASS"; + })(); + console.log(a); + } + expect: { + var a = "FAIL"; + (async function() { + await (a = "PASS", 42); + return "PASS"; + })(); + console.log(a); + } + expect_stdout: "PASS" + node_version: ">=8" +} + +collapse_funarg_1: { + options = { + collapse_vars: true, + unused: true, + } + input: { + A = "FAIL"; + var a = "PASS"; + (async function({}, b) { + return b; + })(null, A = a); + console.log(A); + } + expect: { + A = "FAIL"; + var a = "PASS"; + (async function({}, b) { + return b; + })(null, A = a); + console.log(A); + } + expect_stdout: "PASS" + node_version: ">=8" +} + +collapse_funarg_2: { + options = { + collapse_vars: true, + unused: true, + } + input: { + A = "FAIL"; + B = "PASS"; + (async function() { + console.log(function({}, a) { + return a; + }(null, A = B)); + })(); + console.log(A); + } + expect: { + A = "FAIL"; + B = "PASS"; + (async function() { + console.log(function({}, a) { + return a; + }(null, A = B)); + })(); + console.log(A); + } + expect_stdout: "PASS" + node_version: ">=8" +} + +collapse_property_lambda: { + options = { + collapse_vars: true, + pure_getters: "strict", + } + input: { + (async function f() { + f.g = () => 42; + return f.g(); + })().then(console.log); + } + expect: { + (async function f() { + return (f.g = () => 42)(); + })().then(console.log); + } + expect_stdout: "42" + node_version: ">=8" +} + +drop_async_1: { + options = { + awaits: true, + inline: true, + side_effects: true, + } + input: { + console.log(function(a) { + (async function() { + a *= 7; + })(); + return a; + }(6)); + } + expect: { + console.log(function(a) { + a *= 7; + return a; + }(6)); + } + expect_stdout: "42" + node_version: ">=8" +} + +drop_async_2: { + options = { + awaits: true, + collapse_vars: true, + evaluate: true, + inline: true, + side_effects: true, + } + input: { + console.log(function(a) { + (async b => await (a *= b))(7); + return a; + }(6)); + } + expect: { + console.log(42); + } + expect_stdout: "42" + node_version: ">=8" +} + +drop_return: { + options = { + side_effects: true, + } + input: { + (async function(a) { + while (!console); + return !console.log(a); + })(42); + } + expect: { + (async function(a) { + while (!console); + console.log(a); + })(42); + } + expect_stdout: "42" + node_version: ">=8" +} + +functions: { + options = { + functions: true, + reduce_vars: true, + unused: true, + } + input: { + !async function() { + var a = async function a() { + return a && "a"; + }; + var b = async function x() { + return !!x; + }; + var c = async function(c) { + return c; + }; + if (await c(await b(await a()))) { + var d = async function() {}; + var e = async function y() { + return typeof y; + }; + var f = async function(f) { + return f; + }; + console.log(await a(await d()), await b(await e()), await c(await f(42)), typeof d, await e(), typeof f); + } + }(); + } + expect: { + !async function() { + async function a() { + return a && "a"; + } + async function b() { + return !!b; + } + async function c(c) { + return c; + } + if (await c(await b(await a()))) { + var d = async function() {}; + var e = async function y() { + return typeof y; + }; + var f = async function(f) { + return f; + }; + console.log(await a(await d()), await b(await e()), await c(await f(42)), typeof d, await e(), typeof f); + } + }(); + } + expect_stdout: "a true 42 function function function" + node_version: ">=8" +} + +functions_use_strict: { + options = { + functions: true, + reduce_vars: true, + unused: true, + } + input: { + "use strict"; + !async function() { + var a = async function a() { + return a && "a"; + }; + var b = async function x() { + return !!x; + }; + var c = async function(c) { + return c; + }; + if (await c(await b(await a()))) { + var d = async function() {}; + var e = async function y() { + return typeof y; + }; + var f = async function(f) { + return f; + }; + console.log(await a(await d()), await b(await e()), await c(await f(42)), typeof d, await e(), typeof f); + } + }(); + } + expect: { + "use strict"; + !async function() { + async function a() { + return a && "a"; + } + async function b() { + return !!b; + } + async function c(c) { + return c; + } + if (await c(await b(await a()))) { + var d = async function() {}; + var e = async function y() { + return typeof y; + }; + var f = async function(f) { + return f; + }; + console.log(await a(await d()), await b(await e()), await c(await f(42)), typeof d, await e(), typeof f); + } + }(); + } + expect_stdout: "a true 42 function function function" + node_version: ">=8" +} + +functions_anonymous: { + options = { + functions: true, + reduce_vars: true, + toplevel: true, + unused: true, + } + input: { + var await = async function() { + console.log("PASS"); + }; + await(await); + } + expect: { + async function await() { + console.log("PASS"); + } + await(); + } + expect_stdout: "PASS" + node_version: ">=8" +} + +functions_inner_var: { + options = { + functions: true, + reduce_vars: true, + toplevel: true, + unused: true, + } + input: { + var await = function a() { + var a; + console.log(a, a); + }; + await(await); + } + expect: { + function await() { + var a; + console.log(a, a); + } + await(); + } + expect_stdout: "undefined undefined" + node_version: ">=8" +} + +instanceof_lambda_1: { + options = { + evaluate: true, + side_effects: true, + } + input: { + console.log(42 instanceof async function() {}); + } + expect: { + console.log(false); + } + expect_stdout: "false" + node_version: ">=8" +} + +instanceof_lambda_2: { + options = { + evaluate: true, + side_effects: false, + } + input: { + console.log(null instanceof async function() {}); + } + expect: { + console.log((null, async function() {}, false)); + } + expect_stdout: "false" + node_version: ">=8" +} + +instanceof_lambda_3: { + options = { + evaluate: true, + side_effects: true, + } + input: { + console.log({} instanceof async function() {}); + } + expect: { + console.log({} instanceof async function() {}); + } + expect_stdout: TypeError("Function has non-object prototype 'undefined' in instanceof check") + node_version: ">=8" +} + +instanceof_lambda_4: { + options = { + side_effects: true, + } + input: { + ({ p: "foo" }) instanceof async function() {}; + } + expect: { + [] instanceof async function() {}; + } + expect_stdout: TypeError("Function has non-object prototype 'undefined' in instanceof check") + node_version: ">=8" +} + +issue_4335_1: { + options = { + inline: true, + } + input: { + var await = "PASS"; + (async function() { + console.log(function() { + return await; + }()); + })(); + } + expect: { + var await = "PASS"; + (async function() { + console.log(function() { + return await; + }()); + })(); + } + expect_stdout: "PASS" + node_version: ">=8" +} + +issue_4335_2: { + options = { + inline: true, + } + input: { + (async function() { + console.log(function() { + function await() {} + return "PASS"; + }()); + })(); + } + expect: { + (async function() { + console.log(function() { + function await() {} + return "PASS"; + }()); + })(); + } + expect_stdout: "PASS" + node_version: ">=8" +} + +issue_4337: { + options = { + reduce_vars: true, + unused: true, + } + input: { + (function(a) { + a(); + })(async function() { + console.log("PASS"); + }); + } + expect: { + (function(a) { + (async function() { + console.log("PASS"); + })(); + })(); + } + expect_stdout: "PASS" + node_version: ">=8" +} + +issue_4340: { + options = { + evaluate: true, + reduce_vars: true, + } + input: { + (async function a(a) { + console.log(a || "PASS"); + })(); + } + expect: { + (async function a(a) { + console.log(a || "PASS"); + })(); + } + expect_stdout: "PASS" + node_version: ">=8" +} + +call_expression: { + input: { + console.log(typeof async function(log) { + (await log)("foo"); + }(console.log).then); + console.log("bar"); + } + expect_exact: 'console.log(typeof async function(log){(await log)("foo")}(console.log).then);console.log("bar");' + expect_stdout: [ + "function", + "bar", + "foo", + ] + node_version: ">=8" +} + +property_access_expression: { + input: { + console.log(typeof async function(con) { + (await con).log("foo"); + }(console).then); + console.log("bar"); + } + expect_exact: 'console.log(typeof async function(con){(await con).log("foo")}(console).then);console.log("bar");' + expect_stdout: [ + "function", + "bar", + "foo", + ] + node_version: ">=8" +} + +reduce_iife_1: { + options = { + evaluate: true, + keep_fargs: false, + reduce_vars: true, + unused: true, + } + input: { + (async function(a) { + console.log(a + a); + })(21); + } + expect: { + (async function() { + console.log(42); + })(); + } + expect_stdout: "42" + node_version: ">=8" +} + +reduce_iife_2: { + options = { + evaluate: true, + reduce_vars: true, + toplevel: true, + unused: true, + } + input: { + var a = 21; + (async function() { + console.log(a + a); + })(); + } + expect: { + (async function() { + console.log(42); + })(); + } + expect_stdout: "42" + node_version: ">=8" +} + +reduce_iife_3: { + options = { + evaluate: true, + reduce_vars: true, + side_effects: true, + toplevel: true, + unused: true, + } + input: { + var a = "foo"; + (async function() { + console.log(a, await a, a, await a); + })(); + a = "bar"; + } + expect: { + var a = "foo"; + (async function() { + console.log(a, await a, a, await a); + })(); + a = "bar"; + } + expect_stdout: "foo foo bar bar" + node_version: ">=8" +} + +issue_4347_1: { + options = { + evaluate: true, + reduce_vars: true, + toplevel: true, + unused: true, + } + input: { + var a = "foo"; + f(); + a = "bar"; + f(); + async function f() { + console.log(a); + } + } + expect: { + var a = "foo"; + f(); + a = "bar"; + f(); + async function f() { + console.log(a); + } + } + expect_stdout: [ + "foo", + "bar", + ] + node_version: ">=8" +} + +issue_4347_2: { + options = { + evaluate: true, + reduce_vars: true, + toplevel: true, + unused: true, + } + input: { + var a = "PASS"; + (async function() { + throw 42; + a = "FAIL"; + })(); + console.log(a); + } + expect: { + var a = "PASS"; + (async function() { + throw 42; + a = "FAIL"; + })(); + console.log(a); + } + expect_stdout: "PASS" + node_version: ">=8" +} + +issue_4349_1: { + input: { + console.log(typeof async function() { + await /abc/; + }().then); + } + expect_exact: "console.log(typeof async function(){await/abc/}().then);" + expect_stdout: "function" + node_version: ">=8" +} + +issue_4349_2: { + options = { + collapse_vars: true, + unused: true, + } + input: { + console.log(typeof async function() { + (function(a) { + this[a]; + }(await 0)); + }().then); + } + expect: { + console.log(typeof async function() { + (function(a) { + this[a]; + }(await 0)); + }().then); + } + expect_stdout: "function" + node_version: ">=8" +} + +issue_4349_3: { + options = { + collapse_vars: true, + unused: true, + } + input: { + console.log(typeof function(await) { + return async function(a) { + this[a]; + }(await); + }(this).then); + } + expect: { + console.log(typeof function(await) { + return async function(a) { + this[a]; + }(await); + }(this).then); + } + expect_stdout: "function" + node_version: ">=8" +} + +issue_4359: { + options = { + collapse_vars: true, + unused: true, + } + input: { + try { + (async function(a) { + return a; + })(A); + } catch (e) { + console.log("PASS"); + } + } + expect: { + try { + (async function(a) { + return a; + })(A); + } catch (e) { + console.log("PASS"); + } + } + expect_stdout: "PASS" + node_version: ">=8" +} + +issue_4377: { + options = { + dead_code: true, + inline: true, + side_effects: true, + } + input: { + console.log(typeof function() { + return function() { + f; + async function f() {} + return f(); + }(); + }().then); + } + expect: { + console.log(typeof function() { + return f(); + async function f() {} + }().then); + } + expect_stdout: "function" + node_version: ">=8" +} + +issue_4406: { + options = { + merge_vars: true, + } + input: { + A = "PASS"; + B = "FAIL"; + (function() { + var a, b; + a = A; + (async function({ + [console.log(a)]: {}, + }) {})((b = B) && { undefined: b }); + })(); + } + expect: { + A = "PASS"; + B = "FAIL"; + (function() { + var a, b; + a = A; + (async function({ + [console.log(a)]: {}, + }) {})((b = B) && { undefined: b }); + })(); + } + expect_stdout: "PASS" + node_version: ">=8" +} + +issue_4417: { + options = { + inline: true, + } + input: { + (async function() { + console.log(function() { + return await => 0; + }().prototype); + })(); + } + expect: { + (async function() { + console.log(function() { + return await => 0; + }().prototype); + })(); + } + expect_stdout: "undefined" + node_version: ">=8" +} + +issue_4454_1: { + rename = false + options = { + merge_vars: true, + } + input: { + function f(a) { + (async function(b = console.log(a)) {})(); + var await = 42..toString(); + console.log(await); + } + f("PASS"); + } + expect: { + function f(a) { + (async function(b = console.log(a)) {})(); + var await = 42..toString(); + console.log(await); + } + f("PASS"); + } + expect_stdout: [ + "PASS", + "42", + ] + node_version: ">=8" +} + +issue_4454_2: { + rename = true + options = { + merge_vars: true, + } + input: { + function f(a) { + (async function(b = console.log(a)) {})(); + var await = 42..toString(); + console.log(await); + } + f("PASS"); + } + expect: { + function f(a) { + (async function(c = console.log(a)) {})(); + var b = 42..toString(); + console.log(b); + } + f("PASS"); + } + expect_stdout: [ + "PASS", + "42", + ] + node_version: ">=8" +} + +issue_4534: { + options = { + arguments: true, + } + input: { + (function(await) { + (async () => console.log(arguments[0]))(); + })("PASS"); + } + expect: { + (function(await) { + (async () => console.log(arguments[0]))(); + })("PASS"); + } + expect_stdout: "PASS" + node_version: ">=8" +} + +issue_4581: { + options = { + evaluate: true, + reduce_vars: true, + toplevel: true, + unused: true, + } + input: { + var a = "PASS"; + (async () => (A, a = "FAIL"))(); + console.log(a); + } + expect: { + var a = "PASS"; + (async () => (A, a = "FAIL"))(); + console.log(a); + } + expect_stdout: "PASS" + node_version: ">=8" +} + +issue_4595: { + options = { + awaits: true, + inline: true, + } + input: { + (async function() { + await async function f() { + console.log(f.length); + }(); + })(); + } + expect: { + (async function() { + await async function f() { + console.log(f.length); + }(); + })(); + } + expect_stdout: "0" + node_version: ">=8" +} + +issue_4598: { + options = { + conditionals: true, + } + input: { + if (console.log("PASS")) { + async function f() {} + } + } + expect: { + async function f() {} + console.log("PASS"); + } + expect_stdout: "PASS" + node_version: ">=8" +} + +issue_4618: { + options = { + functions: true, + reduce_vars: true, + unused: true, + } + input: { + console.log(typeof function() { + var await = async function f() { + console || f(); + }; + console.log; + return await; + }()); + } + expect: { + console.log(typeof function() { + var await = async function f() { + console || f(); + }; + console.log; + return await; + }()); + } + expect_stdout: "function" + node_version: ">=8" +} + +issue_4717: { + options = { + inline: true, + reduce_vars: true, + sequences: true, + side_effects: true, + unused: true, + } + input: { + (function() { + async function f() { + var a = function() { + await; + }(); + return "FAIL"; + } + return f(); + })().then(console.log).catch(function() { + console.log("PASS"); + }); + } + expect: { + (async function() { + return function() { + await; + }(), "FAIL"; + })().then(console.log).catch(function() { + console.log("PASS"); + }); + } + expect_stdout: "PASS" + node_version: ">=8" +} + +issue_4738_1: { + options = { + awaits: true, + side_effects: true, + } + input: { + (async function() { + await { + then() { + console.log("PASS"); + }, + }; + })(); + } + expect: { + (async function() { + await { + then() { + console.log("PASS"); + }, + }; + })(); + } + expect_stdout: "PASS" + node_version: ">=8" +} + +issue_4738_2: { + options = { + awaits: true, + side_effects: true, + } + input: { + (async function() { + await { + get then() { + console.log("PASS"); + }, + }; + })(); + } + expect: { + (async function() { + await { + get then() { + console.log("PASS"); + }, + }; + })(); + } + expect_stdout: "PASS" + node_version: ">=8" +} + +issue_4738_3: { + options = { + awaits: true, + side_effects: true, + } + input: { + (async function() { + await { + then: function() { + console.log("PASS"); + }, + }; + })(); + } + expect: { + (async function() { + await { + then: function() { + console.log("PASS"); + }, + }; + })(); + } + expect_stdout: "PASS" + node_version: ">=8" +} + +issue_4747: { + options = { + collapse_vars: true, + reduce_vars: true, + unused: true, + } + input: { + console.log(function(a) { + async function f() { + a = "PASS"; + null.p += "PASS"; + } + f(); + return a; + }("FAIL")); + } + expect: { + console.log(function(a) { + (async function() { + a = "PASS"; + null.p += "PASS"; + })(); + return a; + }("FAIL")); + } + expect_stdout: "PASS" + node_version: ">=8" +} + +issue_4764_1: { + options = { + side_effects: true, + } + input: { + (async function() { + return { + then() { + console.log("PASS"); + }, + }; + })(); + } + expect: { + (async function() { + return { + then() { + console.log("PASS"); + }, + }; + })(); + } + expect_stdout: "PASS" + node_version: ">=8" +} + +issue_4764_2: { + options = { + arrows: true, + side_effects: true, + } + input: { + (async () => ({ + get then() { + console.log("PASS"); + }, + }))(); + } + expect: { + (async () => ({ + get then() { + console.log("PASS"); + }, + }))(); + } + expect_stdout: "PASS" + node_version: ">=8" +} + +issue_4764_3: { + options = { + side_effects: true, + } + input: { + (async function(o) { + return o; + })({ + then() { + console.log("PASS"); + }, + }); + } + expect: { + (async function(o) { + return o; + })({ + then() { + console.log("PASS"); + }, + }); + } + expect_stdout: "PASS" + node_version: ">=8" +} + +issue_4972_1: { + options = { + awaits: true, + side_effects: true, + } + input: { + console.log("foo"); + (async function() { + try { + return await "bar"; + } finally { + console.log("baz"); + } + })().then(console.log); + console.log("moo"); + } + expect: { + console.log("foo"); + (async function() { + try { + return await "bar"; + } finally { + console.log("baz"); + } + })().then(console.log); + console.log("moo"); + } + expect_stdout: [ + "foo", + "moo", + "baz", + "bar", + ] + node_version: ">=8" +} + +issue_4972_2: { + options = { + awaits: true, + side_effects: true, + } + input: { + console.log("foo"); + (async function() { + try { + console.log("bar"); + } finally { + return await "baz"; + } + })().then(console.log); + console.log("moo"); + } + expect: { + console.log("foo"); + (async function() { + try { + console.log("bar"); + } finally { + return "baz"; + } + })().then(console.log); + console.log("moo"); + } + expect_stdout: [ + "foo", + "bar", + "moo", + "baz", + ] + node_version: ">=8" +} + +issue_4972_3: { + options = { + awaits: true, + side_effects: true, + } + input: { + console.log("foo"); + try { + (async function() { + return await "bar"; + })().then(console.log); + } finally { + console.log("baz"); + } + console.log("moo"); + } + expect: { + console.log("foo"); + try { + (async function() { + return "bar"; + })().then(console.log); + } finally { + console.log("baz"); + } + console.log("moo"); + } + expect_stdout: [ + "foo", + "baz", + "moo", + "bar", + ] + node_version: ">=8" +} + +issue_4974: { + options = { + awaits: true, + side_effects: true, + } + input: { + (async function f() { + return 42 in f(); + })(); + console.log("PASS"); + } + expect: { + (async function f() { + return 42 in f(); + })(); + console.log("PASS"); + } + expect_stdout: true + node_version: ">=8" +} + +issue_4975: { + options = { + awaits: true, + side_effects: true, + } + input: { + (async function f(a) { + try { + if (a) console.log(typeof f()); + } catch (e) {} + })(42); + } + expect: { + (async function f(a) { + try { + if (a) console.log(typeof f()); + } catch (e) {} + })(42); + } + expect_stdout: "object" + node_version: ">=8" +} + +issue_4987: { + options = { + awaits: true, + side_effects: true, + } + input: { + (async function() { + try { + await 42; + } finally { + console.log("foo"); + } + })(); + console.log("bar"); + } + expect: { + (async function() { + try { + await 0; + } finally { + console.log("foo"); + } + })(); + console.log("bar"); + } + expect_stdout: [ + "bar", + "foo", + ] + node_version: ">=8" +} + +issue_5001: { + options = { + awaits: true, + inline: true, + side_effects: true, + } + input: { + var a = 0; + (async function() { + a++ | await 42; + })(); + console.log(a ? "PASS" : "FAIL"); + } + expect: { + var a = 0; + a++; + console.log(a ? "PASS" : "FAIL"); + } + expect_stdout: "PASS" + node_version: ">=8" +} + +issue_5019_1: { + options = { + dead_code: true, + } + input: { + (function(a) { + (async function() { + await 42; + console.log(a); + })(); + a = "PASS"; + })("FAIL"); + } + expect: { + (function(a) { + (async function() { + await 42; + console.log(a); + })(); + a = "PASS"; + })("FAIL"); + } + expect_stdout: "PASS" + node_version: ">=8" +} + +issue_5019_2: { + options = { + dead_code: true, + } + input: { + console.log("sync", function(a) { + (async function() { + console.log(await "async", a); + })(); + return a = "PASS"; + }("FAIL")); + } + expect: { + console.log("sync", function(a) { + (async function() { + console.log(await "async", a); + })(); + return a = "PASS"; + }("FAIL")); + } + expect_stdout: [ + "sync PASS", + "async PASS", + ] + node_version: ">=8" +} + +issue_5019_3: { + options = { + inline: true, + toplevel: true, + } + input: { + for (var i in "foo") { + (function(a) { + (async function() { + console.log(await "async", a); + })(); + })(i); + console.log("sync", i); + } + } + expect: { + for (var i in "foo") { + (function(a) { + (async function() { + console.log(await "async", a); + })(); + })(i); + console.log("sync", i); + } + } + expect_stdout: [ + "sync 0", + "sync 1", + "sync 2", + "async 0", + "async 1", + "async 2", + ] + node_version: ">=8" +} + +issue_5023_1: { + options = { + awaits: true, + reduce_vars: true, + side_effects: true, + } + input: { + (async function() { + let a = a; + })(); + console.log("PASS"); + } + expect: { + (async function() { + let a = a; + })(); + console.log("PASS"); + } + expect_stdout: "PASS" + node_version: ">=8" +} + +issue_5023_2: { + options = { + awaits: true, + reduce_vars: true, + side_effects: true, + } + input: { + (async function() { + let a; + a = a; + })(); + console.log("PASS"); + } + expect: { + (function() { + let a; + a = a; + })(); + console.log("PASS"); + } + expect_stdout: "PASS" + node_version: ">=8" +} + +issue_5034: { + options = { + functions: true, + reduce_vars: true, + unused: true, + } + input: { + (function() { + var await = function f() { + return async function() { + return f; + }; + }; + await()().then(function(value) { + console.log(value === await ? "PASS" : "FAIL"); + }); + })(); + } + expect: { + (function() { + var await = function f() { + return async function() { + return f; + }; + }; + await()().then(function(value) { + console.log(value === await ? "PASS" : "FAIL"); + }); + })(); + } + expect_stdout: "PASS" + node_version: ">=8" +} + +issue_5070: { + options = { + awaits: true, + side_effects: true, + } + input: { + (async function() { + try { + for await (var a of console.log("PASS")); + } catch (e) {} + })(); + } + expect: { + (async function() { + try { + for await (var a of console.log("PASS")); + } catch (e) {} + })(); + } + expect_stdout: "PASS" + node_version: ">=10" +} + +issue_5157_async_function: { + options = { + awaits: true, + side_effects: true, + } + input: { + async function f() { + throw "FAIL"; + } + (async function() { + try { + return await f(); + } catch (e) { + return "PASS"; + } + })().then(console.log); + } + expect: { + async function f() { + throw "FAIL"; + } + (async function() { + try { + return await f(); + } catch (e) { + return "PASS"; + } + })().then(console.log); + } + expect_stdout: "PASS" + node_version: ">=8" +} + +issue_5157_async_iife: { + options = { + awaits: true, + side_effects: true, + } + input: { + (async function() { + try { + return await async function() { + throw "FAIL"; + }(); + } catch (e) { + return "PASS"; + } + })().then(console.log); + } + expect: { + (async function() { + try { + return await async function() { + throw "FAIL"; + }(); + } catch (e) { + return "PASS"; + } + })().then(console.log); + } + expect_stdout: "PASS" + node_version: ">=8" +} + +issue_5157_promise: { + options = { + awaits: true, + side_effects: true, + } + input: { + var p = new Promise(function(resolve, reject) { + reject("FAIL"); + }); + (async function() { + try { + return await p; + } catch (e) { + return "PASS"; + } + })().then(console.log); + } + expect: { + var p = new Promise(function(resolve, reject) { + reject("FAIL"); + }); + (async function() { + try { + return await p; + } catch (e) { + return "PASS"; + } + })().then(console.log); + } + expect_stdout: "PASS" + node_version: ">=8" +} + +issue_5159_1: { + options = { + awaits: true, + side_effects: true, + } + input: { + (async function() { + try { + throw "foo"; + } catch (e) { + return await "bar"; + } finally { + console.log("baz"); + } + })().catch(console.log).then(console.log); + console.log("moo"); + } + expect: { + (async function() { + try { + throw "foo"; + } catch (e) { + return await "bar"; + } finally { + console.log("baz"); + } + })().catch(console.log).then(console.log); + console.log("moo"); + } + expect_stdout: [ + "moo", + "baz", + "bar", + ] + node_version: ">=8" +} + +issue_5159_2: { + options = { + awaits: true, + side_effects: true, + } + input: { + (async function() { + try { + throw "foo"; + } catch (e) { + return await "bar"; + } + })().catch(console.log).then(console.log); + console.log("baz"); + } + expect: { + (async function() { + try { + throw "foo"; + } catch (e) { + return "bar"; + } + })().catch(console.log).then(console.log); + console.log("baz"); + } + expect_stdout: [ + "baz", + "bar", + ] + node_version: ">=8" +} + +issue_5177: { + options = { + properties: true, + } + input: { + (async function() { + return { + p(await) {}, + }.p; + })().then(function(a) { + console.log(typeof a); + }); + } + expect: { + (async function() { + return { + p(await) {}, + }.p; + })().then(function(a) { + console.log(typeof a); + }); + } + expect_stdout: "function" + node_version: ">=8" +} + +issue_5250: { + options = { + awaits: true, + inline: true, + } + input: { + (async function() { + await function() { + while (console.log("foo")); + }(); + console.log("bar"); + })(); + console.log("baz"); + } + expect: { + (async function() { + while (console.log("foo")); + await 0; + console.log("bar"); + })(); + console.log("baz"); + } + expect_stdout: [ + "foo", + "baz", + "bar", + ] + node_version: ">=8" +} + +issue_5258_1: { + options = { + awaits: true, + inline: true, + } + input: { + (async function() { + (async function() { + throw "FAIL"; + })(); + return "PASS"; + })().catch(console.log).then(console.log); + } + expect: { + (async function() { + (async function() { + throw "FAIL"; + })(); + return "PASS"; + })().catch(console.log).then(console.log); + } + expect_stdout: "PASS" + node_version: ">=8" +} + +issue_5258_2: { + options = { + awaits: true, + inline: true, + } + input: { + function f() { + throw "FAIL"; + } + (async function() { + (async function() { + f(); + })(); + return "PASS"; + })().catch(console.log).then(console.log); + } + expect: { + function f() { + throw "FAIL"; + } + (async function() { + (async function() { + f(); + })(); + return "PASS"; + })().catch(console.log).then(console.log); + } + expect_stdout: "PASS" + node_version: ">=8" +} + +issue_5298: { + options = { + awaits: true, + side_effects: true, + } + input: { + var a = "PASS"; + (async function() { + for (a in [ 42 in null ]); + })(); + console.log(a); + } + expect: { + var a = "PASS"; + (async function() { + for (a in [ 42 in null ]); + })(); + console.log(a); + } + expect_stdout: "PASS" + node_version: ">=8" +} + +issue_5305_1: { + options = { + awaits: true, + inline: true, + } + input: { + var a = "PASS"; + (async function() { + try { + return await function() { + while (!console); + }(); + } finally { + a = "FAIL"; + } + })(); + console.log(a); + } + expect: { + var a = "PASS"; + (async function() { + try { + while (!console); + return await void 0; + } finally { + a = "FAIL"; + } + })(); + console.log(a); + } + expect_stdout: "PASS" + node_version: ">=8" +} + +issue_5305_2: { + options = { + awaits: true, + inline: true, + } + input: { + var a = "PASS"; + (async function() { + try { + throw null; + } catch (e) { + return await function() { + while (!console); + }(); + } finally { + a = "FAIL"; + } + })(); + console.log(a); + } + expect: { + var a = "PASS"; + (async function() { + try { + throw null; + } catch (e) { + while (!console); + return await void 0; + } finally { + a = "FAIL"; + } + })(); + console.log(a); + } + expect_stdout: "PASS" + node_version: ">=8" +} + +issue_5305_3: { + options = { + awaits: true, + inline: true, + side_effects: true, + } + input: { + var a = "PASS"; + (async function() { + try { + await function() { + while (!console); + }(); + } catch (e) { + a = "FAIL"; + } + })(); + console.log(a); + } + expect: { + var a = "PASS"; + try { + while (!console); + } catch (e) { + a = "FAIL"; + } + console.log(a); + } + expect_stdout: "PASS" + node_version: ">=8" +} + +issue_5456: { + options = { + inline: true, + merge_vars: true, + } + input: { + var a = true; + (function() { + (function(b, c) { + var d = async function() { + c = await null; + }(); + var e = function() { + if (c) + console.log(typeof d); + while (b); + }(); + })(function(i) { + return console.log("foo") && i; + }(a)); + })(); + } + expect: { + var a = true; + (function() { + b = (i = a, console.log("foo") && i), + d = async function() { + c = await null; + }(), + e = function() { + if (c) console.log(typeof d); + while (b); + }(), + void 0; + var b, c, d, e; + var i; + })(); + } + expect_stdout: "foo" + node_version: ">=8" +} + +issue_5478: { + options = { + side_effects: true, + } + input: { + A = { + get then() { + a = "FAIL"; + }, + }; + var a = "PASS"; + (async function() { + for (var b in "foo") + return void A; + })(); + console.log(a); + } + expect: { + A = { + get then() { + a = "FAIL"; + }, + }; + var a = "PASS"; + (async function() { + for (var b in "foo") + return !A; + })(); + console.log(a); + } + expect_stdout: "PASS" + node_version: ">=8" +} + +issue_5493: { + options = { + collapse_vars: true, + reduce_vars: true, + } + input: { + (async function(a) { + var b = await [ 42 || b, a = b ]; + console.log(a); + })(); + } + expect: { + (async function(a) { + var b = await [ 42 || b, a = b ]; + console.log(a); + })(); + } + expect_stdout: "undefined" + node_version: ">=8" +} + +issue_5506: { + options = { + dead_code: true, + } + input: { + console.log(function(a) { + (async function() { + a = null in (a = "PASS"); + })(); + return a; + }("FAIL")); + } + expect: { + console.log(function(a) { + (async function() { + a = null in (a = "PASS"); + })(); + return a; + }("FAIL")); + } + expect_stdout: "PASS" + node_version: ">=8" +} + +issue_5528_1: { + options = { + awaits: true, + inline: true, + } + input: { + (async function() { + await function() { + try { + return; + } finally { + console.log("foo"); + } + }(); + })(); + console.log("bar"); + } + expect: { + (async function() { + await function() { + try { + return; + } finally { + console.log("foo"); + } + }(); + })(); + console.log("bar"); + } + expect_stdout: [ + "foo", + "bar", + ] + node_version: ">=8" +} + +issue_5528_2: { + options = { + awaits: true, + inline: true, + } + input: { + (async function() { + await function() { + try { + return 42; + } finally { + console.log("foo"); + } + }(); + })(); + console.log("bar"); + } + expect: { + (async function() { + await function() { + try { + return 42; + } finally { + console.log("foo"); + } + }(); + })(); + console.log("bar"); + } + expect_stdout: [ + "foo", + "bar", + ] + node_version: ">=8" +} + +issue_5528_3: { + options = { + awaits: true, + inline: true, + } + input: { + (async function() { + await function() { + try { + FAIL; + } catch (e) { + return console.log("foo"); + } finally { + console.log("bar"); + } + }(); + })(); + console.log("baz"); + } + expect: { + (async function() { + await function() { + try { + FAIL; + } catch (e) { + return console.log("foo"); + } finally { + console.log("bar"); + } + }(); + })(); + console.log("baz"); + } + expect_stdout: [ + "foo", + "bar", + "baz", + ] + node_version: ">=8" +} + +issue_5528_4: { + options = { + awaits: true, + inline: true, + } + input: { + (async function() { + await function() { + try { + return { + then() { + console.log("foo"); + }, + }; + } finally { + console.log("bar"); + } + }(); + })(); + console.log("baz"); + } + expect: { + (async function() { + await function() { + try { + return { + then() { + console.log("foo"); + }, + }; + } finally { + console.log("bar"); + } + }(); + })(); + console.log("baz"); + } + expect_stdout: [ + "bar", + "baz", + "foo", + ] + node_version: ">=8" +} + +issue_5634_1: { + options = { + awaits: true, + inline: true, + } + input: { + var a = "foo"; + (async function() { + (async function() { + try { + return { + then(resolve) { + console.log("bar"); + resolve(); + console.log("baz"); + }, + }; + } finally { + a = "moo"; + } + })(); + })(); + console.log(a); + } + expect: { + var a = "foo"; + (async function() { + (async function() { + try { + return { + then(resolve) { + console.log("bar"); + resolve(); + console.log("baz"); + }, + }; + } finally { + a = "moo"; + } + })(); + })(); + console.log(a); + } + expect_stdout: [ + "moo", + "bar", + "baz", + ] + node_version: ">=8" +} + +issue_5634_1_side_effects: { + options = { + awaits: true, + inline: true, + side_effects: true, + } + input: { + var a = "foo"; + (async function() { + (async function() { + try { + return { + then(resolve) { + console.log("bar"); + resolve(); + console.log("baz"); + }, + }; + } finally { + a = "moo"; + } + })(); + })(); + console.log(a); + } + expect: { + var a = "foo"; + (async function() { + try { + return { + then(resolve) { + console.log("bar"); + resolve(); + console.log("baz"); + }, + }; + } finally { + a = "moo"; + } + })(); + console.log(a); + } + expect_stdout: [ + "moo", + "bar", + "baz", + ] + node_version: ">=8" +} + +issue_5634_2: { + options = { + awaits: true, + inline: true, + } + input: { + var a = "foo"; + (async function() { + await async function() { + try { + return { + then(resolve) { + console.log("bar"); + resolve(); + console.log("baz"); + }, + }; + } finally { + a = "moo"; + } + }(); + })(); + console.log(a); + } + expect: { + var a = "foo"; + (async function() { + await async function() { + try { + return { + then(resolve) { + console.log("bar"); + resolve(); + console.log("baz"); + }, + }; + } finally { + a = "moo"; + } + }(); + })(); + console.log(a); + } + expect_stdout: [ + "moo", + "bar", + "baz", + ] + node_version: ">=8" +} + +issue_5634_2_side_effects: { + options = { + awaits: true, + inline: true, + side_effects: true, + } + input: { + var a = "foo"; + (async function() { + await async function() { + try { + return { + then(resolve) { + console.log("bar"); + resolve(); + console.log("baz"); + }, + }; + } finally { + a = "moo"; + } + }(); + })(); + console.log(a); + } + expect: { + var a = "foo"; + (async function() { + try { + return { + then(resolve) { + console.log("bar"); + resolve(); + console.log("baz"); + }, + }; + } finally { + a = "moo"; + } + })(); + console.log(a); + } + expect_stdout: [ + "moo", + "bar", + "baz", + ] + node_version: ">=8" +} + +issue_5634_3: { + options = { + awaits: true, + inline: true, + } + input: { + var a = "foo"; + (async function() { + return async function() { + try { + return { + then(resolve) { + console.log("bar"); + resolve(); + console.log("baz"); + }, + }; + } finally { + a = "moo"; + } + }(); + })(); + console.log(a); + } + expect: { + var a = "foo"; + (async function() { + return async function() { + try { + return { + then(resolve) { + console.log("bar"); + resolve(); + console.log("baz"); + }, + }; + } finally { + a = "moo"; + } + }(); + })(); + console.log(a); + } + expect_stdout: [ + "moo", + "bar", + "baz", + ] + node_version: ">=8" +} + +issue_5634_3_side_effects: { + options = { + awaits: true, + inline: true, + side_effects: true, + } + input: { + var a = "foo"; + (async function() { + return async function() { + try { + return { + then(resolve) { + console.log("bar"); + resolve(); + console.log("baz"); + }, + }; + } finally { + a = "moo"; + } + }(); + })(); + console.log(a); + } + expect: { + var a = "foo"; + (async function() { + try { + return { + then(resolve) { + console.log("bar"); + resolve(); + console.log("baz"); + }, + }; + } finally { + a = "moo"; + } + })(); + console.log(a); + } + expect_stdout: [ + "moo", + "bar", + "baz", + ] + node_version: ">=8" +} + +issue_5692_1: { + options = { + awaits: true, + inline: true, + } + input: { + (async function() { + (async function() { + for await (var k of []); + })(); + console.log("foo"); + })(); + console.log("bar"); + } + expect: { + (async function() { + (async function() { + for await (var k of []); + })(); + console.log("foo"); + })(); + console.log("bar"); + } + expect_stdout: [ + "foo", + "bar", + ] + node_version: ">=10" +} + +issue_5692_2: { + options = { + awaits: true, + inline: true, + } + input: { + (async function() { + (async function() { + for (var k of []); + })(); + console.log("foo"); + })(); + console.log("bar"); + } + expect: { + (async function() { + for (var k of []); + console.log("foo"); + })(); + console.log("bar"); + } + expect_stdout: [ + "foo", + "bar", + ] + node_version: ">=8" +} diff --git a/test/compress/bigint.js b/test/compress/bigint.js new file mode 100644 index 00000000000..cf674a8a4ec --- /dev/null +++ b/test/compress/bigint.js @@ -0,0 +1,106 @@ +arithmetic: { + input: { + console.log(((1n + 0x2n) * (0o3n - -4n)) >> (5n - 6n)); + } + expect_exact: "console.log((1n+0x2n)*(0o3n- -4n)>>5n-6n);" + expect_stdout: "42n" + node_version: ">=10.4.0" +} + +minus_dot: { + input: { + console.log(typeof -42n.toString(), typeof (-42n).toString()); + } + expect_exact: "console.log(typeof-42n.toString(),typeof(-42n).toString());" + expect_stdout: "number string" + node_version: ">=10.4.0" +} + +evaluate: { + options = { + evaluate: true, + unsafe: true, + } + input: { + console.log((0xDEAD_BEEFn).toString(16)); + } + expect: { + console.log(0xdeadbeefn.toString(16)); + } + expect_stdout: "deadbeef" + node_version: ">=10.4.0" +} + +Number: { + options = { + unsafe: true, + } + input: { + console.log(Number(-0xfeed_dead_beef_badn)); + } + expect: { + console.log(+("" + -0xfeed_dead_beef_badn)); + } + expect_stdout: "-1148098955808013200" + node_version: ">=10.4.0" +} + +issue_4590: { + options = { + collapse_vars: true, + } + input: { + A = 1; + 0n || console.log("PASS"); + } + expect: { + A = 1; + 0n || console.log("PASS"); + } + expect_stdout: "PASS" + node_version: ">=10.4.0" +} + +issue_4801: { + options = { + booleans: true, + collapse_vars: true, + reduce_vars: true, + unused: true, + } + input: { + try { + (function(a) { + A = 42; + a || A; + })(!(0 == 42 >> 0o644n)); + } catch (e) { + console.log("PASS"); + } + } + expect: { + try { + (function(a) { + 0 != (A = 42) >> 0o644n || A; + })(); + } catch (e) { + console.log("PASS"); + } + } + expect_stdout: "PASS" + node_version: ">=10.4.0" +} + +issue_5728: { + options = { + evaluate: true, + } + input: { + console.log("" + 4n + 2); + } + expect: { + console.log("42"); + } + expect_stdout: "42" + node_version: ">=10.4.0" +} diff --git a/test/compress/booleans.js b/test/compress/booleans.js new file mode 100644 index 00000000000..ae69fd0f150 --- /dev/null +++ b/test/compress/booleans.js @@ -0,0 +1,866 @@ +iife_boolean_context: { + options = { + booleans: true, + evaluate: true, + } + input: { + console.log(function() { + return Object(1) || false; + }() ? "PASS" : "FAIL"); + console.log(function() { + return [].length || true; + }() ? "PASS" : "FAIL"); + } + expect: { + console.log(function() { + return Object(1); + }() ? "PASS" : "FAIL"); + console.log(function() { + return [].length, 1; + }() ? "PASS" : "FAIL"); + } + expect_stdout: [ + "PASS", + "PASS", + ] + expect_warnings: [ + "WARN: Dropping side-effect-free || [test/compress/booleans.js:2,19]", + "WARN: Boolean || always true [test/compress/booleans.js:5,19]", + ] +} + +de_morgan_1a: { + options = { + booleans: true, + } + input: { + function f(a) { + return a || a; + } + console.log(f(null), f(42)); + } + expect: { + function f(a) { + return a; + } + console.log(f(null), f(42)); + } + expect_stdout: "null 42" +} + +de_morgan_1b: { + options = { + booleans: true, + } + input: { + function f(a) { + return a && a; + } + console.log(f(null), f(42)); + } + expect: { + function f(a) { + return a; + } + console.log(f(null), f(42)); + } + expect_stdout: "null 42" +} + +de_morgan_1c: { + options = { + booleans: true, + } + input: { + console.log(delete (NaN && NaN)); + } + expect: { + console.log(delete (0, NaN)); + } + expect_stdout: "true" +} + +de_morgan_1d: { + options = { + booleans: true, + } + input: { + function f(a) { + return (a = false) || a; + } + console.log(f(null), f(42)); + } + expect: { + function f(a) { + return a = !1; + } + console.log(f(null), f(42)); + } + expect_stdout: "false false" +} + +de_morgan_2a: { + options = { + booleans: true, + conditionals: true, + } + input: { + function f(a, b) { + return a || (a || b); + } + console.log(f(null), f(null, {})); + console.log(f(42), f(42, {})); + } + expect: { + function f(a, b) { + return a || b; + } + console.log(f(null), f(null, {})); + console.log(f(42), f(42, {})); + } + expect_stdout: [ + "undefined {}", + "42 42", + ] +} + +de_morgan_2b: { + options = { + booleans: true, + evaluate: true, + } + input: { + function f(a, b) { + return a || (a && b); + } + console.log(f(null), f(null, {})); + console.log(f(42), f(42, {})); + } + expect: { + function f(a, b) { + return a; + } + console.log(f(null), f(null, {})); + console.log(f(42), f(42, {})); + } + expect_stdout: [ + "null null", + "42 42", + ] +} + +de_morgan_2c: { + options = { + booleans: true, + evaluate: true, + } + input: { + function f(a, b) { + return a && (a || b); + } + console.log(f(null), f(null, {})); + console.log(f(42), f(42, {})); + } + expect: { + function f(a, b) { + return a; + } + console.log(f(null), f(null, {})); + console.log(f(42), f(42, {})); + } + expect_stdout: [ + "null null", + "42 42", + ] +} + +de_morgan_2d: { + options = { + booleans: true, + evaluate: true, + side_effects: true, + } + input: { + function f(a, b) { + return a && (a && b); + } + console.log(f(null), f(null, {})); + console.log(f(42), f(42, {})); + } + expect: { + function f(a, b) { + return a && b; + } + console.log(f(null), f(null, {})); + console.log(f(42), f(42, {})); + } + expect_stdout: [ + "null null", + "undefined {}", + ] +} + +de_morgan_2e: { + options = { + booleans: true, + conditionals: true, + } + input: { + function f(a, b) { + return (a && b) && b; + } + console.log(f(null), f(null, {})); + console.log(f(42), f(42, {})); + } + expect: { + function f(a, b) { + return a && b; + } + console.log(f(null), f(null, {})); + console.log(f(42), f(42, {})); + } + expect_stdout: [ + "null null", + "undefined {}", + ] +} + +de_morgan_3a: { + options = { + booleans: true, + conditionals: true, + } + input: { + function f(a, b, c) { + return a || ((a || b) || c); + } + console.log(f(null, false), f(null, false, {}), f(null, true), f(null, true, {})); + console.log(f(42, false), f(42, false, {}), f(42, true), f(42, true, {})); + } + expect: { + function f(a, b, c) { + return a || b || c; + } + console.log(f(null, !1), f(null, !1, {}), f(null, !0), f(null, !0, {})); + console.log(f(42, !1), f(42, !1, {}), f(42, !0), f(42, !0, {})); + } + expect_stdout: [ + "undefined {} true true", + "42 42 42 42", + ] +} + +de_morgan_3b: { + options = { + booleans: true, + evaluate: true, + side_effects: true, + } + input: { + function f(a, b, c) { + return a || ((a || b) && c); + } + console.log(f(null, false), f(null, false, {}), f(null, true), f(null, true, {})); + console.log(f(42, false), f(42, false, {}), f(42, true), f(42, true, {})); + } + expect: { + function f(a, b, c) { + return a || b && c; + } + console.log(f(null, !1), f(null, !1, {}), f(null, !0), f(null, !0, {})); + console.log(f(42, !1), f(42, !1, {}), f(42, !0), f(42, !0, {})); + } + expect_stdout: [ + "false false undefined {}", + "42 42 42 42", + ] +} + +de_morgan_3c: { + options = { + booleans: true, + evaluate: true, + side_effects: true, + } + input: { + function f(a, b, c) { + return a || ((a && b) || c); + } + console.log(f(null, false), f(null, false, {}), f(null, true), f(null, true, {})); + console.log(f(42, false), f(42, false, {}), f(42, true), f(42, true, {})); + } + expect: { + function f(a, b, c) { + return a || c; + } + console.log(f(null, !1), f(null, !1, {}), f(null, !0), f(null, !0, {})); + console.log(f(42, !1), f(42, !1, {}), f(42, !0), f(42, !0, {})); + } + expect_stdout: [ + "undefined {} undefined {}", + "42 42 42 42", + ] +} + +de_morgan_3d: { + options = { + booleans: true, + evaluate: true, + } + input: { + function f(a, b, c) { + return a || ((a && b) && c); + } + console.log(f(null, false), f(null, false, {}), f(null, true), f(null, true, {})); + console.log(f(42, false), f(42, false, {}), f(42, true), f(42, true, {})); + } + expect: { + function f(a, b, c) { + return a; + } + console.log(f(null, !1), f(null, !1, {}), f(null, !0), f(null, !0, {})); + console.log(f(42, !1), f(42, !1, {}), f(42, !0), f(42, !0, {})); + } + expect_stdout: [ + "null null null null", + "42 42 42 42", + ] +} + +de_morgan_3e: { + options = { + booleans: true, + evaluate: true, + } + input: { + function f(a, b, c) { + return a && ((a || b) || c); + } + console.log(f(null, false), f(null, false, {}), f(null, true), f(null, true, {})); + console.log(f(42, false), f(42, false, {}), f(42, true), f(42, true, {})); + } + expect: { + function f(a, b, c) { + return a; + } + console.log(f(null, !1), f(null, !1, {}), f(null, !0), f(null, !0, {})); + console.log(f(42, !1), f(42, !1, {}), f(42, !0), f(42, !0, {})); + } + expect_stdout: [ + "null null null null", + "42 42 42 42", + ] +} + +de_morgan_3f: { + options = { + booleans: true, + evaluate: true, + side_effects: true, + } + input: { + function f(a, b, c) { + return a && ((a || b) && c); + } + console.log(f(null, false), f(null, false, {}), f(null, true), f(null, true, {})); + console.log(f(42, false), f(42, false, {}), f(42, true), f(42, true, {})); + } + expect: { + function f(a, b, c) { + return a && c; + } + console.log(f(null, !1), f(null, !1, {}), f(null, !0), f(null, !0, {})); + console.log(f(42, !1), f(42, !1, {}), f(42, !0), f(42, !0, {})); + } + expect_stdout: [ + "null null null null", + "undefined {} undefined {}", + ] +} + +de_morgan_3g: { + options = { + booleans: true, + evaluate: true, + side_effects: true, + } + input: { + function f(a, b, c) { + return a && ((a && b) || c); + } + console.log(f(null, false), f(null, false, {}), f(null, true), f(null, true, {})); + console.log(f(42, false), f(42, false, {}), f(42, true), f(42, true, {})); + } + expect: { + function f(a, b, c) { + return a && (b || c); + } + console.log(f(null, !1), f(null, !1, {}), f(null, !0), f(null, !0, {})); + console.log(f(42, !1), f(42, !1, {}), f(42, !0), f(42, !0, {})); + } + expect_stdout: [ + "null null null null", + "undefined {} true true", + ] +} + +de_morgan_3h: { + options = { + booleans: true, + conditionals: true, + } + input: { + function f(a, b, c) { + return a && ((a && b) && c); + } + console.log(f(null, false), f(null, false, {}), f(null, true), f(null, true, {})); + console.log(f(42, false), f(42, false, {}), f(42, true), f(42, true, {})); + } + expect: { + function f(a, b, c) { + return a && b && c; + } + console.log(f(null, !1), f(null, !1, {}), f(null, !0), f(null, !0, {})); + console.log(f(42, !1), f(42, !1, {}), f(42, !0), f(42, !0, {})); + } + expect_stdout: [ + "null null null null", + "false false undefined {}", + ] +} + +conditional_chain: { + options = { + booleans: true, + conditionals: true, + evaluate: true, + } + input: { + function f(a, b) { + return a ? a : b ? b : 42; + } + console.log(f("PASS", "FAIL")); + } + expect: { + function f(a, b) { + return a || b || 42; + } + console.log(f("PASS", "FAIL")); + } + expect_stdout: "PASS" +} + +negated_if: { + options = { + booleans: true, + conditionals: true, + side_effects: true, + } + input: { + console.log(function(a) { + if (!a) + return a ? "FAIL" : "PASS"; + }(!console)); + } + expect: { + console.log(function(a) { + if (!a) + return "PASS"; + }(!console)); + } + expect_stdout: "PASS" +} + +concat_truthy: { + options = { + booleans: true, + evaluate: true, + } + input: { + console.log("foo") + (console.log("bar"), "baz") || console.log("moo"); + } + expect: { + console.log("foo") + (console.log("bar"), "baz"); + } + expect_stdout: [ + "foo", + "bar", + ] + expect_warnings: [ + "WARN: + in boolean context always true [test/compress/booleans.js:1,8]", + "WARN: Condition left of || always true [test/compress/booleans.js:1,8]", + ] +} + +process_returns: { + options = { + booleans: true, + } + input: { + (function() { + return 42; + })() && console.log("PASS"); + } + expect: { + (function() { + return 42; + })() && console.log("PASS"); + } + expect_stdout: "PASS" +} + +issue_3465_1: { + options = { + booleans: true, + } + input: { + console.log(function(a) { + return typeof a; + }() ? "PASS" : "FAIL"); + } + expect: { + console.log(function(a) { + return 1; + }() ? "PASS" : "FAIL"); + } + expect_stdout: "PASS" +} + +issue_3465_2: { + options = { + booleans: true, + } + input: { + console.log(function f(a) { + if (!a) console.log(f(42)); + return typeof a; + }() ? "PASS" : "FAIL"); + } + expect: { + console.log(function f(a) { + if (!a) console.log(f(42)); + return typeof a; + }() ? "PASS" : "FAIL"); + } + expect_stdout: [ + "number", + "PASS", + ] +} + +issue_3465_3: { + options = { + booleans: true, + passes: 2, + unused: true, + } + input: { + console.log(function f(a) { + return typeof a; + }() ? "PASS" : "FAIL"); + } + expect: { + console.log(function(a) { + return 1; + }() ? "PASS" : "FAIL"); + } + expect_stdout: "PASS" +} + +issue_2737_2: { + options = { + booleans: true, + inline: true, + reduce_vars: true, + unused: true, + } + input: { + (function(bar) { + for (;bar();) break; + })(function qux() { + return console.log("PASS"), qux; + }); + } + expect: { + (function(bar) { + for (;bar();) break; + })(function() { + return console.log("PASS"), 1; + }); + } + expect_stdout: "PASS" +} + +issue_3658: { + options = { + booleans: true, + evaluate: true, + reduce_vars: true, + } + input: { + console.log(function f() { + console || f(); + return "PASS"; + }()); + } + expect: { + console.log(function f() { + console || f(); + return "PASS"; + }()); + } + expect_stdout: "PASS" +} + +issue_3690: { + options = { + booleans: true, + unused: true, + } + input: { + console.log(function(a) { + return function() { + return a = [ this ]; + }() ? "PASS" : "FAIL"; + }()); + } + expect: { + console.log(function(a) { + return function() { + return 1; + }() ? "PASS" : "FAIL"; + }()); + } + expect_stdout: "PASS" +} + +issue_4374: { + options = { + booleans: true, + conditionals: true, + if_return: true, + reduce_vars: true, + unused: true, + } + input: { + (function() { + console.log(f()); + function f(a) { + if (null) return 0; + if (a) return 1; + return 0; + } + })(); + } + expect: { + (function() { + console.log(function(a) { + return !null && a ? 1 : 0; + }()); + })(); + } + expect_stdout: "0" +} + +issue_5028_1: { + options = { + booleans: true, + conditionals: true, + } + input: { + var a = 1; + console.log(function() { + return a-- ? a-- ? "FAIL 1" : "PASS" : "FAIL 2"; + }()); + } + expect: { + var a = 1; + console.log(function() { + return a-- ? a-- ? "FAIL 1" : "PASS" : "FAIL 2"; + }()); + } + expect_stdout: "PASS" +} + +issue_5028_2: { + options = { + booleans: true, + conditionals: true, + dead_code: true, + if_return: true, + } + input: { + var a = 1; + (function() { + if (a--) + if (a--) + a = "FAIL"; + else + return; + })(); + console.log(a); + } + expect: { + var a = 1; + (function() { + a-- && a-- && (a = "FAIL"); + })(); + console.log(a); + } + expect_stdout: "-1" +} + +issue_5028_3: { + options = { + booleans: true, + conditionals: true, + evaluate: true, + if_return: true, + } + input: { + var a = 1; + (function() { + if (a--) + if (a--) + a = "FAIL"; + else + return; + })(); + console.log(a); + } + expect: { + var a = 1; + (function() { + a-- && a-- && (a = "FAIL"); + })(); + console.log(a); + } + expect_stdout: "-1" +} + +issue_5041_1: { + options = { + booleans: true, + conditionals: true, + } + input: { + var a = 42; + if (a) + if ([ a = null ]) + if (a) + console.log("FAIL"); + else + console.log("PASS"); + } + expect: { + var a = 42; + a && [ a = null ] && (a ? console.log("FAIL") : console.log("PASS")); + } + expect_stdout: "PASS" +} + +issue_5041_2: { + options = { + booleans: true, + conditionals: true, + } + input: { + var a; + if (!a) + if (a = 42) + if (a) + console.log("PASS"); + else + console.log("FAIL"); + } + expect: { + var a; + a || (a = 42) && (a ? console.log("PASS") : console.log("FAIL")); + } + expect_stdout: "PASS" +} + +issue_5228: { + options = { + booleans: true, + evaluate: true, + inline: true, + passes: 2, + } + input: { + console.log(function() { + return !function() { + do { + return null; + } while (console); + }(); + }()); + } + expect: { + console.log(function() { + do { + return !0; + } while (console); + return !0; + }()); + } + expect_stdout: "true" +} + +issue_5469: { + options = { + assignments: true, + booleans: true, + conditionals: true, + dead_code: true, + evaluate: true, + pure_getters: "strict", + side_effects: true, + } + input: { + console.log(function f(a) { + a && 42[a = A && null]; + }()); + } + expect: { + console.log(function f(a) { + a && A, + 0; + }()); + } + expect_stdout: "undefined" +} + +issue_5694_1: { + options = { + booleans: true, + conditionals: true, + } + input: { + var Infinity; + // Node.js v0.12~6 (vm): 42 + console.log((Infinity = 42) && Infinity); + } + expect: { + var Infinity; + console.log((Infinity = 42) && Infinity); + } + expect_stdout: true +} + +issue_5694_2: { + options = { + booleans: true, + conditionals: true, + } + input: { + var undefined; + // Node.js v0.12~6 (vm): NaN + console.log(("foo", ++undefined) || undefined); + } + expect: { + var undefined; + console.log(("foo", ++undefined) || undefined); + } + expect_stdout: true +} diff --git a/test/compress/classes.js b/test/compress/classes.js new file mode 100644 index 00000000000..19954445461 --- /dev/null +++ b/test/compress/classes.js @@ -0,0 +1,4032 @@ +constructor_1: { + input: { + "use strict"; + console.log(new class { + constructor(a) { + this.a = a; + } + }("PASS").a); + } + expect_exact: '"use strict";console.log(new class{constructor(a){this.a=a}}("PASS").a);' + expect_stdout: "PASS" + node_version: ">=4" +} + +constructor_2: { + input: { + "use strict"; + console.log(new class { + "constructor"(a) { + this.a = a; + } + }("PASS").a); + } + expect_exact: '"use strict";console.log(new class{constructor(a){this.a=a}}("PASS").a);' + expect_stdout: "PASS" + node_version: ">=4" +} + +constructor_3: { + input: { + "use strict"; + console.log(new class { + ["constructor"](a) { + this.a = a; + } + }("FAIL").a || "PASS"); + } + expect_exact: '"use strict";console.log(new class{["constructor"](a){this.a=a}}("FAIL").a||"PASS");' + expect_stdout: "PASS" + node_version: ">=4" +} + +constructor_4: { + input: { + "use strict"; + class A { + static constructor(a) { + console.log(a); + } + } + A.constructor("PASS"); + } + expect_exact: '"use strict";class A{static constructor(a){console.log(a)}}A.constructor("PASS");' + expect_stdout: "PASS" + node_version: ">=4" +} + +fields: { + input: { + var o = new class A { + "#p"; + static #p = "PASS"; + async + get + q() { + return A.#p; + } + ; + [6 * 7] = console ? "foo" : "bar" + }; + for (var k in o) + console.log(k, o[k]); + console.log(o.q); + } + expect_exact: 'var o=new class A{"#p";static#p="PASS";async;get q(){return A.#p}[6*7]=console?"foo":"bar"};for(var k in o)console.log(k,o[k]);console.log(o.q);' + expect_stdout: [ + "42 foo", + "#p undefined", + "async undefined", + "PASS", + ] + node_version: ">=12" +} + +modifier_as_field_name: { + input: { + for (var k in new class { async; static = 42 }) + console.log(k); + } + expect_exact: "for(var k in new class{async;static=42})console.log(k);" + expect_stdout: [ + "async", + "static", + ] + node_version: ">=12" +} + +methods: { + input: { + "use strict"; + class A { + static f() { + return "foo"; + } + *g() { + yield A.f(); + yield "bar"; + } + } + for (var a of new A().g()) + console.log(a); + } + expect_exact: '"use strict";class A{static f(){return"foo"}*g(){yield A.f();yield"bar"}}for(var a of(new A).g())console.log(a);' + expect_stdout: [ + "foo", + "bar", + ] + node_version: ">=4" +} + +private_methods: { + input: { + new class A { + static *#f() { + yield A.#p * 3; + } + async #g() { + for (var a of A.#f()) + return a * await 2; + } + static get #p() { + return 7; + } + get q() { + return this.#g(); + } + }().q.then(console.log); + } + expect_exact: "(new class A{static*#f(){yield 3*A.#p}async#g(){for(var a of A.#f())return a*await 2}static get#p(){return 7}get q(){return this.#g()}}).q.then(console.log);" + expect_stdout: "42" + node_version: ">=14.6" +} + +await: { + input: { + var await = "PASS"; + (async function() { + return await new class extends (await function() {}) { [await 42] = await }; + })().then(function(o) { + console.log(o[42]); + }); + } + expect_exact: 'var await="PASS";(async function(){return await new class extends(await function(){}){[await 42]=await}})().then(function(o){console.log(o[42])});' + expect_stdout: "PASS" + node_version: ">=12" +} + +yield: { + input: { + var a = function*() { + yield new class { [yield "foo"] = "bar" }; + }(); + console.log(a.next().value); + console.log(a.next(42).value[42]); + } + expect_exact: 'var a=function*(){yield new class{[yield"foo"]="bar"}}();console.log(a.next().value);console.log(a.next(42).value[42]);' + expect_stdout: [ + "foo", + "bar", + ] + node_version: ">=12" +} + +conditional_parentheses: { + options = { + conditionals: true, + } + input: { + "use strict"; + if (class {}) + console.log("PASS"); + } + expect_exact: '"use strict";(class{})&&console.log("PASS");' + expect_stdout: "PASS" + node_version: ">=4" +} + +class_super: { + input: { + "use strict"; + class A { + static get p() { + return "Foo"; + } + static get q() { + return super.p || 42; + } + constructor() { + console.log("a.p", super.p, this.p); + console.log("a.q", super.q, this.q); + } + get p() { + return "foo"; + } + get q() { + return super.p || null; + } + } + class B extends A { + static get p() { + return "Bar"; + } + static get q() { + return super.p; + } + constructor() { + super(); + console.log("b.p", super.p, this.p); + console.log("b.q", super.q, this.q); + } + get p() { + return "bar"; + } + get q() { + return super.p; + } + } + console.log("A", A.p, A.q); + console.log("B", B.p, B.q); + new B(); + } + expect_exact: '"use strict";class A{static get p(){return"Foo"}static get q(){return super.p||42}constructor(){console.log("a.p",super.p,this.p);console.log("a.q",super.q,this.q)}get p(){return"foo"}get q(){return super.p||null}}class B extends A{static get p(){return"Bar"}static get q(){return super.p}constructor(){super();console.log("b.p",super.p,this.p);console.log("b.q",super.q,this.q)}get p(){return"bar"}get q(){return super.p}}console.log("A",A.p,A.q);console.log("B",B.p,B.q);new B;' + expect_stdout: [ + "A Foo 42", + "B Bar Foo", + "a.p undefined bar", + "a.q undefined foo", + "b.p foo bar", + "b.q null foo", + ] + node_version: ">=4" +} + +static_newline_1: { + input: { + class A { + static + P + } + console.log("P" in A, "static" in A); + console.log("P" in new A(), "static" in new A()); + } + expect_exact: 'class A{static P}console.log("P"in A,"static"in A);console.log("P"in new A,"static"in new A);' + expect_stdout: [ + "true false", + "false false", + ] + node_version: ">=12" +} + +static_newline_2: { + input: { + class A { + static + static + P + } + console.log("P" in A, "static" in A); + console.log("P" in new A(), "static" in new A()); + } + expect_exact: 'class A{static static;P}console.log("P"in A,"static"in A);console.log("P"in new A,"static"in new A);' + expect_stdout: [ + "false true", + "true false", + ] + node_version: ">=12" +} + +static_newline_3: { + input: { + class A { + static + static + static + P + } + console.log("P" in A, "static" in A); + console.log("P" in new A(), "static" in new A()); + } + expect_exact: 'class A{static static;static P}console.log("P"in A,"static"in A);console.log("P"in new A,"static"in new A);' + expect_stdout: [ + "true true", + "false false", + ] + node_version: ">=12" +} + +static_newline_4: { + input: { + class A { + static + static + static + static + P + } + console.log("P" in A, "static" in A); + console.log("P" in new A(), "static" in new A()); + } + expect_exact: 'class A{static static;static static;P}console.log("P"in A,"static"in A);console.log("P"in new A,"static"in new A);' + expect_stdout: [ + "false true", + "true false", + ] + node_version: ">=12" +} + +static_newline_init: { + input: { + class A { + static + { + console.log("PASS"); + } + } + } + expect_exact: 'class A{static{console.log("PASS")}}' + expect_stdout: "PASS" + node_version: ">=16" +} + +static_init: { + input: { + var a = "foo"; + var b = null; + class A { + static { + var a = "bar"; + b = true; + var c = 42; + console.log(a, b, c); + } + } + console.log(a, b, typeof c); + } + expect_exact: 'var a="foo";var b=null;class A{static{var a="bar";b=true;var c=42;console.log(a,b,c)}}console.log(a,b,typeof c);' + expect_stdout: [ + "bar true 42", + "foo true undefined", + ] + node_version: ">=16" +} + +static_field_init: { + options = { + side_effects: true, + } + input: { + (class { + static [console.log("foo")] = console.log("bar"); + static { + console.log("baz"); + } + static [console.log("moo")] = console.log("moz"); + }); + } + expect: { + (class { + static [(console.log("foo"), console.log("moo"))] = ( + console.log("bar"), + (() => { + console.log("baz"); + })(), + console.log("moz") + ); + }); + } + expect_stdout: [ + "foo", + "moo", + "bar", + "baz", + "moz", + ] + node_version: ">=16" +} + +static_field_init_strict: { + options = { + side_effects: true, + } + input: { + "use strict"; + (class { + static [console.log("foo")] = console.log("bar"); + static { + console.log("baz"); + } + static [console.log("moo")] = console.log("moz"); + }); + } + expect: { + "use strict"; + console.log("foo"), + console.log("moo"), + (() => ( + console.log("bar"), + (() => { + console.log("baz"); + })(), + console.log("moz") + ))(); + } + expect_stdout: [ + "foo", + "moo", + "bar", + "baz", + "moz", + ] + node_version: ">=16" +} + +static_init_side_effects_1: { + options = { + merge_vars: true, + side_effects: true, + } + input: { + var a = "FAIL"; + (class { + static { + a = "PASS"; + } + }); + console.log(a); + } + expect: { + var a = "FAIL"; + (class { + static { + a = "PASS"; + } + }); + console.log(a); + } + expect_stdout: "PASS" + node_version: ">=16" +} + +static_init_side_effects_1_strict: { + options = { + merge_vars: true, + side_effects: true, + } + input: { + "use strict"; + var a = "FAIL"; + (class { + static { + a = "PASS"; + } + }); + console.log(a); + } + expect: { + "use strict"; + var a = "FAIL"; + (() => (() => { + a = "PASS"; + })())(); + console.log(a); + } + expect_stdout: "PASS" + node_version: ">=16" +} + +static_init_side_effects_2: { + options = { + hoist_props: true, + reduce_vars: true, + side_effects: true, + } + input: { + var a = "FAIL"; + (class { + static { + a = "PASS"; + } + }); + console.log(a); + } + expect: { + var a = "FAIL"; + (class { + static { + a = "PASS"; + } + }); + console.log(a); + } + expect_stdout: "PASS" + node_version: ">=16" +} + +static_init_side_effects_2_strict: { + options = { + hoist_props: true, + reduce_vars: true, + side_effects: true, + } + input: { + "use strict"; + var a = "FAIL"; + (class { + static { + a = "PASS"; + } + }); + console.log(a); + } + expect: { + "use strict"; + var a = "FAIL"; + (() => (() => { + a = "PASS"; + })())(); + console.log(a); + } + expect_stdout: "PASS" + node_version: ">=16" +} + +block_scoped: { + options = { + evaluate: true, + dead_code: true, + loops: true, + } + input: { + "use strict"; + while (0) { + class A {} + } + if (console) { + class B {} + } + console.log(typeof A, typeof B); + } + expect: { + "use strict"; + 0; + { + class A {} + } + if (console) { + class B {} + } + console.log(typeof A, typeof B); + } + expect_stdout: "undefined undefined" + node_version: ">=4" +} + +retain_declaration: { + options = { + dead_code: true, + } + input: { + "use strict"; + var a = "FAIL"; + try { + console.log(function() { + return a; + class a {} + }()); + } catch (e) { + console.log("PASS"); + } + } + expect: { + "use strict"; + var a = "FAIL"; + try { + console.log(function() { + return a; + class a {} + }()); + } catch (e) { + console.log("PASS"); + } + } + expect_stdout: "PASS" + node_version: ">=4" +} + +drop_extends: { + options = { + inline: true, + passes: 2, + pure_getters: "strict", + reduce_funcs: true, + reduce_vars: true, + sequences: true, + side_effects: true, + unused: true, + } + input: { + "use strict"; + try { + (function() { + var f = () => {}; + class A extends f { + get p() {} + } + A.q = 42; + return class B extends A {}; + })(); + } catch (e) { + console.log("PASS"); + } + } + expect: { + "use strict"; + try { + (class extends (() => {}) {}); + } catch (e) { + console.log("PASS"); + } + } + expect_stdout: "PASS" + node_version: ">=4" +} + +keep_extends_1: { + options = { + toplevel: true, + unused: true, + } + input: { + "use strict"; + try { + class A extends 42 {} + } catch (e) { + console.log("PASS"); + } + } + expect: { + "use strict"; + try { + (class extends 42 {}); + } catch (e) { + console.log("PASS"); + } + } + expect_stdout: "PASS" + node_version: ">=4" +} + +keep_extends_2: { + options = { + side_effects: true, + } + input: { + "use strict"; + (class extends Function {}); + console.log("PASS"); + } + expect: { + "use strict"; + (class extends Function {}); + console.log("PASS"); + } + expect_stdout: "PASS" + node_version: ">=4" +} + +keep_extends_3: { + options = { + toplevel: true, + unused: true, + } + input: { + "use strict"; + class A extends Function {} + console.log("PASS"); + } + expect: { + "use strict"; + (class extends Function {}); + console.log("PASS"); + } + expect_stdout: "PASS" + node_version: ">=4" +} + +drop_name: { + options = { + unused: true, + } + input: { + "use strict"; + try { + console.log(class A extends 42 {}) + } catch (e) { + console.log("PASS"); + } + } + expect: { + "use strict"; + try { + console.log(class extends 42 {}) + } catch (e) { + console.log("PASS"); + } + } + expect_stdout: "PASS" + node_version: ">=4" +} + +separate_name: { + options = { + merge_vars: true, + toplevel: true, + } + input: { + "use strict"; + class A { + constructor(v) { + this.p = v; + } + } + var a = new A("PASS"); + console.log(a.p); + } + expect: { + "use strict"; + class A { + constructor(v) { + this.p = v; + } + } + var a = new A("PASS"); + console.log(a.p); + } + expect_stdout: "PASS" + node_version: ">=4" +} + +static_getter: { + options = { + pure_getters: "strict", + side_effects: true, + } + input: { + "use strict"; + (class { + static get p() { + console.log("PASS"); + }; + }).p; + } + expect: { + "use strict"; + (class { + static get p() { + console.log("PASS"); + }; + }).p; + } + expect_stdout: "PASS" + node_version: ">=4" +} + +static_setter: { + options = { + pure_getters: "strict", + side_effects: true, + } + input: { + "use strict"; + (class { + static set p(v) { + console.log(v); + }; + }).p = "PASS"; + } + expect: { + "use strict"; + (class { + static set p(v) { + console.log(v); + }; + }).p = "PASS"; + } + expect_stdout: "PASS" + node_version: ">=4" +} + +static_side_effects: { + options = { + inline: true, + toplevel: true, + unused: true, + } + input: { + var a = "FAIL 1"; + class A { + static p = a = "PASS"; + q = a = "FAIL 2"; + } + console.log(a); + } + expect: { + var a = "FAIL 1"; + (class { + static c = a = "PASS"; + }); + console.log(a); + } + expect_stdout: "PASS" + node_version: ">=12" +} + +static_side_effects_strict: { + options = { + inline: true, + toplevel: true, + unused: true, + } + input: { + "use strict"; + var a = "FAIL 1"; + class A { + static p = a = "PASS"; + q = a = "FAIL 2"; + } + console.log(a); + } + expect: { + "use strict"; + var a = "FAIL 1"; + a = "PASS"; + console.log(a); + } + expect_stdout: "PASS" + node_version: ">=12" +} + +single_use_1: { + options = { + reduce_vars: true, + toplevel: true, + unused: true, + } + input: { + "use strict"; + class A {} + console.log(typeof new A()); + } + expect: { + "use strict"; + console.log(typeof new class {}()); + } + expect_stdout: "object" + node_version: ">=4" +} + +single_use_2: { + options = { + reduce_vars: true, + toplevel: true, + unused: true, + } + input: { + "use strict"; + class A { + f(a) { + console.log(a); + } + } + new A().f("PASS"); + } + expect: { + "use strict"; + new class { + f(a) { + console.log(a); + } + }().f("PASS"); + } + expect_stdout: "PASS" + node_version: ">=4" +} + +single_use_3: { + options = { + reduce_vars: true, + toplevel: true, + unused: true, + } + input: { + "use strict"; + class A { + f() { + return A; + } + } + console.log(typeof new A().f()); + } + expect: { + "use strict"; + console.log(typeof new class A { + f() { + return A; + } + }().f()); + } + expect_stdout: "function" + node_version: ">=4" +} + +single_use_4: { + options = { + reduce_funcs: true, + reduce_vars: true, + toplevel: true, + unused: true, + } + input: { + "use strict"; + console.log(new class A { + f() { + return typeof A; + } + }().f()); + } + expect: { + "use strict"; + console.log(new class A { + f() { + return typeof A; + } + }().f()); + } + expect_stdout: "function" + node_version: ">=4" +} + +single_use_5: { + options = { + reduce_vars: true, + unused: true, + } + input: { + function f() { + console.log("foo"); + } + (function() { + "use strict"; + class A extends f { + f() { + console.log("bar"); + } + } + console.log("baz"); + new A().f(); + })(); + } + expect: { + function f() { + console.log("foo"); + } + (function() { + "use strict"; + class A extends f { + f() { + console.log("bar"); + } + } + console.log("baz"); + new A().f(); + })(); + } + expect_stdout: [ + "baz", + "foo", + "bar", + ] + node_version: ">=4" +} + +single_use_6: { + options = { + reduce_vars: true, + toplevel: true, + unused: true, + } + input: { + "use strict"; + class A { + [(console.log("foo"), "f")]() { + console.log("bar"); + } + } + console.log("baz"); + new A().f(); + } + expect: { + "use strict"; + class A { + [(console.log("foo"), "f")]() { + console.log("bar"); + } + } + console.log("baz"); + new A().f(); + } + expect_stdout: [ + "foo", + "baz", + "bar", + ] + node_version: ">=4" +} + +single_use_7: { + options = { + passes: 2, + reduce_vars: true, + toplevel: true, + unused: true, + } + input: { + "use strict"; + class A { + static foo() {} + } + var a = "foo" in A; + console.log(a); + } + expect: { + "use strict"; + console.log("foo" in class { + static foo() {} + }); + } + expect_stdout: "true" + node_version: ">=4" +} + +single_use_extends: { + options = { + reduce_vars: true, + toplevel: true, + unused: true, + } + input: { + "use strict"; + class A extends class B { + f() { + return "PASS"; + } + } {} + console.log(new A().f()); + } + expect: { + "use strict"; + console.log(new class extends class { + f() { + return "PASS"; + } + } {}().f()); + } + expect_stdout: "PASS" + node_version: ">=4" +} + +single_use_extends_non_strict: { + options = { + reduce_vars: true, + toplevel: true, + unused: true, + } + input: { + class A extends class B { + f() { + return "PASS"; + } + } {} + console.log(new A().f()); + } + expect: { + console.log(new class extends class { + f() { + return "PASS"; + } + } {}().f()); + } + expect_stdout: "PASS" + node_version: ">=6" +} + +collapse_non_strict: { + options = { + collapse_vars: true, + toplevel: true, + } + input: { + var a = 42..p++; + new class extends (a || function() { + console.log("PASS"); + }) {} + } + expect: { + var a = 42..p++; + new class extends (a || function() { + console.log("PASS"); + }) {} + } + expect_stdout: "PASS" + node_version: ">=6" +} + +collapse_rhs: { + options = { + collapse_vars: true, + unsafe: true, + } + input: { + "use strict"; + var a = "FAIL"; + a = "PASS"; + class A { + p = "PASS"; + } + console.log(a); + } + expect: { + "use strict"; + var a = "FAIL"; + class A { + p = "PASS"; + } + console.log(a = "PASS"); + } + expect_stdout: "PASS" + node_version: ">=12" +} + +collapse_rhs_static: { + options = { + collapse_vars: true, + } + input: { + "use strict"; + var a = "FAIL"; + a = "PASS"; + class A { + static p = "PASS"; + } + console.log(a); + } + expect: { + "use strict"; + var a = "FAIL"; + class A { + static p = "PASS"; + } + console.log(a = "PASS"); + } + expect_stdout: "PASS" + node_version: ">=12" +} + +inline_non_strict: { + options = { + reduce_funcs: true, + reduce_vars: true, + toplevel: true, + unused: true, + } + input: { + function f(a) { + return a.p = "PASS"; + } + class A { + g() { + return f(42); + } + } + console.log(new A().g()); + } + expect: { + function f(a) { + return a.p = "PASS"; + } + console.log(new class { + g() { + return f(42); + } + }().g()); + } + expect_stdout: "PASS" + node_version: ">=6" +} + +self_comparison: { + options = { + booleans: true, + comparisons: true, + reduce_vars: true, + toplevel: true, + unused: true, + } + input: { + "use strict"; + class A {} + console.log(A == A, A != A); + console.log(A === A, A !== A); + } + expect: { + "use strict"; + console.log(!0, !1); + console.log(!0, !1); + } + expect_stdout: [ + "true false", + "true false", + ] + node_version: ">=4" +} + +property_side_effects: { + options = { + inline: true, + keep_fargs: false, + unused: true, + } + input: { + "use strict"; + (function f(a, b) { + class A { + [a.log("PASS")]() { + b.log("FAIL"); + } + } + })(console, console); + } + expect: { + "use strict"; + (function(a) { + a.log("PASS"); + })(console, console); + } + expect_stdout: "PASS" + node_version: ">=4" +} + +property_side_effects_static: { + options = { + inline: true, + keep_fargs: false, + unused: true, + } + input: { + "use strict"; + (function f(a, b) { + class A { + static [a.log("PASS")]() { + b.log("FAIL"); + } + } + })(console, console); + } + expect: { + "use strict"; + (function(a) { + a.log("PASS"); + })(console, console); + } + expect_stdout: "PASS" + node_version: ">=4" +} + +unused_await: { + options = { + inline: true, + unused: true, + } + input: { + var await = "PASS"; + (async function() { + class A { + static p = console.log(await); + } + })(); + } + expect: { + var await = "PASS"; + (async function() { + (class { + static c = console.log(await); + }); + })(); + } + expect_stdout: true + node_version: ">=12 <16" +} + +unused_await_strict: { + options = { + inline: true, + unused: true, + } + input: { + "use strict"; + var await = "PASS"; + (async function() { + class A { + static p = console.log(await); + } + })(); + } + expect: { + "use strict"; + var await = "PASS"; + (async function() { + (() => console.log(await))(); + })(); + } + expect_stdout: true + node_version: ">=12 <16" +} + +computed_key_side_effects: { + options = { + evaluate: true, + reduce_vars: true, + side_effects: true, + toplevel: true, + unused: true, + } + input: { + "use strict"; + var a = 0; + class A { + [(a++, 0)]() {} + } + console.log(a); + } + expect: { + "use strict"; + console.log(1); + } + expect_stdout: "1" + node_version: ">=4" +} + +computed_key_generator: { + options = { + unused: true, + } + input: { + "use strict"; + var a = function*() { + class A { + static [console.log(yield)]() {} + } + }(); + a.next("FAIL"); + a.next("PASS"); + } + expect: { + "use strict"; + var a = function*() { + console.log(yield); + }(); + a.next("FAIL"); + a.next("PASS"); + } + expect_stdout: "PASS" + node_version: ">=4" +} + +keep_fnames: { + options = { + keep_fnames: true, + toplevel: true, + } + mangle = { + keep_fnames: true, + toplevel: true, + } + input: { + "use strict"; + class Foo {} + console.log(Foo.name, class Bar {}.name); + } + expect: { + "use strict"; + class Foo {} + console.log(Foo.name, class Bar {}.name); + } + expect_stdout: "Foo Bar" + node_version: ">=4" +} + +instanceof_lambda: { + options = { + evaluate: true, + side_effects: true, + } + input: { + "use strict"; + console.log(42 instanceof class {}); + } + expect: { + "use strict"; + console.log(false); + } + expect_stdout: "false" + node_version: ">=4" +} + +drop_instanceof: { + options = { + booleans: true, + toplevel: true, + unused: true, + } + input: { + "use strict"; + class A {} + console.log({} instanceof A, Math instanceof A); + } + expect: { + "use strict"; + console.log(!1, (Math, !1)); + } + expect_stdout: "false false" + node_version: ">=4" +} + +keep_instanceof_1: { + options = { + toplevel: true, + unused: true, + } + input: { + "use strict"; + class A {} + var A; + console.log({} instanceof A, Math instanceof A); + } + expect: { + "use strict"; + class A {} + var A; + console.log({} instanceof A, Math instanceof A); + } + expect_stdout: SyntaxError("Identifier has already been declared") + node_version: ">=4" +} + +keep_instanceof_2: { + options = { + toplevel: true, + unused: true, + } + input: { + "use strict"; + var A = Object; + class A {} + console.log({} instanceof A, Math instanceof A); + } + expect: { + "use strict"; + var A = Object; + class A {} + console.log({} instanceof A, Math instanceof A); + } + expect_stdout: SyntaxError("Identifier has already been declared") + node_version: ">=4" +} + +keep_instanceof_3: { + options = { + toplevel: true, + unused: true, + } + input: { + "use strict"; + class A {} + A = Object; + console.log({} instanceof A, Math instanceof A); + } + expect: { + "use strict"; + class A {} + A = Object; + console.log({} instanceof A, Math instanceof A); + } + expect_stdout: "true true" + node_version: ">=4" +} + +keep_field_reference_1: { + options = { + reduce_funcs: true, + reduce_vars: true, + toplevel: true, + unused: true, + } + input: { + "use strict"; + function f() {} + class A { + p = f; + } + console.log(new A().p === new A().p ? "PASS" : "FAIL"); + } + expect: { + "use strict"; + function f() {} + class A { + p = f; + } + console.log(new A().p === new A().p ? "PASS" : "FAIL"); + } + expect_stdout: "PASS" + node_version: ">=12" +} + +keep_field_reference_2: { + options = { + reduce_funcs: true, + reduce_vars: true, + toplevel: true, + unused: true, + } + input: { + "use strict"; + function f() {} + var A = class { + p = f; + }; + console.log(new A().p === new A().p ? "PASS" : "FAIL"); + } + expect: { + "use strict"; + function f() {} + var A = class { + p = f; + }; + console.log(new A().p === new A().p ? "PASS" : "FAIL"); + } + expect_stdout: "PASS" + node_version: ">=12" +} + +keep_field_reference_3: { + options = { + reduce_funcs: true, + reduce_vars: true, + toplevel: true, + unused: true, + } + input: { + "use strict"; + class A {} + class B { + p = A; + } + console.log(new B().p === new B().p ? "PASS" : "FAIL"); + } + expect: { + "use strict"; + class A {} + class B { + p = A; + } + console.log(new B().p === new B().p ? "PASS" : "FAIL"); + } + expect_stdout: "PASS" + node_version: ">=12" +} + +keep_field_reference_4: { + options = { + reduce_funcs: true, + reduce_vars: true, + toplevel: true, + unused: true, + } + input: { + "use strict"; + var A = class {}; + var B = class { + p = A; + }; + console.log(new B().p === new B().p ? "PASS" : "FAIL"); + } + expect: { + "use strict"; + var A = class {}; + var B = class { + p = A; + }; + console.log(new B().p === new B().p ? "PASS" : "FAIL"); + } + expect_stdout: "PASS" + node_version: ">=12" +} + +keep_static_field_reference_1: { + options = { + reduce_funcs: true, + reduce_vars: true, + toplevel: true, + unused: true, + } + input: { + "use strict"; + function f() {} + class A { + static P = f; + } + console.log(A.P === A.P ? "PASS" : "FAIL"); + } + expect: { + "use strict"; + class A { + static P = function() {}; + } + console.log(A.P === A.P ? "PASS" : "FAIL"); + } + expect_stdout: "PASS" + node_version: ">=12" +} + +keep_static_field_reference_2: { + options = { + reduce_funcs: true, + reduce_vars: true, + toplevel: true, + unused: true, + } + input: { + "use strict"; + function f() {} + var A = class { + static P = f; + }; + console.log(A.P === A.P ? "PASS" : "FAIL"); + } + expect: { + "use strict"; + var A = class { + static P = function() {}; + }; + console.log(A.P === A.P ? "PASS" : "FAIL"); + } + expect_stdout: "PASS" + node_version: ">=12" +} + +keep_static_field_reference_3: { + options = { + reduce_funcs: true, + reduce_vars: true, + toplevel: true, + unused: true, + } + input: { + "use strict"; + class A {} + class B { + static P = A; + } + console.log(B.P === B.P ? "PASS" : "FAIL"); + } + expect: { + "use strict"; + class B { + static P = class {}; + } + console.log(B.P === B.P ? "PASS" : "FAIL"); + } + expect_stdout: "PASS" + node_version: ">=12" +} + +keep_static_field_reference_4: { + options = { + reduce_funcs: true, + reduce_vars: true, + toplevel: true, + unused: true, + } + input: { + "use strict"; + var A = class {}; + var B = class { + static P = A; + }; + console.log(B.P === B.P ? "PASS" : "FAIL"); + } + expect: { + "use strict"; + var B = class { + static P = class {}; + }; + console.log(B.P === B.P ? "PASS" : "FAIL"); + } + expect_stdout: "PASS" + node_version: ">=12" +} + +issue_805_1: { + options = { + inline: true, + passes: 3, + pure_getters: "strict", + reduce_vars: true, + sequences: true, + side_effects: true, + unused: true, + } + input: { + "use strict"; + (function(a) { + var unused = class {}; + unused.prototype[a()] = 42; + (unused.prototype.bar = function() { + console.log("bar"); + })(); + return unused; + })(function() { + console.log("foo"); + return "foo"; + }); + } + expect: { + "use strict"; + console.log("foo"), + console.log("bar"); + } + expect_stdout: [ + "foo", + "bar", + ] + node_version: ">=4" +} + +issue_805_2: { + options = { + inline: true, + passes: 3, + pure_getters: "strict", + reduce_vars: true, + sequences: true, + side_effects: true, + unused: true, + } + input: { + "use strict"; + (function(a) { + class unused {} + unused.prototype[a()] = 42; + (unused.prototype.bar = function() { + console.log("bar"); + })(); + return unused; + })(function() { + console.log("foo"); + return "foo"; + }); + } + expect: { + "use strict"; + console.log("foo"), + console.log("bar"); + } + expect_stdout: [ + "foo", + "bar", + ] + node_version: ">=4" +} + +issue_4681: { + options = { + unused: true, + } + input: { + console.log(function(a) { + class A { + static p = a = this; + } + return typeof a; + }()); + } + expect: { + console.log(function(a) { + (class { + static p = a = this; + }); + return typeof a; + }()); + } + expect_stdout: "function" + node_version: ">=12" +} + +issue_4683: { + options = { + dead_code: true, + evaluate: true, + loops: true, + } + input: { + "use strict"; + for (class extends null {}; void console.log("PASS"); ); + } + expect: { + "use strict"; + (class extends null {}); + void console.log("PASS"); + } + expect_stdout: "PASS" + node_version: ">=4" +} + +issue_4685_1: { + options = { + collapse_vars: true, + unused: true, + } + input: { + "use strict"; + new class { + f() { + (function(g) { + if (g() !== this) + console.log("PASS"); + })(() => this); + } + }().f(); + } + expect: { + "use strict"; + new class { + f() { + (function(g) { + if (g() !== this) + console.log("PASS"); + })(() => this); + } + }().f(); + } + expect_stdout: "PASS" + node_version: ">=4" +} + +issue_4685_2: { + options = { + reduce_vars: true, + unused: true, + } + input: { + "use strict"; + new class { + f() { + (function(g) { + if (g() !== this) + console.log("PASS"); + })(() => { + if (console) + return this; + }); + } + }().f(); + } + expect: { + "use strict"; + new class { + f() { + (function(g) { + if (g() !== this) + console.log("PASS"); + })(() => { + if (console) + return this; + }); + } + }().f(); + } + expect_stdout: "PASS" + node_version: ">=4" +} + +issue_4687_1: { + options = { + collapse_vars: true, + unused: true, + } + input: { + "use strict"; + new class { + f() { + console.log(function(g) { + return g() === this; + }(() => this) || "PASS"); + } + }().f(); + } + expect: { + "use strict"; + new class { + f() { + console.log(function(g) { + return g() === this; + }(() => this) || "PASS"); + } + }().f(); + } + expect_stdout: "PASS" + node_version: ">=4" +} + +issue_4687_2: { + options = { + reduce_vars: true, + unused: true, + } + input: { + "use strict"; + new class { + f() { + console.log(function(g) { + return g() === this; + }(() => { + if (console) + return this; + }) || "PASS"); + } + }().f(); + } + expect: { + "use strict"; + new class { + f() { + console.log(function(g) { + return g() === this; + }(() => { + if (console) + return this; + }) || "PASS"); + } + }().f(); + } + expect_stdout: "PASS" + node_version: ">=4" +} + +issue_4705: { + options = { + evaluate: true, + reduce_vars: true, + toplevel: true, + unused: true, + } + input: { + var a = "PASS"; + class A { + p = a = "FAIL"; + [console.log(a)]; + } + } + expect: { + (class { + [console.log("PASS")]() {} + }); + } + expect_stdout: "PASS" + node_version: ">=12" +} + +issue_4705_strict: { + options = { + evaluate: true, + reduce_vars: true, + toplevel: true, + unused: true, + } + input: { + "use strict"; + var a = "PASS"; + class A { + p = a = "FAIL"; + [console.log(a)]; + } + } + expect: { + "use strict"; + console.log("PASS"); + } + expect_stdout: "PASS" + node_version: ">=12" +} + +issue_4720: { + options = { + ie: true, + reduce_vars: true, + toplevel: true, + unused: true, + } + input: { + class A { + static p = function f() {}; + } + console.log(typeof A.p, typeof f); + } + expect: { + class A { + static p = function f() {}; + } + console.log(typeof A.p, typeof f); + } + expect_stdout: "function undefined" + node_version: ">=12" +} + +issue_4721: { + options = { + side_effects: true, + } + input: { + "use strict"; + var a = "foo"; + try { + (class extends 42 { + [a = "bar"]() {} + }) + } catch (e) { + console.log(a); + } + } + expect: { + "use strict"; + var a = "foo"; + try { + (class extends 42 { + [a = "bar"]() {} + }); + } catch (e) { + console.log(a); + } + } + expect_stdout: true + node_version: ">=4" +} + +issue_4722_1: { + options = { + side_effects: true, + } + input: { + "use strict"; + try { + (class extends function*() {} {}); + } catch (e) { + console.log("PASS"); + } + } + expect: { + "use strict"; + try { + (class extends function*() {} {}); + } catch (e) { + console.log("PASS"); + } + } + expect_stdout: "PASS" + node_version: ">=4" +} + +issue_4722_2: { + options = { + side_effects: true, + } + input: { + "use strict"; + try { + (class extends async function() {} {}); + } catch (e) { + console.log("PASS"); + } + } + expect: { + "use strict"; + try { + (class extends async function() {} {}); + } catch (e) { + console.log("PASS"); + } + } + expect_stdout: "PASS" + node_version: ">=8" +} + +issue_4722_3: { + options = { + side_effects: true, + } + input: { + "use strict"; + try { + (class extends async function*() {} {}); + } catch (e) { + console.log("PASS"); + } + } + expect: { + "use strict"; + try { + (class extends async function*() {} {}); + } catch (e) { + console.log("PASS"); + } + } + expect_stdout: "PASS" + node_version: ">=10" +} + +issue_4725_1: { + options = { + inline: true, + } + input: { + "use strict"; + console.log(typeof new class { + f() { + return function g() { + return g; + }(); + } + }().f()); + } + expect: { + "use strict"; + console.log(typeof new class { + f() { + return function g() { + return g; + }(); + } + }().f()); + } + expect_stdout: "function" + node_version: ">=4" +} + +issue_4725_2: { + options = { + if_return: true, + inline: true, + } + input: { + "use strict"; + new class { + f() { + return function() { + while (console.log("PASS")); + }(); + } + }().f(); + } + expect: { + "use strict"; + new class { + f() { + while (console.log("PASS")); + } + }().f(); + } + expect_stdout: "PASS" + node_version: ">=4" +} + +new_target: { + input: { + console.log(typeof new class { + constructor() { + this.f = () => new.target; + } + }().f()); + } + expect: { + console.log(typeof new class { + constructor() { + this.f = () => new.target; + } + }().f()); + } + expect_stdout: "function" + node_version: ">=6" +} + +issue_4756: { + options = { + toplevel: true, + unused: true, + } + input: { + try { + class A extends 42 { + static [console.log("foo")] = console.log("bar"); + } + } catch (e) { + console.log("baz"); + } + } + expect: { + try { + (class extends 42 { + static [console.log("foo")] = console.log("bar"); + }); + } catch (e) { + console.log("baz"); + } + } + expect_stdout: [ + "foo", + "baz", + ] + node_version: ">=12" +} + +issue_4756_strict: { + options = { + toplevel: true, + unused: true, + } + input: { + "use strict"; + try { + class A extends 42 { + static [console.log("foo")] = console.log("bar"); + } + } catch (e) { + console.log("baz"); + } + } + expect: { + "use strict"; + try { + (class extends 42 { + static [console.log("foo")] = console.log("bar"); + }); + } catch (e) { + console.log("baz"); + } + } + expect_stdout: [ + "foo", + "baz", + ] + node_version: ">=12" +} + +issue_4821_1: { + options = { + reduce_vars: true, + toplevel: true, + unused: true, + } + input: { + var a; + class A { + static p = void (a = this); + } + console.log(typeof a); + } + expect: { + var a; + (class { + static p = void (a = this); + }); + console.log(typeof a); + } + expect_stdout: "function" + node_version: ">=12" +} + +issue_4821_2: { + options = { + side_effects: true, + toplevel: true, + unused: true, + } + input: { + var a; + class A { + static p = void (a = this); + } + console.log(typeof a); + } + expect: { + var a; + (class { + static p = void (a = this); + }); + console.log(typeof a); + } + expect_stdout: "function" + node_version: ">=12" +} + +issue_4829_1: { + options = { + properties: true, + } + input: { + "use strict"; + try { + class A extends { f() {} }.f {} + } catch (e) { + console.log("PASS"); + } + } + expect: { + "use strict"; + try { + class A extends [ () => {} ][0] {} + } catch (e) { + console.log("PASS"); + } + } + expect_stdout: "PASS" + node_version: ">=4" +} + +issue_4829_2: { + options = { + properties: true, + } + input: { + "use strict"; + try { + class A extends { + f() { + return arguments; + }, + }.f {} + } catch (e) { + console.log("PASS"); + } + } + expect: { + "use strict"; + try { + class A extends { + f() { + return arguments; + }, + }.f {} + } catch (e) { + console.log("PASS"); + } + } + expect_stdout: "PASS" + node_version: ">=4" +} + +mangle_properties: { + mangle = { + properties: { + domprops: true, + keep_quoted: true, + }, + } + input: { + class A { + static #P = "PASS"; + static get Q() { + return this.#P; + } + #p(n) { + return (this["q"] = n) * this.r; + } + set q(v) { + this.r = v + 1; + } + r = this.#p(6); + } + console.log(A.Q, new A().r); + } + expect: { + class A { + static #t = "PASS"; + static get s() { + return this.#t; + } + #i(t) { + return (this["q"] = t) * this.e; + } + set q(t) { + this.e = t + 1; + } + e = this.#i(6); + } + console.log(A.s, new A().e); + } + expect_stdout: "PASS 42" + expect_warnings: [ + "INFO: Preserving reserved property q", + "INFO: Mapping property #P to #t", + "INFO: Mapping property Q to s", + "INFO: Mapping property #p to #i", + "INFO: Mapping property r to e", + "INFO: Preserving reserved property log", + ] + node_version: ">=14.6" +} + +issue_4848: { + options = { + if_return: true, + } + input: { + "use strict"; + function f(a) { + a(function() { + new A(); + }); + if (!console) + return; + class A { + constructor() { + console.log("PASS"); + } + } + } + var g; + f(function(h) { + g = h; + }); + g(); + } + expect: { + "use strict"; + function f(a) { + a(function() { + new A(); + }); + if (!console) + return; + class A { + constructor() { + console.log("PASS"); + } + } + } + var g; + f(function(h) { + g = h; + }); + g(); + } + expect_stdout: "PASS" + node_version: ">=4" +} + +drop_unused_self_reference: { + options = { + pure_getters: "strict", + reduce_vars: true, + sequences: true, + side_effects: true, + toplevel: true, + unused: true, + } + input: { + "use strict"; + class A {} + (A.p = A).q = console.log("PASS"); + } + expect: { + "use strict"; + console.log("PASS"); + } + expect_stdout: "PASS" + node_version: ">=4" +} + +issue_4951_1: { + input: { + class A { + static#p = console.log("PASS"); + } + } + expect_exact: 'class A{static#p=console.log("PASS")}' + expect_stdout: "PASS" + node_version: ">=12" +} + +issue_4951_2: { + input: { + new class { + constructor() { + this.#f().then(console.log); + } + async#f() { + return await "PASS"; + } + }(); + } + expect_exact: 'new class{constructor(){this.#f().then(console.log)}async#f(){return await"PASS"}};' + expect_stdout: "PASS" + node_version: ">=14.6" +} + +issue_4962_1: { + options = { + ie: true, + inline: true, + reduce_funcs: true, + reduce_vars: true, + unused: true, + } + input: { + (function() { + function f() { + while (console.log(typeof g)); + } + class A { + static p = f(); + } + })(function g() {}); + } + expect: { + (function() { + function f() { + while (console.log(typeof g)); + } + (class { + static c = f(); + }); + })(function g() {}); + } + expect_stdout: "undefined" + node_version: ">=12" +} + +issue_4962_1_strict: { + options = { + ie: true, + inline: true, + reduce_funcs: true, + reduce_vars: true, + unused: true, + } + input: { + "use strict"; + (function() { + function f() { + while (console.log(typeof g)); + } + class A { + static p = f(); + } + })(function g() {}); + } + expect: { + "use strict"; + (function g() {}); + while (console.log(typeof g)); + } + expect_stdout: "undefined" + node_version: ">=12" +} + +issue_4962_1_strict_direct: { + options = { + ie: true, + inline: true, + reduce_funcs: true, + reduce_vars: true, + unused: true, + } + input: { + (function() { + function f() { + "use strict"; + while (console.log(typeof g)); + } + class A { + static p = f(); + } + })(function g() {}); + } + expect: { + (function g() {}), + void class { + static c = function() { + "use strict"; + while (console.log(typeof g)); + }(); + }; + } + expect_stdout: "undefined" + node_version: ">=12" +} + +issue_4962_2: { + options = { + ie: true, + inline: true, + reduce_funcs: true, + reduce_vars: true, + unused: true, + } + input: { + console.log(function f() {}(function g() { + function h() { + f; + } + class A { + static p = h(); + } + }, typeof g)); + } + expect: { + console.log(function f() {}(function g() { + function h() { + f; + } + (class { + static c = h(); + }); + })); + } + expect_stdout: "undefined" + node_version: ">=12" +} + +issue_4962_2_strict: { + options = { + ie: true, + inline: true, + reduce_funcs: true, + reduce_vars: true, + unused: true, + } + input: { + "use strict"; + console.log(function f() {}(function g() { + function h() { + f; + } + class A { + static p = h(); + } + }, typeof g)); + } + expect: { + "use strict"; + console.log(function f() {}(function g() { + f; + })); + } + expect_stdout: "undefined" + node_version: ">=12" +} + +issue_4962_2_strict_direct: { + options = { + ie: true, + inline: true, + reduce_funcs: true, + reduce_vars: true, + unused: true, + } + input: { + console.log(function f() {}(function g() { + function h() { + "use strict"; + f; + } + class A { + static p = h(); + } + }, typeof g)); + } + expect: { + console.log(function f() {}(function g() { + (class { + static c = function() { + "use strict"; + f; + }(); + }); + })); + } + expect_stdout: "undefined" + node_version: ">=12" +} + +issue_4962_2_strict_direct_inline: { + options = { + directives: true, + ie: true, + inline: true, + passes: 2, + reduce_funcs: true, + reduce_vars: true, + unused: true, + } + input: { + console.log(function f() {}(function g() { + function h() { + "use strict"; + f; + } + class A { + static p = h(); + } + }, typeof g)); + } + expect: { + console.log(function f() {}(function g() { + (class { + static c = f; + }); + })); + } + expect_stdout: "undefined" + node_version: ">=12" +} + +issue_4982_1: { + options = { + dead_code: true, + } + input: { + "use strict"; + try {} catch (e) { + class A extends 42 {} + } + console.log("PASS"); + } + expect: { + "use strict"; + { + class A {} + } + console.log("PASS"); + } + expect_stdout: "PASS" + node_version: ">=4" +} + +issue_4982_2: { + options = { + dead_code: true, + } + input: { + var a = "PASS"; + try {} catch (e) { + class A { + static p = a = "FAIL"; + } + } + console.log(a); + } + expect: { + var a = "PASS"; + { + class A {} + } + console.log(a); + } + expect_stdout: "PASS" + node_version: ">=12" +} + +issue_4992: { + options = { + reduce_vars: true, + toplevel: true, + unused: true, + } + input: { + class A { + static P = this; + get p() {} + } + console.log(typeof A.P); + } + expect: { + console.log(typeof class { + static P = this; + get p() {} + }.P); + } + expect_stdout: "function" + node_version: ">=12" +} + +issue_4996_1: { + options = { + evaluate: true, + reduce_vars: true, + toplevel: true, + } + input: { + var a = 1; + console.log(new class A { + p = a-- && new A(); + }().p.p); + } + expect: { + var a = 1; + console.log(new class A { + p = a-- && new A(); + }().p.p); + } + expect_stdout: "0" + node_version: ">=12" +} + +issue_4996_2: { + options = { + reduce_vars: true, + toplevel: true, + unused: true, + } + input: { + var a = 1; + console.log(new class A { + p = a-- && new A(); + }().p.p); + } + expect: { + var a = 1; + console.log(new class A { + p = a-- && new A(); + }().p.p); + } + expect_stdout: "0" + node_version: ">=12" +} + +issue_5015_1: { + options = { + side_effects: true, + } + input: { + "use strict"; + var a; + try { + (class a { + [a]() {} + }); + } catch (e) { + console.log("PASS"); + } + } + expect: { + "use strict"; + var a; + try { + (class a { + [a]() {} + }); + } catch (e) { + console.log("PASS"); + } + } + expect_stdout: "PASS" + node_version: ">=4" +} + +issue_5015_2: { + options = { + reduce_vars: true, + side_effects: true, + toplevel: true, + } + input: { + "use strict"; + try { + new class A { + [(A, 42)]() {} + }(); + } catch (e) { + console.log("PASS"); + } + } + expect: { + "use strict"; + try { + new class A { + [(A, 42)]() {} + }(); + } catch (e) { + console.log("PASS"); + } + } + expect_stdout: "PASS" + node_version: ">=4" +} + +issue_5015_3: { + options = { + reduce_vars: true, + side_effects: true, + toplevel: true, + } + input: { + "use strict"; + (class A { + static f() { + return A; + } + }); + console.log("PASS"); + } + expect: { + "use strict"; + (class A {}); + console.log("PASS"); + } + expect_stdout: "PASS" + node_version: ">=4" +} + +issue_5015_4: { + options = { + reduce_vars: true, + sequences: true, + side_effects: true, + toplevel: true, + unused: true, + } + input: { + "use strict"; + (class A { + static f() { + return A; + } + }); + console.log("PASS"); + } + expect: { + "use strict"; + console.log("PASS"); + } + expect_stdout: "PASS" + node_version: ">=4" +} + +issue_5053_1: { + options = { + evaluate: true, + reduce_vars: true, + toplevel: true, + } + input: { + "use strict"; + try { + console.log(new class A { + constructor() { + A = 42; + } + }()); + } catch (e) { + console.log("PASS"); + } + } + expect: { + "use strict"; + try { + console.log(new class A { + constructor() { + A = 42; + } + }()); + } catch (e) { + console.log("PASS"); + } + } + expect_stdout: "PASS" + node_version: ">=4" +} + +issue_5053_2: { + options = { + evaluate: true, + reduce_vars: true, + toplevel: true, + } + input: { + "use strict"; + try { + console.log(new class A { + f() { + A = 42; + } + }().f()); + } catch (e) { + console.log("PASS"); + } + } + expect: { + "use strict"; + try { + console.log(new class A { + f() { + A = 42; + } + }().f()); + } catch (e) { + console.log("PASS"); + } + } + expect_stdout: "PASS" + node_version: ">=4" +} + +issue_5053_3: { + options = { + evaluate: true, + reduce_vars: true, + toplevel: true, + } + input: { + try { + console.log(new class A { + p = A = 42; + }().p); + } catch (e) { + console.log("PASS"); + } + } + expect: { + try { + console.log(new class A { + p = A = 42; + }().p); + } catch (e) { + console.log("PASS"); + } + } + expect_stdout: "PASS" + node_version: ">=12" +} + +issue_5053_4: { + options = { + evaluate: true, + reduce_vars: true, + toplevel: true, + unused: true, + } + input: { + "use strict"; + class A { + constructor() { + A = 42; + } + } + try { + console.log(new A()); + } catch (e) { + console.log("PASS"); + } + } + expect: { + "use strict"; + class A { + constructor() { + A = 42; + } + } + try { + console.log(new A()); + } catch (e) { + console.log("PASS"); + } + } + expect_stdout: "PASS" + node_version: ">=4" +} + +issue_5082_1: { + options = { + inline: true, + reduce_funcs: true, + reduce_vars: true, + unused: true, + } + input: { + (function() { + class A { + p = console.log("PASS"); + q() {} + } + class B { + static P = new A(); + } + })(); + } + expect: { + (function() { + class A { + p = console.log("PASS"); + q() {} + } + (class { + static c = new A(); + }); + })(); + } + expect_stdout: "PASS" + node_version: ">=12" +} + +issue_5082_1_strict: { + options = { + inline: true, + reduce_funcs: true, + reduce_vars: true, + unused: true, + } + input: { + "use strict"; + (function() { + class A { + p = console.log("PASS"); + q() {} + } + class B { + static P = new A(); + } + })(); + } + expect: { + "use strict"; + (function() { + class A { + p = console.log("PASS"); + q() {} + } + new A(); + })(); + } + expect_stdout: "PASS" + node_version: ">=12" +} + +issue_5082_2: { + options = { + inline: true, + passes: 2, + reduce_funcs: true, + reduce_vars: true, + unused: true, + } + input: { + (function() { + class A { + p = console.log("PASS"); + q() {} + } + class B { + static P = new A(); + } + })(); + } + expect: { + (function() { + class A { + p = console.log("PASS"); + q() {} + } + (class { + static c = new A(); + }); + })(); + } + expect_stdout: "PASS" + node_version: ">=12" +} + +issue_5082_2_static: { + options = { + inline: true, + passes: 2, + reduce_funcs: true, + reduce_vars: true, + unused: true, + } + input: { + "use strict"; + (function() { + class A { + p = console.log("PASS"); + q() {} + } + class B { + static P = new A(); + } + })(); + } + expect: { + "use strict"; + void new class { + p = console.log("PASS"); + q() {} + }(); + } + expect_stdout: "PASS" + node_version: ">=12" +} + +issue_5142: { + options = { + evaluate: true, + merge_vars: true, + reduce_vars: true, + toplevel: true, + } + input: { + var a = 0, b; + if (++a) + new class { + p = b = null; + constructor(c) { + console.log(c ? "FAIL" : "PASS"); + } + }(b, a); + } + expect: { + var a = 0, b; + if (++a) + new class { + p = b = null; + constructor(c) { + console.log(c ? "FAIL" : "PASS"); + } + }(b, 1); + } + expect_stdout: "PASS" + node_version: ">=12" +} + +issue_5294_1: { + options = { + reduce_vars: true, + side_effects: true, + toplevel: true, + } + input: { + (class A { + static p = console.log(typeof A); + }); + } + expect: { + (class A { + static c = console.log(typeof A); + }); + } + expect_stdout: "function" + node_version: ">=12" +} + +issue_5294_2: { + options = { + reduce_vars: true, + side_effects: true, + toplevel: true, + } + input: { + class A { + static p = console.log(typeof A); + } + } + expect: { + class A { + static p = console.log(typeof A); + } + } + expect_stdout: "function" + node_version: ">=12" +} + +issue_5294_3: { + options = { + reduce_vars: true, + toplevel: true, + unused: true, + } + input: { + var a = this; + (class A { + static p = console.log(a === A ? "FAIL" : "PASS"); + }); + } + expect: { + var a = this; + (class A { + static p = console.log(a === A ? "FAIL" : "PASS"); + }); + } + expect_stdout: "PASS" + node_version: ">=12" +} + +issue_5294_4: { + options = { + reduce_vars: true, + toplevel: true, + unused: true, + } + input: { + (class A { + static p = function() { + var a = this; + console.log(a === A ? "FAIL" : "PASS"); + }(); + }); + } + expect: { + (class A { + static p = function() { + console.log(this === A ? "FAIL" : "PASS"); + }(); + }); + } + expect_stdout: "PASS" + node_version: ">=12" +} + +issue_5322: { + options = { + toplevel: true, + unused: true, + } + input: { + var a = 41; + class A { + static p() { + console.log(++a); + } + static q = this.p(); + } + } + expect: { + var a = 41; + (class { + static p() { + console.log(++a); + } + static q = this.p(); + }); + } + expect_stdout: "42" + node_version: ">=12" +} + +issue_5352: { + options = { + merge_vars: true, + } + input: { + function f(a) { + var b; + new class { + [b = console.log(a)] = b; + }(a.p); + } + f("PASS"); + } + expect: { + function f(a) { + var b; + new class { + [b = console.log(a)] = b; + }(a.p); + } + f("PASS"); + } + expect_stdout: "PASS" + node_version: ">=12" +} + +issue_5387: { + options = { + properties: true, + } + input: { + "use strict"; + (function(a) { + try { + class A extends a {} + } catch (e) { + console.log("PASS"); + } + })({ + f() { + return this; + } + }.f); + } + expect: { + "use strict"; + (function(a) { + try { + class A extends a {} + } catch (e) { + console.log("PASS"); + } + })({ + f() { + return this; + } + }.f); + } + expect_stdout: "PASS" + node_version: ">=4" +} + +issue_5389_1: { + options = { + collapse_vars: true, + toplevel: true, + } + input: { + function log(m, n) { + console.log(m, n); + } + var a = log; + class A { + [a = "FAIL"] = a = "PASS"; + } + var b = new A(); + log(a, b.FAIL); + } + expect: { + function log(m, n) { + console.log(m, n); + } + var a = log; + class A { + [a = "FAIL"] = a = "PASS"; + } + var b = new A(); + log(a, b.FAIL); + } + expect_stdout: "PASS PASS" + node_version: ">=12" +} + +issue_5389_2: { + options = { + collapse_vars: true, + toplevel: true, + } + input: { + function log(m, n) { + console.log(m, n); + } + var a = log; + var A = class { + [a = "FAIL"] = a = "PASS"; + }; + var b = new A(); + log(a, b.FAIL); + } + expect: { + function log(m, n) { + console.log(m, n); + } + var a = log; + var A; + var b = new class { + [a = "FAIL"] = a = "PASS"; + }(); + log(a, b.FAIL); + } + expect_stdout: "PASS PASS" + node_version: ">=12" +} + +issue_5436: { + options = { + merge_vars: true, + } + input: { + function f(a) { + class A { + p = a; + } + var b = "FAIL"; + A == b && b(); + return new A(); + } + console.log(f("PASS").p); + } + expect: { + function f(a) { + class A { + p = a; + } + var b = "FAIL"; + A == b && b(); + return new A(); + } + console.log(f("PASS").p); + } + expect_stdout: "PASS" + node_version: ">=12" +} + +issue_5481: { + options = { + collapse_vars: true, + } + input: { + "use strict"; + var a = "FAIL 1", log = console.log; + try { + a = "PASS"; + (class extends 42 {}); + log("FAIL 2", a); + } catch (e) { + log(a); + } + } + expect: { + "use strict"; + var a = "FAIL 1", log = console.log; + try { + a = "PASS"; + (class extends 42 {}); + log("FAIL 2", a); + } catch (e) { + log(a); + } + } + expect_stdout: "PASS" + node_version: ">=4" +} + +issue_5489: { + options = { + side_effects: true, + } + input: { + (class { + [console.log("foo")]; + static { + console.log("bar"); + } + static [console.log("baz")]() {} + }); + } + expect: { + (class { + [(console.log("foo"), console.log("baz"))]; + static { + console.log("bar"); + } + }); + } + expect_stdout: [ + "foo", + "baz", + "bar", + ] + node_version: ">=16" +} + +issue_5489_strict: { + options = { + side_effects: true, + } + input: { + "use strict"; + (class { + [console.log("foo")]; + static { + console.log("bar"); + } + static [console.log("baz")]() {} + }); + } + expect: { + "use strict"; + console.log("foo"), + console.log("baz"), + (() => (() => { + console.log("bar"); + })())(); + } + expect_stdout: [ + "foo", + "baz", + "bar", + ] + node_version: ">=16" +} + +issue_5502: { + options = { + collapse_vars: true, + } + input: { + "use strict"; + var a = "FAIL"; + class A { + static p = a; + [a = "PASS"]; + } + try { + b++; + } finally { + var a, b = 42; + } + console.log(a, b); + } + expect: { + "use strict"; + var a = "FAIL"; + class A { + static p = a; + [a = "PASS"]; + } + try { + b++; + } finally { + var a, b = 42; + } + console.log(a, b); + } + expect_stdout: "PASS 42" + node_version: ">=12" +} + +issue_5504: { + options = { + collapse_vars: true, + } + input: { + "use strict"; + var a; + console.log((a = 42, class { + static p; + }).p); + } + expect: { + "use strict"; + var a; + console.log((a = 42, class { + static p; + }).p); + } + expect_stdout: "undefined" + node_version: ">=12" +} + +issue_5512: { + options = { + collapse_vars: true, + } + input: { + "use strict"; + a = "PASS"; + class A { + static { + console.log(a); + } + static p = "PASS"; + } + var a; + } + expect: { + "use strict"; + a = "PASS"; + class A { + static { + console.log(a); + } + static p = "PASS"; + } + var a; + } + expect_stdout: "PASS" + node_version: ">=16" +} + +issue_5531_1: { + options = { + inline: true, + toplevel: true, + } + input: { + class A { + p = function() { + var a = function f() { + if (!a) + console.log("foo"); + return 42; + }(a++); + }(); + } + new A(); + new A(); + } + expect: { + class A { + p = function() { + var a = function f() { + if (!a) + console.log("foo"); + return 42; + }(a++); + }(); + } + new A(); + new A(); + } + expect_stdout: [ + "foo", + "foo", + ] + node_version: ">=12" +} + +issue_5531_2: { + options = { + inline: true, + toplevel: true, + } + input: { + class A { + static p = function() { + var a = function f() { + if (!a) + console.log("foo"); + return 42; + }(a++); + }(); + } + new A(); + new A(); + } + expect: { + class A { + static p = (a = function f() { + if (!a) + console.log("foo"); + return 42; + }(a++), void 0); + } + var a; + new A(); + new A(); + } + expect_stdout: "foo" + node_version: ">=12" +} + +issue_5531_3: { + options = { + inline: true, + } + input: { + class A { + static { + (function() { + var a = function f() { + if (!a) + console.log("foo"); + return 42; + }(a++); + })(); + } + } + new A(); + new A(); + } + expect: { + class A { + static { + a = function f() { + if (!a) + console.log("foo"); + return 42; + }(a++), + void 0; + var a; + } + } + new A(); + new A(); + } + expect_stdout: "foo" + node_version: ">=16" +} + +issue_5662: { + options = { + inline: true, + reduce_vars: true, + } + input: { + console.log(new (function() { + var g = function(a) { + return a; + }; + return class { + h(b) { + return g(b); + } + }; + }())().h("PASS")); + } + expect: { + console.log(new (function() { + var g = function(a) { + return a; + }; + return class { + h(b) { + return g(b); + } + }; + }())().h("PASS")); + } + expect_stdout: "PASS" + node_version: ">=6" +} + +issue_5682_class_key: { + mangle = { + properties: true, + } + input: { + "use strict"; + function f(a) { + return "foo" in a; + } + class A { + foo() {} + } + console.log(f(new A()) ? "PASS" : "FAIL"); + } + expect: { + "use strict"; + function f(o) { + return "o" in o; + } + class A { + o() {} + } + console.log(f(new A()) ? "PASS" : "FAIL"); + } + expect_stdout: "PASS" + node_version: ">=4" +} + +issue_5682_class_key_computed: { + mangle = { + properties: true, + } + input: { + "use strict"; + function f(a) { + return "foo" in a; + } + class A { + ["foo"]() {} + } + console.log(f(new A()) ? "PASS" : "FAIL"); + } + expect: { + "use strict"; + function f(o) { + return "o" in o; + } + class A { + ["o"]() {} + } + console.log(f(new A()) ? "PASS" : "FAIL"); + } + expect_stdout: "PASS" + node_version: ">=4" +} + +issue_5724: { + options = { + arrows: true, + inline: true, + keep_fargs: true, + toplevel: true, + unused: true, + } + input: { + "use strict"; + class A { + static P = function(a) { + console.log(a, a); + }(a); + } + } + expect: { + "use strict"; + (function(a) { + console.log(a, a); + })(a); + } + expect_stdout: ReferenceError("a is not defined") + node_version: ">=12" +} + +issue_5735_1: { + options = { + inline: true, + } + input: { + console.log(typeof function(a) { + return class { + static P = { ...a }; + }; + }([ 42..p ] = [])); + } + expect: { + console.log(typeof function(a) { + return class { + static P = { ...a }; + }; + }([ 42..p ] = [])); + } + expect_stdout: "function" + node_version: ">=12" +} + +issue_5735_2: { + options = { + inline: true, + } + input: { + console.log(typeof function(a) { + return class { + p = a; + }; + }(console.log("PASS"))); + } + expect: { + console.log(typeof function(a) { + return class { + p = a; + }; + }(console.log("PASS"))); + } + expect_stdout: [ + "PASS", + "function", + ] + node_version: ">=12" +} + +issue_5747_1: { + options = { + collapse_vars: true, + } + input: { + "use strict"; + (async function() { + var a = await 42; + class A { + static P = a && console.log(typeof this); + } + })(); + } + expect: { + "use strict"; + (async function() { + var a = await 42; + class A { + static P = a && console.log(typeof this); + } + })(); + } + expect_stdout: "function" + node_version: ">=12" +} + +issue_5747_2: { + options = { + collapse_vars: true, + } + input: { + "use strict"; + (async function() { + var a = await 42; + class A { + static { + a && console.log(typeof this); + } + } + })(); + } + expect: { + "use strict"; + (async function() { + var a = await 42; + class A { + static { + a && console.log(typeof this); + } + } + })(); + } + expect_stdout: "function" + node_version: ">=16" +} diff --git a/test/compress/collapse_vars.js b/test/compress/collapse_vars.js new file mode 100644 index 00000000000..45d26ea3131 --- /dev/null +++ b/test/compress/collapse_vars.js @@ -0,0 +1,10174 @@ +collapse_vars_side_effects_1: { + options = { + booleans: true, + collapse_vars: true, + comparisons: true, + conditionals: true, + dead_code: true, + evaluate: true, + hoist_funs: true, + if_return: true, + join_vars: true, + keep_fargs: true, + loops: true, + properties: true, + reduce_funcs: true, + reduce_vars: true, + sequences: true, + side_effects: true, + unused: true, + } + input: { + function f1() { + var e = 7; + var s = "abcdef"; + var i = 2; + var log = console.log.bind(console); + var x = s.charAt(i++); + var y = s.charAt(i++); + var z = s.charAt(i++); + log(x, y, z, e); + } + function f2() { + var e = 7; + var log = console.log.bind(console); + var s = "abcdef"; + var i = 2; + var x = s.charAt(i++); + var y = s.charAt(i++); + var z = s.charAt(i++); + log(x, i, y, z, e); + } + function f3() { + var e = 7; + var s = "abcdef"; + var i = 2; + var log = console.log.bind(console); + var x = s.charAt(i++); + var y = s.charAt(i++); + var z = s.charAt(i++); + log(x, z, y, e); + } + function f4() { + var log = console.log.bind(console), + i = 10, + x = i += 2, + y = i += 3, + z = i += 4; + log(x, z, y, i); + } + f1(), f2(), f3(), f4(); + } + expect: { + function f1() { + var s = "abcdef", i = 2; + console.log.bind(console)(s.charAt(i++), s.charAt(+i), s.charAt(4), 7); + } + function f2() { + var s = "abcdef", i = 2; + console.log.bind(console)(s.charAt(i++), 5, s.charAt(i++), s.charAt(+i), 7); + } + function f3() { + var s = "abcdef", + i = 2, + log = console.log.bind(console), + x = s.charAt(i++), + y = s.charAt(+i); + log(x, s.charAt(4), y, 7); + } + function f4() { + var i = 10; + i += 2, + i += 3, + i += 4; + console.log.bind(console)(12, 19, 15, 19); + } + f1(), f2(), f3(), f4(); + } + expect_stdout: true +} + +collapse_vars_side_effects_2: { + options = { + booleans: true, + collapse_vars: true, + comparisons: true, + conditionals: true, + dead_code: true, + evaluate: true, + hoist_funs: true, + if_return: true, + join_vars: true, + keep_fargs: true, + loops: true, + properties: true, + sequences: true, + side_effects: true, + unused: true, + } + input: { + function fn(x) { return console.log(x), x; } + + function p1() { var a = foo(), b = bar(), c = baz(); return a + b + c; } + function p2() { var a = foo(), c = bar(), b = baz(); return a + b + c; } + function p3() { var b = foo(), a = bar(), c = baz(); return a + b + c; } + function p4() { var b = foo(), c = bar(), a = baz(); return a + b + c; } + function p5() { var c = foo(), a = bar(), b = baz(); return a + b + c; } + function p6() { var c = foo(), b = bar(), a = baz(); return a + b + c; } + + function q1() { var a = foo(), b = bar(), c = baz(); return fn(a + b + c); } + function q2() { var a = foo(), c = bar(), b = baz(); return fn(a + b + c); } + function q3() { var b = foo(), a = bar(), c = baz(); return fn(a + b + c); } + function q4() { var b = foo(), c = bar(), a = baz(); return fn(a + b + c); } + function q5() { var c = foo(), a = bar(), b = baz(); return fn(a + b + c); } + function q6() { var c = foo(), b = bar(), a = baz(); return fn(a + b + c); } + + function r1() { var a = foo(), b = bar(), c = baz(); return fn(a) + fn(b) + fn(c); } + function r2() { var a = foo(), c = bar(), b = baz(); return fn(a) + fn(b) + fn(c); } + function r3() { var b = foo(), a = bar(), c = baz(); return fn(a) + fn(b) + fn(c); } + function r4() { var b = foo(), c = bar(), a = baz(); return fn(a) + fn(b) + fn(c); } + function r5() { var c = foo(), a = bar(), b = baz(); return fn(a) + fn(b) + fn(c); } + function r6() { var c = foo(), b = bar(), a = baz(); return fn(a) + fn(b) + fn(c); } + + function s1() { var a = foo(), b = bar(), c = baz(); return g(a + b + c); } + function s6() { var c = foo(), b = bar(), a = baz(); return g(a + b + c); } + + function t1() { var a = foo(), b = bar(), c = baz(); return g(a) + g(b) + g(c); } + function t6() { var c = foo(), b = bar(), a = baz(); return g(a) + g(b) + g(c); } + } + expect: { + function fn(x) { return console.log(x), x; } + + function p1() { return foo() + bar() + baz(); } + function p2() { var a = foo(), c = bar(); return a + baz() + c; } + function p3() { var b = foo(); return bar() + b + baz(); } + function p4() { var b = foo(), c = bar(); return baz() + b + c; } + function p5() { var c = foo(); return bar() + baz() + c; } + function p6() { var c = foo(), b = bar(); return baz() + b + c; } + + function q1() { return fn(foo() + bar() + baz()); } + function q2() { var a = foo(), c = bar(); return fn(a + baz() + c); } + function q3() { var b = foo(); return fn(bar() + b + baz()); } + function q4() { var b = foo(), c = bar(); return fn(baz() + b + c); } + function q5() { var c = foo(); return fn(bar() + baz() + c); } + function q6() { var c = foo(), b = bar(); return fn(baz() + b + c); } + + function r1() { var a = foo(), b = bar(), c = baz(); return fn(a) + fn(b) + fn(c); } + function r2() { var a = foo(), c = bar(), b = baz(); return fn(a) + fn(b) + fn(c); } + function r3() { var b = foo(), a = bar(), c = baz(); return fn(a) + fn(b) + fn(c); } + function r4() { var b = foo(), c = bar(); return fn(baz()) + fn(b) + fn(c); } + function r5() { var c = foo(), a = bar(), b = baz(); return fn(a) + fn(b) + fn(c); } + function r6() { var c = foo(), b = bar(); return fn(baz()) + fn(b) + fn(c); } + + function s1() { var a = foo(), b = bar(), c = baz(); return g(a + b + c); } + function s6() { var c = foo(), b = bar(), a = baz(); return g(a + b + c); } + + function t1() { var a = foo(), b = bar(), c = baz(); return g(a) + g(b) + g(c); } + function t6() { var c = foo(), b = bar(), a = baz(); return g(a) + g(b) + g(c); } + } +} + +collapse_vars_issue_721: { + options = { + booleans: true, + collapse_vars: true, + comparisons: true, + conditionals: true, + dead_code: true, + evaluate: true, + hoist_funs: true, + if_return: true, + join_vars: true, + keep_fargs: true, + loops: true, + passes: 2, + properties: true, + reduce_funcs: true, + reduce_vars: true, + sequences: true, + side_effects: true, + unused: true, + } + input: { + define(["require", "exports", 'handlebars'], function(require, exports, hb) { + var win = window; + var _hb = win.Handlebars = hb; + return _hb; + }); + def(function(hb) { + var win = window; + var prop = 'Handlebars'; + var _hb = win[prop] = hb; + return _hb; + }); + def(function(hb) { + var prop = 'Handlebars'; + var win = window; + var _hb = win[prop] = hb; + return _hb; + }); + def(function(hb) { + var prop = 'Handlebars'; + var win = g(); + var _hb = win[prop] = hb; + return _hb; + }); + def(function(hb) { + var prop = g1(); + var win = g2(); + var _hb = win[prop] = hb; + return _hb; + }); + def(function(hb) { + var win = g2(); + var prop = g1(); + var _hb = win[prop] = hb; + return _hb; + }); + } + expect: { + define([ "require", "exports", "handlebars" ], function(require, exports, hb) { + return window.Handlebars = hb; + }), + def(function(hb) { + return window.Handlebars = hb; + }), + def(function(hb) { + return window.Handlebars = hb; + }), + def(function(hb) { + return g().Handlebars = hb; + }), + def(function(hb) { + var prop = g1(); + return g2()[prop] = hb; + }), + def(function(hb) { + return g2()[g1()] = hb; + }); + } +} + +collapse_vars_properties: { + options = { + booleans: true, + collapse_vars: true, + comparisons: true, + conditionals: true, + dead_code: true, + evaluate: true, + hoist_funs: true, + if_return: true, + join_vars: true, + keep_fargs: true, + loops: true, + properties: true, + reduce_funcs: true, + reduce_vars: true, + sequences: true, + side_effects: true, + unused: true, + } + input: { + function f1(obj) { + var prop = 'LiteralProperty'; + return !!-+obj[prop]; + } + function f2(obj) { + var prop1 = 'One'; + var prop2 = 'Two'; + return ~!!-+obj[prop1 + prop2]; + } + } + expect: { + function f1(obj) { + return !!-+obj.LiteralProperty; + } + function f2(obj) { + return ~!!-+obj.OneTwo; + } + } +} + +collapse_vars_if: { + options = { + booleans: true, + collapse_vars: true, + comparisons: true, + conditionals: true, + dead_code: true, + evaluate: true, + hoist_funs: true, + if_return: true, + join_vars: true, + keep_fargs: true, + loops: true, + properties: true, + reduce_funcs: true, + reduce_vars: true, + sequences: true, + side_effects: true, + unused: true, + } + input: { + function f1() { + var not_used = sideeffect(), x = g1 + g2; + var y = x / 4, z = 'Bar' + y; + if ('x' != z) { return g9; } + else return g5; + } + function f2() { + var x = g1 + g2, not_used = sideeffect(); + var y = x / 4 + var z = 'Bar' + y; + if ('x' != z) { return g9; } + else return g5; + } + function f3(x) { + if (x) { + var a = 1; + return a; + } + else { + var b = 2; + return b; + } + } + } + expect: { + function f1() { + sideeffect(); + return "x" != "Bar" + (g1 + g2) / 4 ? g9 : g5; + } + function f2() { + var x = g1 + g2; + sideeffect(); + return "x" != "Bar" + x / 4 ? g9 : g5; + } + function f3(x) { + return x ? 1 : 2; + } + } +} + +collapse_vars_while: { + options = { + booleans: true, + collapse_vars: true, + comparisons: true, + conditionals: true, + dead_code: true, + evaluate: true, + hoist_funs: true, + if_return: true, + join_vars: true, + keep_fargs: true, + loops: false, + properties: true, + reduce_funcs: true, + reduce_vars: true, + sequences: true, + side_effects: true, + unused: true, + } + input: { + function f1(y) { + // Neither the non-constant while condition `c` will be + // replaced, nor the non-constant `x` in the body. + var x = y, c = 3 - y; + while (c) { return x; } + var z = y * y; + return z; + } + function f2(y) { + // The constant `x` will be replaced in the while body. + var x = 7; + while (y) { return x; } + var z = y * y; + return z; + } + function f3(y) { + // The non-constant `n` will not be replaced in the while body. + var n = 5 - y; + while (y) { return n; } + var z = y * y; + return z; + } + } + expect: { + function f1(y) { + var x = y, c = 3 - y; + while (c) return x; + return y * y; + } + function f2(y) { + while (y) return 7; + return y * y + } + function f3(y) { + var n = 5 - y; + while (y) return n; + return y * y; + } + } +} + +collapse_vars_do_while: { + options = { + booleans: false, + collapse_vars: true, + comparisons: true, + conditionals: true, + dead_code: true, + evaluate: true, + hoist_funs: true, + if_return: true, + join_vars: true, + keep_fargs: true, + loops: false, + properties: true, + sequences: true, + side_effects: true, + unused: "keep_assign", + } + input: { + function f1(y) { + // The constant do-while condition `c` will not be replaced. + var c = 9; + do {} while (c === 77); + } + function f2(y) { + // The non-constant do-while condition `c` will not be replaced. + var c = 5 - y; + do { } while (c); + } + function f3(y) { + // The constant `x` will be replaced in the do loop body. + function fn(n) { console.log(n); } + var a = 2, x = 7; + do { + fn(a = x); + break; + } while (y); + } + function f4(y) { + // The non-constant `a` will not be replaced in the do loop body. + var a = y / 4; + do { + return a; + } while (y); + } + function f5(y) { + function p(x) { console.log(x); } + do { + // The non-constant `a` will be replaced in p(a) + // because it is declared in same block. + var a = y - 3; + p(a); + } while (--y); + } + } + expect: { + function f1(y) { + var c = 9; + do ; while (77 === c); + } + function f2(y) { + var c = 5 - y; + do ; while (c); + } + function f3(y) { + function fn(n) { console.log(n); } + var a = 2, x = 7; + do { + fn(a = x); + break; + } while (y); + } + function f4(y) { + var a = y / 4; + do + return a; + while (y); + } + function f5(y) { + function p(x) { console.log(x); } + do { + p(y - 3); + } while (--y); + } + } +} + +collapse_vars_do_while_drop_assign: { + options = { + booleans: false, + collapse_vars: true, + comparisons: true, + conditionals: true, + dead_code: true, + evaluate: true, + hoist_funs: true, + if_return: true, + join_vars: true, + keep_fargs: true, + loops: false, + properties: true, + sequences: true, + side_effects: true, + unused: true, + } + input: { + function f1(y) { + // The constant do-while condition `c` will be not replaced. + var c = 9; + do {} while (c === 77); + } + function f2(y) { + // The non-constant do-while condition `c` will not be replaced. + var c = 5 - y; + do { } while (c); + } + function f3(y) { + // The constant `x` will be replaced in the do loop body. + function fn(n) { console.log(n); } + var a = 2, x = 7; + do { + fn(a = x); + break; + } while (y); + } + function f4(y) { + // The non-constant `a` will not be replaced in the do loop body. + var a = y / 4; + do { + return a; + } while (y); + } + function f5(y) { + function p(x) { console.log(x); } + do { + // The non-constant `a` will be replaced in p(a) + // because it is declared in same block. + var a = y - 3; + p(a); + } while (--y); + } + } + expect: { + function f1(y) { + var c = 9; + do ; while (77 === c); + } + function f2(y) { + var c = 5 - y; + do ; while (c); + } + function f3(y) { + function fn(n) { console.log(n); } + var x = 7; + do { + fn(x); + break; + } while (y); + } + function f4(y) { + var a = y / 4; + do + return a; + while (y); + } + function f5(y) { + function p(x) { console.log(x); } + do { + p(y - 3); + } while (--y); + } + } +} + +collapse_vars_seq: { + options = { + booleans: true, + collapse_vars: true, + comparisons: true, + conditionals: true, + dead_code: true, + evaluate: true, + hoist_funs: true, + if_return: true, + join_vars: true, + keep_fargs: true, + loops: true, + properties: true, + sequences: true, + side_effects: true, + unused: true, + } + input: { + var f1 = function(x, y) { + var a, b, r = x + y, q = r * r, z = q - r; + a = z, b = 7; + return a + b; + }; + console.log(f1(1, 2)); + } + expect: { + var f1 = function(x, y) { + var r = x + y; + return r * r - r + 7; + }; + console.log(f1(1, 2)); + } + expect_stdout: "13" +} + +collapse_vars_throw: { + options = { + booleans: true, + collapse_vars: true, + comparisons: true, + conditionals: true, + dead_code: true, + evaluate: true, + hoist_funs: true, + if_return: true, + join_vars: true, + keep_fargs: true, + loops: true, + properties: true, + sequences: true, + side_effects: true, + unused: true, + } + input: { + var f1 = function(x, y) { + var a, b, r = x + y, q = r * r, z = q - r; + a = z, b = 7; + throw a + b; + }; + try { + f1(1, 2); + } catch (e) { + console.log(e); + } + } + expect: { + var f1 = function(x, y) { + var r = x + y; + throw r * r - r + 7; + }; + try { + f1(1, 2); + } catch (e) { + console.log(e); + } + } + expect_stdout: "13" +} + +collapse_vars_switch_1: { + options = { + booleans: true, + collapse_vars: true, + comparisons: true, + conditionals: true, + dead_code: true, + evaluate: true, + hoist_funs: true, + if_return: true, + join_vars: true, + keep_fargs: true, + loops: true, + properties: true, + sequences: true, + side_effects: true, + unused: true, + } + input: { + function f1() { + var not_used = sideeffect(), x = g1 + g2; + var y = x / 4, z = 'Bar' + y; + switch (z) { case 0: return g9; } + } + function f2() { + var x = g1 + g2, not_used = sideeffect(); + var y = x / 4 + var z = 'Bar' + y; + switch (z) { case 0: return g9; } + } + function f3(x) { + switch(x) { case 1: var a = 3 - x; return a; } + } + } + expect: { + function f1() { + sideeffect(); + switch ("Bar" + (g1 + g2) / 4) { case 0: return g9 } + } + function f2() { + var x = g1 + g2; + sideeffect(); + switch ("Bar" + x / 4) { case 0: return g9 } + } + function f3(x) { + // verify no extraneous semicolon in case block before return + // when the var definition was eliminated + switch(x) { case 1: return 3 - x; } + } + } +} + +collapse_vars_switch_2: { + options = { + collapse_vars: true, + } + input: { + var c = 0; + (function(b) { + switch (b && [ b = 0, (c++, 0) ]) { + case c = 1 + c: + } + })(); + console.log(c); + } + expect: { + var c = 0; + (function(b) { + switch (b && [ b = 0, (c++, 0) ]) { + case c = 1 + c: + } + })(); + console.log(c); + } + expect_stdout: "1" +} + +collapse_vars_assignment: { + options = { + booleans: true, + collapse_vars: true, + comparisons: true, + conditionals: true, + evaluate: true, + hoist_funs: true, + if_return: true, + join_vars: true, + keep_fargs: true, + loops: true, + properties: true, + sequences: true, + side_effects: true, + unused: true, + } + input: { + function log(x) { return console.log(x), x; } + function f0(c) { + var a = 3 / c; + return a = a; + } + function f1(c) { + var a = 3 / c; + var b = 1 - a; + return b; + } + function f2(c) { + var a = 3 / c; + var b = a - 7; + return log(c = b); + } + function f3(c) { + var a = 3 / c; + var b = a - 7; + return log(c |= b); + } + function f4(c) { + var a = 3 / c; + var b = 2; + return log(b += a); + } + function f5(c) { + var b = 2; + var a = 3 / c; + return log(b += a); + } + function f6(c) { + var b = g(); + var a = 3 / c; + return log(b += a); + } + } + expect: { + function log(x) { return console.log(x), x; } + function f0(c) { + return 3 / c; + } + function f1(c) { + return 1 - 3 / c; + } + function f2(c) { + return log(c = 3 / c - 7); + } + function f3(c) { + return log(c |= 3 / c - 7); + } + function f4(c) { + var b = 2; + return log(b += 3 / c); + } + function f5(c) { + var b = 2; + return log(b += 3 / c); + } + function f6(c) { + var b = g(); + return log(b += 3 / c); + } + } +} + +collapse_vars_lvalues: { + options = { + booleans: true, + collapse_vars: true, + comparisons: true, + conditionals: true, + evaluate: true, + hoist_funs: true, + if_return: true, + join_vars: true, + keep_fargs: true, + loops: true, + properties: true, + sequences: true, + side_effects: true, + unused: "keep_assign", + } + input: { + function f0(x) { var i = ++x; return x += i; } + function f1(x) { var a = (x -= 3); return x += a; } + function f2(x) { var z = x, a = ++z; return z += a; } + function f3(x) { var a = (x -= 3), b = x + a; return b; } + function f4(x) { var a = (x -= 3); return x + a; } + function f5(x) { var w = e1(), v = e2(), c = v = --x, b = w = x; return b - c; } + function f6(x) { var w = e1(), v = e2(), c = v = --x, b = w = x; return c - b; } + function f7(x) { var w = e1(), v = e2(), c = v - x, b = w = x; return b - c; } + function f8(x) { var w = e1(), v = e2(), b = w = x, c = v - x; return b - c; } + function f9(x) { var w = e1(), v = e2(), b = w = x, c = v - x; return c - b; } + } + expect: { + function f0(x) { var i = ++x; return x += i; } + function f1(x) { var a = (x -= 3); return x += a; } + function f2(x) { var z = x, a = ++z; return z += a; } + function f3(x) { var a = (x -= 3); return x + a; } + function f4(x) { var a = (x -= 3); return x + a; } + function f5(x) { var w = e1(), v = e2(), c = v = --x; return (w = x) - c; } + function f6(x) { var w = e1(), v = e2(); return (v = --x) - (w = x); } + function f7(x) { var w = e1(); return (w = x) - (e2() - x); } + function f8(x) { var w = e1(); return (w = x) - (e2() - x); } + function f9(x) { var w = e1(); return e2() - x - (w = x); } + } +} + +collapse_vars_lvalues_drop_assign: { + options = { + booleans: true, + collapse_vars: true, + comparisons: true, + conditionals: true, + evaluate: true, + hoist_funs: true, + if_return: true, + join_vars: true, + keep_fargs: true, + loops: true, + passes: 3, + properties: true, + sequences: true, + side_effects: true, + unused: true, + } + input: { + function f0(x) { var i = ++x; return x += i; } + function f1(x) { var a = (x -= 3); return x += a; } + function f2(x) { var z = x, a = ++z; return z += a; } + function f3(x) { var a = (x -= 3), b = x + a; return b; } + function f4(x) { var a = (x -= 3); return x + a; } + function f5(x) { var w = e1(), v = e2(), c = v = --x, b = w = x; return b - c; } + function f6(x) { var w = e1(), v = e2(), c = v = --x, b = w = x; return c - b; } + function f7(x) { var w = e1(), v = e2(), c = v - x, b = w = x; return b - c; } + function f8(x) { var w = e1(), v = e2(), b = w = x, c = v - x; return b - c; } + function f9(x) { var w = e1(), v = e2(), b = w = x, c = v - x; return c - b; } + } + expect: { + function f0(x) { var i = ++x; return x += i; } + function f1(x) { var a = (x -= 3); return x += a; } + function f2(x) { var z = x, a = ++z; return z += a; } + function f3(x) { var a = (x -= 3); return x + a; } + function f4(x) { var a = (x -= 3); return x + a; } + function f5(x) { e1(), e2(); var c = --x; return x - c; } + function f6(x) { return e1(), e2(), --x - x; } + function f7(x) { return e1(), x - (e2() - x); } + function f8(x) { return e1(), x - (e2() - x); } + function f9(x) { return e1(), e2() - x - x; } + } +} + +collapse_vars_misc: { + options = { + booleans: true, + collapse_vars: true, + comparisons: true, + conditionals: true, + evaluate: true, + hoist_funs: true, + if_return: true, + join_vars: true, + keep_fargs: true, + loops: true, + properties: true, + reduce_funcs: true, + reduce_vars: true, + sequences: true, + side_effects: true, + unused: true, + } + input: { + function f0(o, a, h) { + var b = 3 - a; + var obj = o; + var seven = 7; + var prop = 'run'; + var t = obj[prop](b)[seven] = h; + return t; + } + function f1(x) { var y = 5 - x; return y; } + function f2(x) { var z = foo(), y = z / (5 - x); return y; } + function f3(x) { var z = foo(), y = (5 - x) / z; return y; } + function f4(x) { var z = foo(), y = (5 - u) / z; return y; } + function f5(x) { var z = foo(), y = (5 - window.x) / z; return y; } + function f6() { var b = window.a * window.z; return b && zap(); } + function f7() { var b = window.a * window.z; return b + b; } + function f8() { var b = window.a * window.z; var c = b + 5; return b + c; } + function f9() { var b = window.a * window.z; return bar() || b; } + function f10(x) { var a = 5, b = 3; return a += b; } + function f11(x) { var a = 5, b = 3; return a += --b; } + } + expect: { + function f0(o, a, h) { + return o.run(3 - a)[7] = h; + } + function f1(x) { return 5 - x } + function f2(x) { return foo() / (5 - x) } + function f3(x) { return (5 - x) / foo() } + function f4(x) { var z = foo(); return (5 - u) / z } + function f5(x) { var z = foo(); return (5 - window.x) / z } + function f6() { return window.a * window.z && zap() } + function f7() { var b = window.a * window.z; return b + b } + function f8() { var b = window.a * window.z; return b + (5 + b) } + function f9() { var b = window.a * window.z; return bar() || b } + function f10(x) { return 8; } + function f11(x) { return 7; } + } +} + +collapse_vars_self_reference: { + options = { + booleans: true, + collapse_vars: true, + comparisons: true, + conditionals: true, + dead_code: true, + evaluate: true, + hoist_funs: true, + if_return: true, + join_vars: true, + keep_fargs: true, + loops: true, + properties: true, + sequences: true, + side_effects: true, + unused: false, + } + input: { + // avoid bug in self-referential declaration. + function f1() { + var self = { + inner: function() { return self; } + }; + } + function f2() { + var self = { inner: self }; + } + } + expect: { + // note: `unused` option is false + function f1() { + var self = { + inner: function() { return self } + }; + } + function f2() { + var self = { inner: self }; + } + } +} + +collapse_vars_repeated: { + options = { + booleans: true, + collapse_vars: true, + comparisons: true, + conditionals: true, + dead_code: true, + evaluate: true, + hoist_funs: true, + if_return: true, + join_vars: true, + keep_fargs: true, + loops: true, + properties: true, + reduce_funcs: true, + reduce_vars: true, + sequences: true, + side_effects: true, + unused: true, + } + input: { + function f1() { + var dummy = 3, a = 5, unused = 2, a = 1, a = 3; + return -a; + } + function f2(x) { + var a = 3, a = x; + return a; + } + (function(x) { + var a = "GOOD" + x, e = "BAD", k = "!", e = a; + console.log(e + k); + })("!"), + (function(x) { + var a = "GOOD" + x, e = "BAD" + x, k = "!", e = a; + console.log(e + k); + })("!"); + } + expect: { + function f1() { + return -3; + } + function f2(x) { + return x; + } + (function(x) { + console.log("GOOD!!"); + })(), + (function(x) { + console.log("GOOD!!"); + })(); + } + expect_stdout: true +} + +collapse_vars_closures: { + options = { + booleans: true, + collapse_vars: true, + comparisons: true, + conditionals: true, + dead_code: true, + evaluate: true, + hoist_funs: true, + if_return: true, + join_vars: true, + keep_fargs: true, + loops: true, + properties: true, + reduce_funcs: true, + reduce_vars: true, + sequences: true, + side_effects: true, + unused: true, + } + input: { + function constant_vars_can_be_replaced_in_any_scope() { + var outer = 3; + return function() { return outer; } + } + function non_constant_vars_can_only_be_replace_in_same_scope(x) { + var outer = x; + return function() { return outer; } + } + } + expect: { + function constant_vars_can_be_replaced_in_any_scope() { + return function() { return 3 } + } + function non_constant_vars_can_only_be_replace_in_same_scope(x) { + var outer = x + return function() { return outer } + } + } +} + +collapse_vars_unary: { + options = { + booleans: true, + collapse_vars: true, + comparisons: true, + conditionals: true, + dead_code: true, + evaluate: true, + hoist_funs: true, + if_return: true, + join_vars: true, + keep_fargs: true, + loops: true, + properties: true, + sequences: true, + side_effects: true, + unused: true, + } + input: { + function f0(o, p) { + var x = o[p]; + return delete x; + } + function f1(n) { + var k = !!n; + return n > +k; + } + function f2(n) { + // test unary with constant + var k = 7; + return k--; + } + function f3(n) { + // test unary with constant + var k = 7; + return ++k; + } + function f4(n) { + // test unary with non-constant + var k = 8 - n; + return k--; + } + function f5(n) { + // test unary with non-constant + var k = 9 - n; + return ++k; + } + } + expect: { + function f0(o, p) { + var x = o[p]; + return delete x; + } + function f1(n) { + return +!!n < n; + } + function f2(n) { + var k = 7; + return k--; + } + function f3(n) { + var k = 7; + return ++k; + } + function f4(n) { + var k = 8 - n; + return k--; + } + function f5(n) { + var k = 9 - n; + return ++k; + } + } +} + +collapse_vars_try: { + options = { + booleans: true, + collapse_vars: true, + comparisons: true, + conditionals: true, + dead_code: true, + evaluate: true, + hoist_funs: true, + if_return: true, + join_vars: true, + keep_fargs: true, + loops: true, + properties: true, + reduce_funcs: true, + reduce_vars: true, + sequences: true, + side_effects: true, + unused: true, + } + input: { + function f1() { + try { + var a = 1; + return a; + } + catch (ex) { + var b = 2; + return b; + } + finally { + var c = 3; + return c; + } + } + function f2() { + var t = could_throw(); // shouldn't be replaced in try block + try { + return t + might_throw(); + } + catch (ex) { + return 3; + } + } + } + expect: { + function f1() { + try { + return 1; + } + catch (ex) { + return 2; + } + finally { + return 3; + } + } + function f2() { + var t = could_throw(); + try { + return t + might_throw(); + } + catch (ex) { + return 3; + } + } + } +} + +collapse_vars_array_1: { + options = { + booleans: true, + collapse_vars: true, + comparisons: true, + conditionals: true, + dead_code: true, + evaluate: true, + hoist_funs: true, + if_return: true, + join_vars: true, + keep_fargs: true, + loops: true, + properties: true, + sequences: true, + side_effects: true, + unused: true, + } + input: { + function f1(x, y) { + var z = x + y; + return [z]; + } + function f2(x, y) { + var z = x + y; + return [x, side_effect(), z]; + } + function f3(x, y) { + var z = f(x + y); + return [ [3], [z, x, y], [g()] ]; + } + } + expect: { + function f1(x, y) { + return [x + y] + } + function f2(x, y) { + var z = x + y + return [x, side_effect(), z] + } + function f3(x, y) { + return [ [3], [f(x + y), x, y], [g()] ] + } + } +} + +collapse_vars_array_2: { + options = { + collapse_vars: true, + unused: true, + } + input: { + function f(a) { + var b; + return [ (b = a, b.g()) ]; + } + console.log(f({ + g: function() { + return "PASS"; + } + })[0]); + } + expect: { + function f(a) { + return [ a.g() ]; + } + console.log(f({ + g: function() { + return "PASS"; + } + })[0]); + } + expect_stdout: "PASS" +} + +collapse_vars_array_3: { + options = { + collapse_vars: true, + unused: true, + } + input: { + function f(a) { + var b; + return [ b = a, b, b ]; + } + console.log(f().length); + } + expect: { + function f(a) { + return [ a, a, a ]; + } + console.log(f().length); + } + expect_stdout: "3" +} + +collapse_vars_object_1: { + options = { + booleans: true, + collapse_vars: true, + comparisons: true, + conditionals: true, + dead_code: true, + evaluate: true, + hoist_funs: true, + if_return: true, + join_vars: true, + keep_fargs: true, + loops: true, + properties: true, + sequences: true, + side_effects: true, + unused: true, + } + input: { + function f0(x, y) { + var z = x + y; + return { + get b() { return 7; }, + r: z + }; + } + function f1(x, y) { + var z = x + y; + return { + r: z, + get b() { return 7; } + }; + } + function f2(x, y) { + var z = x + y; + var k = x - y; + return { + q: k, + r: g(x), + s: z + }; + } + function f3(x, y) { + var z = f(x + y); + return [{ + a: {q: x, r: y, s: z}, + b: g() + }]; + } + } + expect: { + function f0(x, y) { + return { + get b() { return 7; }, + r: x + y + }; + } + function f1(x, y) { + return { + r: x + y, + get b() { return 7; } + }; + } + function f2(x, y) { + var z = x + y; + return { + q: x - y, + r: g(x), + s: z + }; + } + function f3(x, y) { + return [{ + a: {q: x, r: y, s: f(x + y)}, + b: g() + }]; + } + } +} + +collapse_vars_object_2: { + options = { + collapse_vars: true, + unused: true, + } + input: { + function f(a) { + var b; + return { + p: (b = a, b.g()) + }; + } + console.log(f({ + g: function() { + return "PASS"; + } + }).p); + } + expect: { + function f(a) { + return { + p: a.g() + }; + } + console.log(f({ + g: function() { + return "PASS"; + } + }).p); + } + expect_stdout: "PASS" +} + +collapse_vars_object_3: { + options = { + collapse_vars: true, + unused: true, + } + input: { + function f(a) { + var b; + return { + p: b = a, + q: b, + r: b, + }; + } + console.log(f("PASS").r); + } + expect: { + function f(a) { + return { + p: a, + q: a, + r: a, + }; + } + console.log(f("PASS").r); + } + expect_stdout: "PASS" +} + +collapse_vars_eval_and_with: { + options = { + booleans: true, + collapse_vars: true, + comparisons: true, + conditionals: true, + dead_code: true, + evaluate: true, + hoist_funs: true, + if_return: true, + join_vars: true, + keep_fargs: true, + loops: true, + properties: true, + sequences: false, + side_effects: true, + unused: true, + } + input: { + // Don't attempt to collapse vars in presence of eval() or with statement. + (function f0() { + var a = 2; + console.log(a - 5); + eval("console.log(a);"); + })(); + (function f1() { + var o = {a: 1}, a = 2; + with (o) console.log(a); + })(); + (function f2() { + var o = {a: 1}, a = 2; + return function() { with (o) console.log(a) }; + })()(); + } + expect: { + (function f0() { + var a = 2; + console.log(a - 5); + eval("console.log(a);"); + })(); + (function f1() { + var o = {a: 1}, a = 2; + with(o) console.log(a); + })(); + (function f2() { + var o = {a: 1}, a = 2; + return function() { with (o) console.log(a) }; + })()(); + } + expect_stdout: true +} + +collapse_vars_constants: { + options = { + booleans: true, + collapse_vars: true, + comparisons: true, + conditionals: true, + dead_code: true, + evaluate: true, + hoist_funs: true, + if_return: true, + join_vars: true, + keep_fargs: true, + loops: true, + properties: true, + reduce_funcs: true, + reduce_vars: true, + sequences: true, + side_effects: true, + unused: true, + } + input: { + function f1(x) { + var a = 4, b = x.prop, c = 5, d = sideeffect1(), e = sideeffect2(); + return b + (function() { return d - a * e - c; })(); + } + function f2(x) { + var a = 4, b = x.prop, c = 5, not_used = sideeffect1(), e = sideeffect2(); + return b + (function() { return -a * e - c; })(); + } + function f3(x) { + var a = 4, b = x.prop, c = 5, not_used = sideeffect1(); + return b + (function() { return -a - c; })(); + } + } + expect: { + function f1(x) { + var b = x.prop, d = sideeffect1(), e = sideeffect2(); + return b + (function() { return d - 4 * e - 5; })(); + } + function f2(x) { + var b = x.prop, e = (sideeffect1(), sideeffect2()); + return b + (function() { return -4 * e - 5; })(); + } + function f3(x) { + var b = x.prop; + sideeffect1(); + return b + -9; + } + } +} + +collapse_vars_arguments_1: { + options = { + booleans: true, + collapse_vars: true, + comparisons: true, + conditionals: true, + dead_code: true, + evaluate: true, + hoist_funs: true, + if_return: true, + join_vars: true, + keep_fargs: true, + loops: true, + properties: true, + reduce_funcs: true, + reduce_vars: true, + sequences: true, + side_effects: true, + toplevel: true, + unused: true, + } + input: { + var outer = function() { + // Do not replace `arguments` but do replace the constant `k` before it. + var k = 7, arguments = 5, inner = function() { console.log(arguments); } + inner(k, 1); + } + outer(); + } + expect: { + (function() { + (function(){console.log(arguments);})(7, 1); + })(); + } + expect_stdout: true +} + +collapse_vars_arguments_2: { + options = { + collapse_vars: true, + } + input: { + function log(a, b) { + console.log(b); + } + function f(c) { + var d = arguments[0]; + c = "FAIL"; + log(c, d); + } + f(); + f("PASS"); + } + expect: { + function log(a, b) { + console.log(b); + } + function f(c) { + var d = arguments[0]; + log(c = "FAIL", d); + } + f(); + f("PASS"); + } + expect_stdout: [ + "undefined", + "PASS", + ] +} + +collapse_vars_arguments_3: { + options = { + collapse_vars: true, + } + input: { + function log(a, b) { + console.log(b); + } + function f(c) { + var args = arguments; + console.log(c); + var d = args[0]; + c = "FAIL"; + log(c, d); + } + f(); + f("PASS"); + } + expect: { + function log(a, b) { + console.log(b); + } + function f(c) { + var args = arguments; + console.log(c); + var d = args[0]; + log(c = "FAIL", d); + } + f(); + f("PASS"); + } + expect_stdout: [ + "undefined", + "undefined", + "PASS", + "PASS", + ] +} + +collapse_vars_short_circuit: { + options = { + booleans: true, + collapse_vars: true, + comparisons: true, + conditionals: true, + dead_code: true, + evaluate: true, + hoist_funs: true, + if_return: true, + join_vars: true, + keep_fargs: true, + loops: true, + properties: true, + sequences: true, + side_effects: true, + unused: true, + } + input: { + function f0(x) { var a = foo(), b = bar(); return b || x; } + function f1(x) { var a = foo(), b = bar(); return b && x; } + function f2(x) { var a = foo(), b = bar(); return x && a && b; } + function f3(x) { var a = foo(), b = bar(); return a && x; } + function f4(x) { var a = foo(), b = bar(); return a && x && b; } + function f5(x) { var a = foo(), b = bar(); return x || a || b; } + function f6(x) { var a = foo(), b = bar(); return a || x || b; } + function f7(x) { var a = foo(), b = bar(); return a && b && x; } + function f8(x,y) { var a = foo(), b = bar(); return (x || a) && (y || b); } + function f9(x,y) { var a = foo(), b = bar(); return (x && a) || (y && b); } + function f10(x,y) { var a = foo(), b = bar(); return (x - a) || (y - b); } + function f11(x,y) { var a = foo(), b = bar(); return (x - b) || (y - a); } + function f12(x,y) { var a = foo(), b = bar(); return (x - y) || (b - a); } + function f13(x,y) { var a = foo(), b = bar(); return (a - b) || (x - y); } + function f14(x,y) { var a = foo(), b = bar(); return (b - a) || (x - y); } + } + expect: { + function f0(x) { foo(); return bar() || x; } + function f1(x) { foo(); return bar() && x; } + function f2(x) { var a = foo(), b = bar(); return x && a && b; } + function f3(x) { var a = foo(); bar(); return a && x; } + function f4(x) { var a = foo(), b = bar(); return a && x && b; } + function f5(x) { var a = foo(), b = bar(); return x || a || b; } + function f6(x) { var a = foo(), b = bar(); return a || x || b; } + function f7(x) { var a = foo(), b = bar(); return a && b && x; } + function f8(x,y) { var a = foo(), b = bar(); return (x || a) && (y || b); } + function f9(x,y) { var a = foo(), b = bar(); return (x && a) || (y && b); } + function f10(x,y) { var a = foo(), b = bar(); return (x - a) || (y - b); } + function f11(x,y) { var a = foo(); return (x - bar()) || (y - a); } + function f12(x,y) { var a = foo(), b = bar(); return (x - y) || (b - a); } + function f13(x,y) { return (foo() - bar()) || (x - y); } + function f14(x,y) { var a = foo(); return (bar() - a) || (x - y); } + } +} + +collapse_vars_short_circuited_conditions: { + options = { + booleans: true, + collapse_vars: true, + comparisons: false, + conditionals: false, + dead_code: true, + evaluate: true, + hoist_funs: true, + if_return: false, + join_vars: true, + keep_fargs: true, + loops: true, + sequences: false, + side_effects: true, + unused: true, + } + input: { + function c1(x) { var a = foo(), b = bar(), c = baz(); return a ? b : c; } + function c2(x) { var a = foo(), b = bar(), c = baz(); return a ? c : b; } + function c3(x) { var a = foo(), b = bar(), c = baz(); return b ? a : c; } + function c4(x) { var a = foo(), b = bar(), c = baz(); return b ? c : a; } + function c5(x) { var a = foo(), b = bar(), c = baz(); return c ? a : b; } + function c6(x) { var a = foo(), b = bar(), c = baz(); return c ? b : a; } + + function i1(x) { var a = foo(), b = bar(), c = baz(); if (a) return b; else return c; } + function i2(x) { var a = foo(), b = bar(), c = baz(); if (a) return c; else return b; } + function i3(x) { var a = foo(), b = bar(), c = baz(); if (b) return a; else return c; } + function i4(x) { var a = foo(), b = bar(), c = baz(); if (b) return c; else return a; } + function i5(x) { var a = foo(), b = bar(), c = baz(); if (c) return a; else return b; } + function i6(x) { var a = foo(), b = bar(), c = baz(); if (c) return b; else return a; } + } + expect: { + function c1(x) { var a = foo(), b = bar(), c = baz(); return a ? b : c; } + function c2(x) { var a = foo(), b = bar(), c = baz(); return a ? c : b; } + function c3(x) { var a = foo(), b = bar(), c = baz(); return b ? a : c; } + function c4(x) { var a = foo(), b = bar(), c = baz(); return b ? c : a; } + function c5(x) { var a = foo(), b = bar(); return baz() ? a : b; } + function c6(x) { var a = foo(), b = bar(); return baz() ? b : a; } + + function i1(x) { var a = foo(), b = bar(), c = baz(); if (a) return b; else return c; } + function i2(x) { var a = foo(), b = bar(), c = baz(); if (a) return c; else return b; } + function i3(x) { var a = foo(), b = bar(), c = baz(); if (b) return a; else return c; } + function i4(x) { var a = foo(), b = bar(), c = baz(); if (b) return c; else return a; } + function i5(x) { var a = foo(), b = bar(); if (baz()) return a; else return b; } + function i6(x) { var a = foo(), b = bar(); if (baz()) return b; else return a; } + } +} + +collapse_vars_regexp: { + options = { + booleans: true, + collapse_vars: true, + comparisons: true, + conditionals: true, + dead_code: true, + evaluate: true, + hoist_funs: true, + if_return: true, + join_vars: true, + keep_fargs: true, + loops: false, + reduce_funcs: true, + reduce_vars: true, + side_effects: true, + unused: true, + } + input: { + function f1() { + var k = 9; + var rx = /[A-Z]+/; + return [rx, k]; + } + function f2() { + var rx = /ab*/g; + return function(s) { + return rx.exec(s); + }; + } + function f3() { + var rx = /ab*/g; + return function() { + return rx; + }; + } + (function() { + var result; + var s = "acdabcdeabbb"; + var rx = /ab*/g; + while (result = rx.exec(s)) + console.log(result[0]); + })(); + (function() { + var result; + var s = "acdabcdeabbb"; + var rx = f2(); + while (result = rx(s)) + console.log(result[0]); + })(); + (function() { + var result; + var s = "acdabcdeabbb"; + var rx = f3(); + while (result = rx().exec(s)) + console.log(result[0]); + })(); + } + expect: { + function f1() { + return [/[A-Z]+/, 9]; + } + function f2() { + var rx = /ab*/g; + return function(s) { + return rx.exec(s); + }; + } + function f3() { + var rx = /ab*/g; + return function() { + return rx; + }; + } + (function() { + var result, rx = /ab*/g; + while (result = rx.exec("acdabcdeabbb")) + console.log(result[0]); + })(); + (function() { + var result, rx = f2(); + while (result = rx("acdabcdeabbb")) + console.log(result[0]); + })(); + (function() { + var result, rx = f3(); + while (result = rx().exec("acdabcdeabbb")) + console.log(result[0]); + })(); + } + expect_stdout: [ + "a", + "ab", + "abbb", + "a", + "ab", + "abbb", + "a", + "ab", + "abbb", + ] +} + +collapse_arg_sequence: { + options = { + collapse_vars: true, + unused: true, + } + input: { + (function(a) { + a("foo"); + })((console.log("bar"), console.log)); + } + expect: { + (function(a) { + (0, console.log)("foo"); + })(console.log("bar")); + } + expect_stdout: [ + "bar", + "foo", + ] +} + +collapse_for_init: { + options = { + collapse_vars: true, + toplevel: true, + } + input: { + for (var a = (Math, console), b = a.log("PASS"); b;); + } + expect: { + Math; + for (var a, b = console.log("PASS"); b;); + } + expect_stdout: "PASS" +} + +issue_1537: { + options = { + collapse_vars: true, + } + input: { + var k = ''; + for (k in {prop: 'val'}){} + } + expect: { + var k = ''; + for (k in {prop: 'val'}); + } +} + +issue_1562: { + options = { + collapse_vars: true, + evaluate: true, + reduce_funcs: true, + reduce_vars: true, + toplevel: true, + unused: true, + } + input: { + var v = 1, B = 2; + for (v in objs) f(B); + + var x = 3, C = 10; + while(x + 2) bar(C); + + var y = 4, D = 20; + do bar(D); while(y + 2); + + var z = 5, E = 30; + for (; f(z + 2) ;) bar(E); + } + expect: { + var v = 1; + for (v in objs) f(2); + + while(5) bar(10); + + do bar(20); while(6); + + for (; f(7) ;) bar(30); + } +} + +issue_1605_1: { + options = { + collapse_vars: true, + toplevel: false, + unused: true, + } + input: { + function foo(x) { + var y = x; + return y; + } + var o = new Object; + o.p = 1; + } + expect: { + function foo(x) { + return x; + } + var o = new Object; + o.p = 1; + } +} + +issue_1605_2: { + options = { + collapse_vars: true, + toplevel: "vars", + unused: true, + } + input: { + function foo(x) { + var y = x; + return y; + } + var o = new Object; + o.p = 1; + } + expect: { + function foo(x) { + return x; + } + (new Object).p = 1; + } +} + +issue_1631_1: { + options = { + collapse_vars: true, + hoist_funs: true, + join_vars: true, + sequences: true, + side_effects: true, + } + input: { + var pc = 0; + function f(x) { + pc = 200; + return 100; + } + function x() { + var t = f(); + pc += t; + return pc; + } + console.log(x()); + } + expect: { + function f(x) { + return pc = 200, 100; + } + function x() { + var t = f(); + return pc += t; + } + var pc = 0; + console.log(x()); + } + expect_stdout: "300" +} + +issue_1631_2: { + options = { + collapse_vars: true, + hoist_funs: true, + join_vars: true, + sequences: true, + side_effects: true, + } + input: { + var a = 0, b = 1; + function f() { + a = 2; + return 4; + } + function g() { + var t = f(); + b = a + t; + return b; + } + console.log(g()); + } + expect: { + function f() { + return a = 2, 4; + } + function g() { + var t = f(); + return b = a + t; + } + var a = 0, b = 1; + console.log(g()); + } + expect_stdout: "6" +} + +issue_1631_3: { + options = { + collapse_vars: true, + hoist_funs: true, + join_vars: true, + sequences: true, + side_effects: true, + unused: true, + } + input: { + function g() { + var a = 0, b = 1; + function f() { + a = 2; + return 4; + } + var t = f(); + b = a + t; + return b; + } + console.log(g()); + } + expect: { + function g() { + function f() { + return a = 2, 4; + } + var a = 0, t = f(); + return a + t; + } + console.log(g()); + } + expect_stdout: "6" +} + +var_side_effects_1: { + options = { + collapse_vars: true, + unused: true, + } + input: { + var print = console.log.bind(console); + function foo(x) { + var twice = x * 2; + print('Foo:', twice); + } + foo(10); + } + expect: { + var print = console.log.bind(console); + function foo(x) { + print('Foo:', 2 * x); + } + foo(10); + } + expect_stdout: true +} + +var_side_effects_2: { + options = { + collapse_vars: true, + unused: true, + } + input: { + var print = console.log.bind(console); + function foo(x) { + var twice = x.y * 2; + print('Foo:', twice); + } + foo({ y: 10 }); + } + expect: { + var print = console.log.bind(console); + function foo(x) { + var twice = 2 * x.y; + print('Foo:', twice); + } + foo({ y: 10 }); + } + expect_stdout: true +} + +var_side_effects_3: { + options = { + collapse_vars: true, + pure_getters: true, + unsafe: true, + unused: true, + } + input: { + var print = console.log.bind(console); + function foo(x) { + var twice = x.y * 2; + print('Foo:', twice); + } + foo({ y: 10 }); + } + expect: { + var print = console.log.bind(console); + function foo(x) { + print('Foo:', 2 * x.y); + } + foo({ y: 10 }); + } + expect_stdout: true +} + +reduce_vars_assign: { + options = { + collapse_vars: true, + reduce_funcs: true, + reduce_vars: true, + } + input: { + !function() { + var a = 1; + a = [].length, + console.log(a); + }(); + } + expect: { + !function() { + var a = 1; + a = [].length, + console.log(a); + }(); + } + expect_stdout: "0" +} + +iife_1: { + options = { + collapse_vars: true, + reduce_funcs: true, + reduce_vars: true, + toplevel: true, + unused: true, + } + input: { + var log = function(x) { + console.log(x); + }, foo = bar(); + log(foo); + } + expect: { + (function(x) { + console.log(x); + })(bar()); + } +} + +iife_2: { + options = { + collapse_vars: true, + reduce_funcs: false, + reduce_vars: false, + toplevel: true, + unused: false, + } + input: { + var foo = bar(); + !function(x) { + console.log(x); + }(foo); + } + expect: { + var foo; + !function(x) { + console.log(x); + }(bar()); + } +} + +var_defs: { + options = { + booleans: true, + collapse_vars: true, + comparisons: true, + conditionals: true, + dead_code: true, + evaluate: true, + hoist_funs: true, + if_return: true, + join_vars: true, + keep_fargs: true, + loops: true, + properties: true, + sequences: true, + side_effects: true, + unsafe: true, + unused: true, + } + input: { + var f1 = function(x, y) { + var a, b, r = x + y, q = r * r, z = q - r, a = z, b = 7; + console.log(a + b); + }; + f1("1", 0); + } + expect: { + var f1 = function(x, y) { + var r = x + y; + console.log(r * r - r + 7); + }; + f1("1", 0); + } + expect_stdout: "97" +} + +assignment: { + options = { + collapse_vars: true, + unused: true, + } + input: { + function f() { + var a; + a = x; + return a; + } + } + expect: { + function f() { + return x; + } + } +} + +for_init: { + options = { + collapse_vars: true, + unused: true, + } + input: { + function f(x, y) { + var a = x; + var b = y; + for (a; b;); + } + } + expect: { + function f(x, y) { + var b = y; + for (x; b;); + } + } +} + +switch_case_1: { + options = { + collapse_vars: true, + unused: true, + } + input: { + function f(x, y, z) { + var a = x(); + var b = y(); + var c = z; + switch (a) { + default: d(); + case b: e(); + case c: f(); + } + } + } + expect: { + function f(x, y, z) { + switch (x()) { + default: d(); + case y(): e(); + case z: f(); + } + } + } +} + +switch_case_2: { + options = { + collapse_vars: true, + } + input: { + var a = 1, b = 2; + switch (b++) { + case b: + var c = a; + var a; + break; + } + console.log(a); + } + expect: { + var a = 1, b = 2; + switch (b++) { + case b: + var c = a; + var a; + break; + } + console.log(a); + } + expect_stdout: "1" +} + +switch_case_3: { + options = { + collapse_vars: true, + } + input: { + var a = 1, b = 2; + switch (a) { + case a: + var b; + break; + case b: + break; + } + console.log(b); + } + expect: { + var a = 1, b = 2; + switch (a) { + case a: + var b; + break; + case b: + break; + } + console.log(b); + } + expect_stdout: "2" +} + +issue_27: { + options = { + collapse_vars: true, + unused: true, + } + input: { + (function(jQuery) { + var $; + $ = jQuery; + $("body").addClass("foo"); + })(jQuery); + } + expect: { + (function(jQuery) { + jQuery("body").addClass("foo"); + })(jQuery); + } +} + +modified: { + options = { + collapse_vars: true, + unused: true, + } + input: { + function f1(b) { + var a = b; + return b + a; + } + function f2(b) { + var a = b; + return b++ + a; + } + function f3(b) { + var a = b++; + return b + a; + } + function f4(b) { + var a = b++; + return b++ + a; + } + function f5(b) { + var a = function() { + return b; + }(); + return b++ + a; + } + console.log(f1(1), f2(1), f3(1), f4(1), f5(1)); + } + expect: { + function f1(b) { + return b + b; + } + function f2(b) { + var a = b; + return b++ + a; + } + function f3(b) { + var a = b++; + return b + a; + } + function f4(b) { + var a = b++; + return b++ + a; + } + function f5(b) { + var a = function() { + return b; + }(); + return b++ + a; + } + console.log(f1(1), f2(1), f3(1), f4(1), f5(1)); + } + expect_stdout: "2 2 3 3 2" +} + +issue_1858: { + options = { + collapse_vars: true, + pure_getters: true, + reduce_vars: true, + unused: true, + } + input: { + console.log(function(x) { + var a = {}, b = a.b = x; + return a.b + b; + }(1)); + } + expect: { + console.log(function(x) { + var a = {}, b = a.b = 1; + return a.b + b; + }()); + } + expect_stdout: "2" +} + +anonymous_function: { + options = { + collapse_vars: true, + } + input: { + console.log(function f(a) { + f ^= 0; + return f * a; + }(1)); + } + expect: { + console.log(function f(a) { + f ^= 0; + return f * a; + }(1)); + } + expect_stdout: true +} + +side_effects_property: { + options = { + collapse_vars: true, + } + input: { + var a = []; + var b = 0; + a[b++] = function() { return 42;}; + var c = a[b++](); + console.log(c); + } + expect: { + var a = []; + var b = 0; + a[b++] = function() { return 42;}; + var c = a[b++](); + console.log(c); + } + expect_stdout: true +} + +undeclared_1: { + options = { + collapse_vars: true, + unused: true, + } + input: { + function f(x, y) { + var a; + a = x; + b = y; + return b + a; + } + } + expect: { + function f(x, y) { + return (b = y) + x; + } + } +} + +undeclared_2: { + options = { + collapse_vars: true, + unused: true, + } + input: { + function f(x, y) { + var a; + a = x; + b = y; + return a + b; + } + } + expect: { + function f(x, y) { + return x + (b = y); + } + } +} + +undeclared_3: { + options = { + collapse_vars: true, + unused: true, + } + input: { + function f(x, y) { + var a; + a = x; + b = y; + return b + a(); + } + } + expect: { + function f(x, y) { + return (b = y) + x(); + } + } +} + +undeclared_4: { + options = { + collapse_vars: true, + unused: true, + } + input: { + function f(x, y) { + var a; + a = x; + b = y; + return a() + b; + } + } + expect: { + function f(x, y) { + b = y; + return x() + b; + } + } +} + +ref_scope: { + options = { + collapse_vars: true, + unused: true, + } + input: { + console.log(function() { + var a = 1, b = 2, c = 3; + var a = c++, b = b /= a; + return function() { + return a; + }() + b; + }()); + } + expect: { + console.log(function() { + var a = 1, b = 2, c = 3; + b = b /= a = c++; + return function() { + return a; + }() + b; + }()); + } + expect_stdout: true +} + +chained_1: { + options = { + collapse_vars: true, + unused: true, + } + input: { + var a = 2; + var a = 3 / a; + console.log(a); + } + expect: { + var a = 3 / (a = 2); + console.log(a); + } + expect_stdout: true +} + +chained_2: { + options = { + collapse_vars: true, + unused: true, + } + input: { + var a; + var a = 2; + a = 3 / a; + console.log(a); + } + expect: { + var a; + a = 3 / (a = 2); + console.log(a); + } + expect_stdout: true +} + +chained_3: { + options = { + collapse_vars: true, + unused: true, + } + input: { + console.log(function(a, b) { + var c = a, c = b; + b++; + return c; + }(1, 2)); + } + expect: { + console.log(function(a, b) { + var c = 1, c = b; + b++; + return c; + }(0, 2)); + } + expect_stdout: "2" +} + +chained_4: { + options = { + collapse_vars: true, + } + input: { + var a = "foo", b = 42; + var b = void (b = a); + console.log(a, b); + } + expect: { + var a = "foo", b = 42; + var b = void (b = a); + console.log(a, b); + } + expect_stdout: "foo undefined" +} + +chained_5: { + options = { + collapse_vars: true, + } + input: { + var a = "PASS"; + var a = (console, console.log(a)); + a && ++a; + } + expect: { + var a = "PASS"; + console; + var a; + (a = console.log(a)) && ++a; + } + expect_stdout: "PASS" +} + +boolean_binary_1: { + options = { + collapse_vars: true, + } + input: { + var a = 1; + a++; + (function() {} || a || 3).toString(); + console.log(a); + } + expect: { + var a = 1; + a++; + (function() {} || a || 3).toString(); + console.log(a); + } + expect_stdout: true +} + +boolean_binary_2: { + options = { + collapse_vars: true, + } + input: { + var c = 0; + c += 1; + (function() { + c = 1 + c; + } || 9).toString(); + console.log(c); + } + expect: { + var c = 0; + c += 1; + (function() { + c = 1 + c; + } || 9).toString(); + console.log(c); + } + expect_stdout: true +} + +inner_lvalues: { + options = { + collapse_vars: true, + unused: true, + } + input: { + var a, b = 10; + var a = (--b || a || 3).toString(), c = --b + -a; + console.log(null, a, b); + } + expect: { + var b = 10; + var a = (--b || a || 3).toString(), c = --b + -a; + console.log(null, a, b); + } + expect_stdout: true +} + +double_def_1: { + options = { + collapse_vars: true, + unused: true, + } + input: { + var a = x, a = a && y; + a(); + } + expect: { + var a = x; + (a = a && y)(); + } +} + +double_def_2: { + options = { + collapse_vars: true, + toplevel: true, + unused: true, + } + input: { + var a = x, a = a && y; + a(); + } + expect: { + (x && y)(); + } +} + +toplevel_single_reference: { + options = { + collapse_vars: true, + unused: true, + } + input: { + var a; + for (var b in x) { + var a = b; + b(a); + } + } + expect: { + for (var b in x) { + var a = b; + b(b); + } + } +} + +unused_orig: { + options = { + collapse_vars: true, + dead_code: true, + passes: 2, + reduce_funcs: true, + reduce_vars: true, + unused: true, + } + input: { + var a = 1; + console.log(function(b) { + var a; + var c = b; + for (var d in c) { + var a = c[0]; + return --b + a; + } + try { + } catch (e) { + --b + a; + } + a && a.NaN; + }([2]), a); + } + expect: { + var a = 1; + console.log(function(b) { + var c = b; + for (var d in c) { + var a; + return --b + c[0]; + } + a && a.NaN; + }([2]), a); + } + expect_stdout: "3 1" +} + +issue_315: { + options = { + collapse_vars: true, + evaluate: true, + keep_fargs: false, + reduce_funcs: true, + reduce_vars: true, + sequences: true, + unused: true, + } + input: { + console.log(function(s) { + var w, _i, _len, _ref, _results; + _ref = s.trim().split(" "); + _results = []; + for (_i = 0, _len = _ref.length; _i < _len; _i++) { + w = _ref[_i]; + _results.push(w.toLowerCase()); + } + return _results; + }("test")); + } + expect: { + console.log(function() { + var w, _i, _len, _ref, _results; + for (_results = [], _i = 0, _len = (_ref = "test".trim().split(" ")).length; _i < _len ; _i++) + w = _ref[_i], _results.push(w.toLowerCase()); + return _results; + }()); + } + expect_stdout: true +} + +lvalues_def: { + options = { + collapse_vars: true, + side_effects: true, + unused: true, + } + input: { + var a = 0, b = 1; + var a = b++, b = +function() {}(); + a && a[a++]; + console.log(a, b); + } + expect: { + var a = 0, b = 1; + a = b++, b = +void 0; + a && a[a++]; + console.log(a, b); + } + expect_stdout: true +} + +compound_assignment_1: { + options = { + collapse_vars: true, + } + input: { + var a; + a = 1; + a += a + 2; + console.log(a); + } + expect: { + var a; + a = 1; + a += a + 2; + console.log(a); + } + expect_stdout: "4" +} + +compound_assignment_2: { + options = { + collapse_vars: true, + } + input: { + var a; + a = 1; + for (a += a + 2; console.log(a);); + } + expect: { + var a; + a = 1; + for (a += a + 2; console.log(a);); + } + expect_stdout: "4" +} + +compound_assignment_3: { + options = { + collapse_vars: true, + } + input: { + var a = 1; + a += (console.log("PASS"), 2); + a.p; + } + expect: { + var a = 1; + (a += (console.log("PASS"), 2)).p; + } + expect_stdout: "PASS" +} + +compound_assignment_4: { + options = { + collapse_vars: true, + evaluate: true, + } + input: { + A = "PASS"; + var a = ""; + a += (a = "FAIL", A); + a.p; + console.log(a); + } + expect: { + var a = ""; + (a += (a = "FAIL", A = "PASS")).p; + console.log(a); + } + expect_stdout: "PASS" +} + +compound_assignment_5: { + options = { + collapse_vars: true, + } + input: { + var a = 0, b; + a += 42; + b && (a *= null); + console.log(a); + } + expect: { + var a = 0, b; + a += 42; + b && (a *= null); + console.log(a); + } + expect_stdout: "42" +} + +compound_assignment_6: { + options = { + collapse_vars: true, + } + input: { + var a; + a ^= 6; + a *= a + 1; + console.log(a); + } + expect: { + var a; + a = (a ^= 6) * (a + 1); + console.log(a); + } + expect_stdout: "42" +} + +compound_assignment_7: { + options = { + assignments: true, + collapse_vars: true, + reduce_vars: true, + toplevel: true, + } + input: { + var a = "FA"; + a = a + "I"; + a = a + "L"; + if (console) + a = "PASS"; + console.log(a); + } + expect: { + var a = "FA"; + a = a + "I" + "L"; + if (console) + a = "PASS"; + console.log(a); + } + expect_stdout: "PASS" +} + +compound_assignment_8: { + options = { + assignments: true, + collapse_vars: true, + reduce_vars: true, + toplevel: true, + } + input: { + var a = 2; + a = 3 * a; + a = 7 * a; + console || (a = "FAIL"); + console.log(a); + } + expect: { + var a = 2; + a = a * 3 * 7; + console || (a = "FAIL"); + console.log(a); + } + expect_stdout: "42" +} + +issue_2187_1: { + options = { + collapse_vars: true, + reduce_vars: true, + unused: true, + } + input: { + var a = 1; + !function(foo) { + foo(); + var a = 2; + console.log(a); + }(function() { + console.log(a); + }); + } + expect: { + var a = 1; + !function(foo) { + foo(); + console.log(2); + }(function() { + console.log(a); + }); + } + expect_stdout: [ + "1", + "2", + ] +} + +issue_2187_2: { + options = { + collapse_vars: true, + unused: true, + } + input: { + var b = 1; + console.log(function(a) { + return a && ++b; + }(b--)); + } + expect: { + var b = 1; + console.log(function(a) { + return b-- && ++b; + }()); + } + expect_stdout: "1" +} + +issue_2187_3: { + options = { + collapse_vars: true, + inline: true, + unused: true, + } + input: { + var b = 1; + console.log(function(a) { + return a && ++b; + }(b--)); + } + expect: { + var b = 1; + console.log(b-- && ++b); + } + expect_stdout: "1" +} + +issue_2203_1: { + options = { + collapse_vars: true, + unused: true, + } + input: { + a = "FAIL"; + console.log({ + a: "PASS", + b: function() { + return function(c) { + return c.a; + }((String, (Object, this))); + } + }.b()); + } + expect: { + a = "FAIL"; + console.log({ + a: "PASS", + b: function() { + return function(c) { + return c.a; + }((String, (Object, this))); + } + }.b()); + } + expect_stdout: "PASS" +} + +issue_2203_2: { + options = { + collapse_vars: true, + unused: true, + } + input: { + a = "PASS"; + console.log({ + a: "FAIL", + b: function() { + return function(c) { + return c.a; + }((String, (Object, function() { + return this; + }()))); + } + }.b()); + } + expect: { + a = "PASS"; + console.log({ + a: "FAIL", + b: function() { + return function(c) { + return (Object, function() { + return this; + }()).a; + }(String); + } + }.b()); + } + expect_stdout: "PASS" +} + +duplicate_argname: { + options = { + collapse_vars: true, + unused: true, + } + input: { + function f() { return "PASS"; } + console.log(function(a, a) { + f++; + return a; + }("FAIL", f())); + } + expect: { + function f() { return "PASS"; } + console.log(function(a, a) { + f++; + return a; + }("FAIL", f())); + } + expect_stdout: "PASS" +} + +issue_2298: { + options = { + collapse_vars: true, + passes: 2, + reduce_funcs: true, + reduce_vars: true, + unused: true, + } + input: { + !function() { + function f() { + var a = undefined; + var undefined = a++; + try { + !function g(b) { + b[1] = "foo"; + }(); + console.log("FAIL"); + } catch (e) { + console.log("PASS"); + } + } + f(); + }(); + } + expect: { + !function() { + (function() { + try { + !function(b) { + (void 0)[1] = "foo"; + }(); + console.log("FAIL"); + } catch (e) { + console.log("PASS"); + } + })(); + }(); + } + expect_stdout: "PASS" +} + +issue_2313_1: { + options = { + collapse_vars: true, + conditionals: true, + } + input: { + var a = 0, b = 0; + var foo = { + get c() { + a++; + return 42; + }, + set c(c) { + b++; + }, + d: function() { + this.c++; + if (this.c) console.log(a, b); + } + } + foo.d(); + } + expect: { + var a = 0, b = 0; + var foo = { + get c() { + a++; + return 42; + }, + set c(c) { + b++; + }, + d: function() { + this.c++; + this.c && console.log(a, b); + } + } + foo.d(); + } + expect_stdout: "2 1" +} + +issue_2313_2: { + options = { + collapse_vars: true, + } + input: { + var c = 0; + !function a() { + a && c++; + var a = 0; + a && c++; + }(); + console.log(c); + } + expect: { + var c = 0; + !function a() { + a && c++; + var a; + (a = 0) && c++; + }(); + console.log(c); + } + expect_stdout: "0" +} + +issue_2319_1: { + options = { + collapse_vars: true, + unused: true, + } + input: { + console.log(function(a) { + return a; + }(!function() { + return this; + }())); + } + expect: { + console.log(function(a) { + return !function() { + return this; + }(); + }()); + } + expect_stdout: "false" +} + +issue_2319_2: { + options = { + collapse_vars: true, + unused: true, + } + input: { + console.log(function(a) { + "use strict"; + return a; + }(!function() { + return this; + }())); + } + expect: { + console.log(function(a) { + "use strict"; + return a; + }(!function() { + return this; + }())); + } + expect_stdout: "false" +} + +issue_2319_3: { + options = { + collapse_vars: true, + unused: true, + } + input: { + "use strict"; + console.log(function(a) { + return a; + }(!function() { + return this; + }())); + } + expect: { + "use strict"; + console.log(function(a) { + return !function() { + return this; + }(); + }()); + } + expect_stdout: "true" +} + +issue_2365: { + options = { + collapse_vars: true, + pure_getters: true, + } + input: { + console.log(function(a) { + var b = a.f; + a.f++; + return b; + }({ f: 1 })); + console.log(function() { + var a = { f: 1 }, b = a.f; + a.f++; + return b; + }()); + console.log({ + f: 1, + g: function() { + var b = this.f; + this.f++; + return b; + } + }.g()); + } + expect: { + console.log(function(a) { + var b = a.f; + a.f++; + return b; + }({ f: 1 })); + console.log(function() { + var a = { f: 1 }, b = a.f; + a.f++; + return b; + }()); + console.log({ + f: 1, + g: function() { + var b = this.f; + this.f++; + return b; + } + }.g()); + } + expect_stdout: [ + "1", + "1", + "1", + ] +} + +issue_2364_1: { + options = { + collapse_vars: true, + pure_getters: true, + } + input: { + function inc(obj) { + return obj.count++; + } + function foo() { + var first = arguments[0]; + var result = inc(first); + return foo.amount = first.count, result; + } + var data = { + count: 0, + }; + var answer = foo(data); + console.log(foo.amount, answer); + } + expect: { + function inc(obj) { + return obj.count++; + } + function foo() { + var first = arguments[0]; + var result = inc(first); + return foo.amount = first.count, result; + } + var data = { + count: 0 + }; + var answer = foo(data); + console.log(foo.amount, answer); + } + expect_stdout: "1 0" +} + +issue_2364_2: { + options = { + collapse_vars: true, + pure_getters: true, + } + input: { + function callValidate() { + var validate = compilation.validate; + var result = validate.apply(null, arguments); + return callValidate.errors = validate.errors, result; + } + } + expect: { + function callValidate() { + var validate = compilation.validate; + var result = validate.apply(null, arguments); + return callValidate.errors = validate.errors, result; + } + } +} + +issue_2364_3: { + options = { + collapse_vars: true, + pure_getters: true, + } + input: { + function inc(obj) { + return obj.count++; + } + function foo(bar) { + var result = inc(bar); + return foo.amount = bar.count, result; + } + var data = { + count: 0, + }; + var answer = foo(data); + console.log(foo.amount, answer); + } + expect: { + function inc(obj) { + return obj.count++; + } + function foo(bar) { + var result = inc(bar); + return foo.amount = bar.count, result; + } + var data = { + count: 0, + }; + var answer = foo(data); + console.log(foo.amount, answer); + } + expect_stdout: "1 0" +} + +issue_2364_4: { + options = { + collapse_vars: true, + pure_getters: true, + } + input: { + function inc(obj) { + return obj.count++; + } + function foo(bar, baz) { + var result = inc(bar); + return foo.amount = baz.count, result; + } + var data = { + count: 0, + }; + var answer = foo(data, data); + console.log(foo.amount, answer); + } + expect: { + function inc(obj) { + return obj.count++; + } + function foo(bar, baz) { + var result = inc(bar); + return foo.amount = baz.count, result; + } + var data = { + count: 0, + }; + var answer = foo(data, data); + console.log(foo.amount, answer); + } + expect_stdout: "1 0" +} + +issue_2364_5: { + options = { + collapse_vars: true, + evaluate: true, + properties: true, + pure_getters: true, + reduce_funcs: true, + reduce_vars: true, + unused: true, + } + input: { + function f0(o, a, h) { + var b = 3 - a; + var obj = o; + var seven = 7; + var prop = 'run'; + var t = obj[prop](b)[seven] = h; + return t; + } + } + expect: { + function f0(o, a, h) { + return o.run(3 - a)[7] = h; + } + } +} + +issue_2364_6: { + options = { + collapse_vars: true, + pure_getters: true, + } + input: { + function f(a, b) { + var c = a.p; + b.p = "FAIL"; + return c; + } + var o = { + p: "PASS" + } + console.log(f(o, o)); + } + expect: { + function f(a, b) { + var c = a.p; + b.p = "FAIL"; + return c; + } + var o = { + p: "PASS" + } + console.log(f(o, o)); + } + expect_stdout: "PASS" +} + +issue_2364_7: { + options = { + collapse_vars: true, + pure_getters: true, + } + input: { + function f(a, b) { + var c = a.p; + b.f(); + return c; + } + var o = { + p: "PASS", + f: function() { + this.p = "FAIL"; + } + } + console.log(f(o, o)); + } + expect: { + function f(a, b) { + var c = a.p; + b.f(); + return c; + } + var o = { + p: "PASS", + f: function() { + this.p = "FAIL"; + } + } + console.log(f(o, o)); + } + expect_stdout: "PASS" +} + +issue_2364_8: { + options = { + collapse_vars: true, + pure_getters: true, + } + input: { + function f(a, b, c) { + var d = a[b.f = function() { + return "PASS"; + }]; + return c.f(d); + } + var o = { + f: function() { + return "FAIL"; + } + }; + console.log(f({}, o, o)); + } + expect: { + function f(a, b, c) { + var d = a[b.f = function() { + return "PASS"; + }]; + return c.f(d); + } + var o = { + f: function() { + return "FAIL"; + } + }; + console.log(f({}, o, o)); + } + expect_stdout: "PASS" +} + +issue_2364_9: { + options = { + collapse_vars: true, + pure_getters: true, + } + input: { + function f(a, b) { + var d = a(); + return b.f(d); + } + var o = { + f: function() { + return "FAIL"; + } + }; + console.log(f(function() { + o.f = function() { + return "PASS"; + }; + }, o)); + } + expect: { + function f(a, b) { + var d = a(); + return b.f(d); + } + var o = { + f: function() { + return "FAIL"; + } + }; + console.log(f(function() { + o.f = function() { + return "PASS"; + }; + }, o)); + } + expect_stdout: "PASS" +} + +pure_getters_chain: { + options = { + collapse_vars: true, + pure_getters: true, + unused: true, + } + input: { + function o(t, r) { + var a = t[1], s = t[2], o = t[3], i = t[5]; + return a <= 23 && s <= 59 && o <= 59 && (!r || i); + } + console.log(o([ , 23, 59, 59, , 42], 1)); + } + expect: { + function o(t, r) { + return t[1] <= 23 && t[2] <= 59 && t[3] <= 59 && (!r || t[5]); + } + console.log(o([ , 23, 59, 59, , 42], 1)); + } + expect_stdout: "42" +} + +conditional_1: { + options = { + collapse_vars: true, + unused: true, + } + input: { + function f(a, b) { + var c = ""; + var d = b ? ">" : "<"; + if (a) c += "="; + return c += d; + } + console.log(f(0, 0), f(0, 1), f(1, 0), f(1, 1)); + } + expect: { + function f(a, b) { + var c = ""; + if (a) c += "="; + return c += b ? ">" : "<"; + } + console.log(f(0, 0), f(0, 1), f(1, 0), f(1, 1)); + } + expect_stdout: "< > =< =>" +} + +conditional_2: { + options = { + collapse_vars: true, + unused: true, + } + input: { + function f(a, b) { + var c = a + 1, d = a + 2; + return b ? c : d; + } + console.log(f(3, 0), f(4, 1)); + } + expect: { + function f(a, b) { + return b ? a + 1 : a + 2; + } + console.log(f(3, 0), f(4, 1)); + } + expect_stdout: "5 5" +} + +issue_2425_1: { + options = { + collapse_vars: true, + unused: true, + } + input: { + var a = 8; + (function(b) { + b.toString(); + })(--a, a |= 10); + console.log(a); + } + expect: { + var a = 8; + (function(b) { + b.toString(); + })(--a, a |= 10); + console.log(a); + } + expect_stdout: "15" +} + +issue_2425_2: { + options = { + collapse_vars: true, + unused: true, + } + input: { + var a = 8; + (function(b, c) { + b.toString(); + })(--a, a |= 10); + console.log(a); + } + expect: { + var a = 8; + (function(b, c) { + b.toString(); + })(--a, a |= 10); + console.log(a); + } + expect_stdout: "15" +} + +issue_2425_3: { + options = { + collapse_vars: true, + unused: true, + } + input: { + var a = 8; + (function(b, b) { + b.toString(); + })(--a, a |= 10); + console.log(a); + } + expect: { + var a = 8; + (function(b, b) { + (a |= 10).toString(); + })(--a); + console.log(a); + } + expect_stdout: "15" +} + +issue_2437_1: { + options = { + collapse_vars: true, + conditionals: true, + if_return: true, + inline: true, + join_vars: true, + passes: 2, + reduce_funcs: true, + reduce_vars: true, + sequences: true, + side_effects: true, + toplevel: true, + unused: true, + } + input: { + function foo() { + return bar(); + } + function bar() { + if (xhrDesc) { + var req = new XMLHttpRequest(); + var result = !!req.onreadystatechange; + Object.defineProperty(XMLHttpRequest.prototype, 'onreadystatechange', xhrDesc || {}); + return result; + } else { + var req = new XMLHttpRequest(); + var detectFunc = function(){}; + req.onreadystatechange = detectFunc; + var result = req[SYMBOL_FAKE_ONREADYSTATECHANGE_1] === detectFunc; + req.onreadystatechange = null; + return result; + } + } + console.log(foo()); + } + expect: { + var req, detectFunc, result; + console.log(( + xhrDesc ? (result = !!(req = new XMLHttpRequest).onreadystatechange, + Object.defineProperty(XMLHttpRequest.prototype, "onreadystatechange", xhrDesc||{}) + ) : ( + (req = new XMLHttpRequest).onreadystatechange = detectFunc = function(){}, + result = req[SYMBOL_FAKE_ONREADYSTATECHANGE_1] === detectFunc, + req.onreadystatechange = null + ), + result + )); + } +} + +issue_2437_2: { + options = { + collapse_vars: true, + conditionals: true, + inline: true, + join_vars: true, + negate_iife: true, + passes: 3, + reduce_funcs: true, + reduce_vars: true, + sequences: true, + side_effects: true, + toplevel: true, + unused: true, + } + input: { + function foo() { + bar(); + } + function bar() { + if (xhrDesc) { + var req = new XMLHttpRequest(); + var result = !!req.onreadystatechange; + Object.defineProperty(XMLHttpRequest.prototype, 'onreadystatechange', xhrDesc || {}); + return result; + } else { + var req = new XMLHttpRequest(); + var detectFunc = function(){}; + req.onreadystatechange = detectFunc; + var result = req[SYMBOL_FAKE_ONREADYSTATECHANGE_1] === detectFunc; + req.onreadystatechange = null; + return result; + } + } + foo(); + } + expect: { + var req; + xhrDesc ? ( + (req = new XMLHttpRequest).onreadystatechange, + Object.defineProperty(XMLHttpRequest.prototype, "onreadystatechange", xhrDesc || {}) + ) : ( + (req = new XMLHttpRequest).onreadystatechange = function(){}, + req[SYMBOL_FAKE_ONREADYSTATECHANGE_1], + req.onreadystatechange = null + ); + } +} + +issue_2436_1: { + options = { + collapse_vars: true, + inline: true, + pure_getters: "strict", + reduce_vars: true, + toplevel: true, + unused: true, + } + input: { + var o = { + a: 1, + b: 2, + }; + console.log(function(c) { + return { + x: c.a, + y: c.b, + }; + }(o)); + } + expect: { + var o = { + a: 1, + b: 2, + }; + console.log({ + x: o.a, + y: o.b, + }); + } + expect_stdout: true +} + +issue_2436_2: { + options = { + collapse_vars: true, + inline: true, + pure_getters: "strict", + reduce_vars: true, + toplevel: true, + unused: true, + } + input: { + var o = { + a: 1, + b: 2, + }; + console.log(function(c) { + o.a = 3; + return { + x: c.a, + y: c.b, + }; + }(o)); + } + expect: { + var o = { + a: 1, + b: 2, + }; + console.log(function(c) { + o.a = 3; + return { + x: c.a, + y: c.b, + }; + }(o)); + } + expect_stdout: true +} + +issue_2436_3: { + options = { + collapse_vars: true, + inline: true, + pure_getters: "strict", + reduce_vars: true, + toplevel: true, + unused: true, + } + input: { + var o = { + a: 1, + b: 2, + }; + console.log(function(c) { + o = { + a: 3, + b: 4, + }; + return { + x: c.a, + y: c.b, + }; + }(o)); + } + expect: { + console.log(function(c) { + ({ + a: 3, + b: 4, + }); + return { + x: c.a, + y: c.b, + }; + }({ + a: 1, + b: 2, + })); + } + expect_stdout: true +} + +issue_2436_4: { + options = { + collapse_vars: true, + inline: true, + pure_getters: "strict", + reduce_vars: true, + toplevel: true, + unused: true, + } + input: { + var o = { + a: 1, + b: 2, + }; + console.log(function(c) { + return { + x: c.a, + y: c.b, + }; + var o; + }(o)); + } + expect: { + console.log({ + x: (c = { + a: 1, + b: 2, + }).a, + y: c.b, + }); + var c; + } + expect_stdout: true +} + +issue_2436_5: { + options = { + collapse_vars: true, + inline: true, + pure_getters: "strict", + reduce_vars: true, + toplevel: true, + unused: true, + } + input: { + var o = { + a: 1, + b: 2, + }; + console.log(function(o) { + return { + x: o.a, + y: o.b, + }; + }(o)); + } + expect: { + console.log(function(o) { + return { + x: o.a, + y: o.b, + }; + }({ + a: 1, + b: 2, + })); + } + expect_stdout: true +} + +issue_2436_6: { + options = { + collapse_vars: true, + evaluate: true, + inline: true, + passes: 2, + pure_getters: "strict", + reduce_vars: true, + toplevel: true, + unsafe: true, + unused: true, + } + input: { + var o = { + a: 1, + b: 2, + }; + console.log(function(c) { + return { + x: c.a, + y: c.b, + }; + }(o)); + } + expect: { + console.log({ + x: 1, + y: 2, + }); + } + expect_stdout: true +} + +issue_2436_7: { + options = { + collapse_vars: true, + hoist_props: true, + inline: true, + passes: 3, + pure_getters: "strict", + reduce_vars: true, + toplevel: true, + unused: true, + } + input: { + var o = { + a: 1, + b: 2, + }; + console.log(function(c) { + return { + x: c.a, + y: c.b, + }; + }(o)); + } + expect: { + console.log({ + x: 1, + y: 2, + }); + } + expect_stdout: true +} + +issue_2436_8: { + options = { + collapse_vars: true, + inline: true, + pure_getters: "strict", + reduce_vars: true, + toplevel: true, + unused: true, + } + input: { + console.log(function(c) { + return { + x: c.a, + y: c.b, + }; + }(o)); + } + expect: { + console.log({ + x: (c = o).a, + y: c.b, + }); + var c; + } + expect_stdout: true +} + +issue_2436_9: { + options = { + collapse_vars: true, + inline: true, + pure_getters: "strict", + reduce_vars: true, + toplevel: true, + unused: true, + } + input: { + var o = console; + console.log(function(c) { + return { + x: c.a, + y: c.b, + }; + }(o)); + } + expect: { + var o = console; + console.log({ + x: o.a, + y: o.b, + }); + } + expect_stdout: true +} + +issue_2436_10: { + options = { + collapse_vars: true, + inline: true, + pure_getters: true, + reduce_vars: true, + toplevel: true, + unused: true, + } + input: { + var o = { + a: 1, + b: 2, + }; + function f(n) { + o = { b: 3 }; + return n; + } + console.log(function(c) { + return [ + c.a, + f(c.b), + c.b, + ]; + }(o).join(" ")); + } + expect: { + function f(n) { + ({ b: 3 }); + return n; + } + console.log([ + (c = { + a: 1, + b: 2, + }).a, + f(c.b), + c.b, + ].join(" ")); + var c; + } + expect_stdout: "1 2 2" +} + +issue_2436_11: { + options = { + collapse_vars: true, + join_vars: true, + reduce_vars: true, + unused: true, + } + input: { + function matrix() {} + function isCollection() {} + function _randomDataForMatrix() {} + function _randomInt() {} + function f(arg1, arg2) { + if (isCollection(arg1)) { + var size = arg1; + var max = arg2; + var min = 0; + var res = _randomDataForMatrix(size.valueOf(), min, max, _randomInt); + return size && true === size.isMatrix ? matrix(res) : res; + } else { + var min = arg1; + var max = arg2; + return _randomInt(min, max); + } + } + } + expect: { + function matrix() {} + function isCollection() {} + function _randomDataForMatrix() {} + function _randomInt() {} + function f(arg1, arg2) { + if (isCollection(arg1)) { + var size = arg1, max = arg2, min = 0, res = _randomDataForMatrix(size.valueOf(), min, max, _randomInt); + return size && true === size.isMatrix ? matrix(res) : res; + } else + return _randomInt(min = arg1, max = arg2); + } + } +} + +issue_2436_12: { + options = { + collapse_vars: true, + unused: true, + } + input: { + function isUndefined() {} + function f() { + var viewValue = this.$$lastCommittedViewValue; + var modelValue = viewValue; + return isUndefined(modelValue) ? modelValue : null; + } + } + expect: { + function isUndefined() {} + function f() { + var modelValue = this.$$lastCommittedViewValue; + return isUndefined(modelValue) ? modelValue : null; + } + } +} + +issue_2436_13: { + options = { + collapse_vars: true, + passes: 2, + reduce_vars: true, + unused: true, + } + input: { + var a = "PASS"; + (function() { + function f(b) { + (function g(b) { + var b = b && (b.null = "FAIL"); + })(a); + } + f(); + })(); + console.log(a); + } + expect: { + var a = "PASS"; + (function() { + (function(b) { + (function(b) { + a && (a.null = "FAIL"); + })(); + })(); + })(); + console.log(a); + } + expect_stdout: "PASS" +} + +issue_2436_14: { + options = { + collapse_vars: true, + reduce_vars: true, + unsafe: true, + unused: true, + } + input: { + var a = "PASS"; + var b = {}; + (function() { + var c = a; + c && function(c, d) { + console.log(c, d); + }(b, c); + })(); + } + expect: { + var a = "PASS"; + var b = {}; + (function() { + a && function(c, d) { + console.log(b, d); + }(0, a); + })(); + } + expect_stdout: true +} + +issue_2497: { + options = { + collapse_vars: true, + unused: true, + } + input: { + function sample() { + if (true) { + for (var i = 0; i < 1; ++i) { + for (var k = 0; k < 1; ++k) { + var value = 1; + var x = value; + value = x ? x + 1 : 0; + } + } + } else { + for (var i = 0; i < 1; ++i) { + for (var k = 0; k < 1; ++k) { + var value = 1; + } + } + } + } + } + expect: { + function sample() { + if (true) + for (var i = 0; i < 1; ++i) + for (var k = 0; k < 1; ++k) + value = (value = 1) ? value + 1 : 0; + else + for (i = 0; i < 1; ++i) + for (k = 0; k < 1; ++k) + var value = 1; + } + } +} + +issue_2506: { + options = { + collapse_vars: true, + passes: 2, + reduce_vars: true, + unused: true, + } + input: { + var c = 0; + function f0(bar) { + function f1(Infinity_2) { + function f13(NaN) { + if (false <= NaN & this >> 1 >= 0) { + c++; + } + } + var b_2 = f13(NaN, c++); + } + var bar = f1(-3, -1); + } + f0(false); + console.log(c); + } + expect: { + var c = 0; + function f0(bar) { + (function(Infinity_2) { + (function(NaN) { + if (false <= 0/0 & this >> 1 >= 0) + c++; + })(0, c++); + })(); + } + f0(false); + console.log(c); + } + expect_stdout: "1" +} + +issue_2571_1: { + options = { + collapse_vars: true, + toplevel: true, + } + input: { + var b = 1; + try { + var a = function f0(c) { + throw c; + }(2); + var d = --b + a; + } catch (e) { + } + console.log(b); + } + expect: { + var b = 1; + try { + var a = function f0(c) { + throw c; + }(2); + var d = --b + a; + } catch (e) { + } + console.log(b); + } + expect_stdout: "1" +} + +issue_2571_2: { + options = { + collapse_vars: true, + toplevel: true, + } + input: { + try { + var a = A, b = 1; + throw a; + } catch (e) { + console.log(b); + } + } + expect: { + try { + var a = A, b = 1; + throw a; + } catch (e) { + console.log(b); + } + } + expect_stdout: "undefined" +} + +may_throw_1: { + options = { + collapse_vars: true, + } + input: { + function f() { + var a_2 = function() { + var a; + }(); + } + } + expect: { + function f() { + var a_2 = function() { + var a; + }(); + } + } +} + +may_throw_2: { + options = { + collapse_vars: true, + unused: true, + } + input: { + function f(b) { + try { + var a = x(); + ++b; + return b(a); + } catch (e) {} + console.log(b); + } + f(0); + } + expect: { + function f(b) { + try { + var a = x(); + return (++b)(a); + } catch (e) {} + console.log(b); + } + f(0); + } + expect_stdout: "0" +} + +side_effect_free_replacement: { + options = { + collapse_vars: true, + inline: true, + side_effects: true, + unused: true, + } + input: { + var b; + (function(a) { + x(a); + })(b); + } + expect: { + var b; + x(b); + } +} + +recursive_function_replacement: { + rename = true + options = { + collapse_vars: true, + inline: true, + passes: 2, + reduce_funcs: true, + reduce_vars: true, + toplevel: true, + unused: true, + } + mangle = {} + input: { + function f(a) { + return x(g(a)); + } + function g(a) { + return y(f(a)); + } + console.log(f(c)); + } + expect: { + console.log(function n(o) { + return x(y(n(o))); + }(c)); + } +} + +cascade_conditional: { + options = { + collapse_vars: true, + } + input: { + function f(a, b) { + (a = x(), a) ? a++ : (b = y(a), b(a)); + } + } + expect: { + function f(a, b) { + (a = x()) ? a++ : (b = y(a))(a); + } + } +} + +cascade_if_1: { + options = { + collapse_vars: true, + } + input: { + var a; + if (a = x(), a) + if (a == y()) z(); + } + expect: { + var a; + if (a = x()) + if (a == y()) z(); + } +} + +cascade_if_2: { + options = { + collapse_vars: true, + } + input: { + function f(a, b) { + if (a(), b = x()) return b; + } + } + expect: { + function f(a, b) { + if (a(), b = x()) return b; + } + } +} + +cascade_return: { + options = { + collapse_vars: true, + } + input: { + function f(a) { + return a = x(); + return a; + } + } + expect: { + function f(a) { + return a = x(); + return a; + } + } +} + +cascade_switch: { + options = { + collapse_vars: true, + } + input: { + function f(a, b) { + switch(a = x(), a) { + case a = x(), b(a): + break; + } + } + } + expect: { + function f(a, b) { + switch(a = x()) { + case b(a = x()): + break; + } + } + } +} + +cascade_call: { + options = { + collapse_vars: true, + unused: true, + } + input: { + function f(a) { + var b; + return x((b = a, y(b))); + } + } + expect: { + function f(a) { + return x(y(a)); + } + } +} + +replace_all_var: { + options = { + collapse_vars: true, + unused: true, + } + input: { + var a = "PASS"; + (function() { + var b = b || c && c[a = "FAIL"], c = a; + })(); + console.log(a); + } + expect: { + var a = "PASS"; + (function() { + var b = b || c && c[a = "FAIL"], c = a; + })(); + console.log(a); + } + expect_stdout: "PASS" +} + +replace_all_var_scope: { + rename = true + options = { + collapse_vars: true, + unused: true, + } + mangle = {} + input: { + var a = 100, b = 10; + (function(r, a) { + switch (~a) { + case (b += a): + case a++: + } + })(--b, a); + console.log(a, b); + } + expect: { + var a = 100, b = 10; + (function(c, o) { + switch (~a) { + case (b += a): + case o++: + } + })(--b, a); + console.log(a, b); + } + expect_stdout: "100 109" +} + +cascade_statement: { + options = { + collapse_vars: true, + } + input: { + function f1(a, b) { + var c; + if (a) + return c = b, c || a; + else + c = a, c(b); + } + function f2(a, b) { + var c; + while (a) + c = b, a = c + b; + do + throw c = a + b, c; + while (c); + } + function f3(a, b) { + for (; a < b; a++) + if (c = a, c && b) + var c = (c = b(a), c); + } + } + expect: { + function f1(a, b) { + var c; + if (a) + return (c = b) || a; + else + (c = a)(b); + } + function f2(a, b) { + var c; + while (a) + a = (c = b) + b; + do + throw c = a + b; + while (c); + } + function f3(a, b) { + for (; a < b; a++) + if ((c = a) && b) + var c = c = b(a); + } + } +} + +cascade_forin: { + options = { + collapse_vars: true, + } + input: { + var a; + function f(b) { + return [ b, b, b ]; + } + for (var c in a = console, f(a)) + console.log(c); + } + expect: { + var a; + function f(b) { + return [ b, b, b ]; + } + for (var c in f(a = console)) + console.log(c); + } + expect_stdout: [ + "0", + "1", + "2", + ] +} + +unsafe_builtin: { + options = { + collapse_vars: true, + pure_getters: "strict", + reduce_vars: true, + unsafe: true, + unused: true, + } + input: { + function f(a) { + var b = Math.abs(a); + return Math.pow(b, 2); + } + console.log(f(-1), f(2)); + } + expect: { + function f(a) { + return Math.pow(Math.abs(a), 2); + } + console.log(f(-1), f(2)); + } + expect_stdout: "1 4" +} + +return_1: { + options = { + collapse_vars: true, + unused: true, + } + input: { + var log = console.log; + function f(b, c) { + var a = c; + if (b) return b; + log(a); + } + f(false, 1); + f(true, 2); + } + expect: { + var log = console.log; + function f(b, c) { + if (b) return b; + log(c); + } + f(false, 1); + f(true, 2); + } + expect_stdout: "1" +} + +return_2: { + options = { + collapse_vars: true, + unused: true, + } + input: { + var log = console.log; + function f(b, c) { + var a = c(); + if (b) return b; + log(a); + } + f(false, function() { return 1 }); + f(true, function() { return 2 }); + } + expect: { + var log = console.log; + function f(b, c) { + var a = c(); + if (b) return b; + log(a); + } + f(false, function() { return 1 }); + f(true, function() { return 2 }); + } + expect_stdout: "1" +} + +return_3: { + options = { + collapse_vars: true, + unused: true, + } + input: { + var log = console.log; + function f(b, c) { + var a = b <<= c; + if (b) return b; + log(a); + } + f(false, 1); + f(true, 2); + } + expect: { + var log = console.log; + function f(b, c) { + var a = b <<= c; + if (b) return b; + log(a); + } + f(false, 1); + f(true, 2); + } + expect_stdout: "0" +} + +return_4: { + options = { + collapse_vars: true, + } + input: { + var a = "FAIL"; + (function(b) { + a = "PASS"; + return; + b(a); + })(); + console.log(a); + } + expect: { + var a = "FAIL"; + (function(b) { + a = "PASS"; + return; + b(a); + })(); + console.log(a); + } + expect_stdout: "PASS" +} + +issue_2858: { + options = { + collapse_vars: true, + unused: true, + } + input: { + var b; + (function() { + function f() { + a++; + } + f(); + var c = f(); + var a = void 0; + c || (b = a); + })(); + console.log(b); + } + expect: { + var b; + (function() { + function f() { + a++; + } + f(); + var c = f(); + var a = void 0; + c || (b = a); + })(); + console.log(b); + } + expect_stdout: "undefined" +} + +cond_branch_1: { + options = { + collapse_vars: true, + sequences: true, + unused: true, + } + input: { + function f1(b, c) { + var log = console.log; + var a = ++c; + if (b) b++; + log(a, b); + } + function f2(b, c) { + var log = console.log; + var a = ++c; + b && b++; + log(a, b); + } + function f3(b, c) { + var log = console.log; + var a = ++c; + b ? b++ : b--; + log(a, b); + } + f1(1, 2); + f2(3, 4); + f3(5, 6); + } + expect: { + function f1(b, c) { + if (b) b++; + (0, console.log)(++c, b); + } + function f2(b, c) { + b && b++, + (0, console.log)(++c, b); + } + function f3(b, c) { + b ? b++ : b--, + (0, console.log)(++c, b); + } + f1(1, 2), + f2(3, 4), + f3(5, 6); + } + expect_stdout: [ + "3 2", + "5 4", + "7 6", + ] +} + +cond_branch_2: { + options = { + collapse_vars: true, + sequences: true, + unused: true, + } + input: { + function f1(b, c) { + var log = console.log; + var a = ++c; + if (b) b += a; + log(a, b); + } + function f2(b, c) { + var log = console.log; + var a = ++c; + b && (b += a); + log(a, b); + } + function f3(b, c) { + var log = console.log; + var a = ++c; + b ? b += a : b--; + log(a, b); + } + f1(1, 2); + f2(3, 4); + f3(5, 6); + } + expect: { + function f1(b, c) { + var a = ++c; + if (b) b += a; + (0, console.log)(a, b); + } + function f2(b, c) { + var a = ++c; + b && (b += a), + (0, console.log)(a, b); + } + function f3(b, c) { + var a = ++c; + b ? b += a : b--, + (0, console.log)(a, b); + } + f1(1, 2), + f2(3, 4), + f3(5, 6); + } + expect_stdout: [ + "3 4", + "5 8", + "7 12", + ] +} + +cond_branch_switch: { + options = { + collapse_vars: true, + } + input: { + var c = 0; + if (c = 1 + c, 0) switch (c = 1 + c) { + } + console.log(c); + } + expect: { + var c = 0; + if (c = 1 + c, 0) switch (c = 1 + c) { + } + console.log(c); + } + expect_stdout: "1" +} + +issue_2873_1: { + options = { + collapse_vars: true, + } + input: { + var b = 1, c = 0; + do { + c++; + if (!--b) break; + c = 1 + c; + } while (0); + console.log(b, c); + } + expect: { + var b = 1, c = 0; + do { + c++; + if (!--b) break; + c = 1 + c; + } while (0); + console.log(b, c); + } + expect_stdout: "0 1" +} + +issue_2873_2: { + options = { + collapse_vars: true, + } + input: { + var b = 1, c = 0; + do { + c++; + if (!--b) continue; + c = 1 + c; + } while (0); + console.log(b, c); + } + expect: { + var b = 1, c = 0; + do { + c++; + if (!--b) continue; + c = 1 + c; + } while (0); + console.log(b, c); + } + expect_stdout: "0 1" +} + +issue_2878: { + options = { + collapse_vars: true, + sequences: true, + } + input: { + var c = 0; + (function(a, b) { + function f2() { + if (a) c++; + } + b = f2(); + a = 1; + b && b.b; + f2(); + })(); + console.log(c); + } + expect: { + var c = 0; + (function(a, b) { + function f2() { + if (a) c++; + } + b = f2(), + a = 1, + b && b.b, + f2(); + })(), + console.log(c); + } + expect_stdout: "1" +} + +issue_2891_1: { + options = { + collapse_vars: true, + } + input: { + var a = "PASS", b; + try { + b = c.p = 0; + a = "FAIL"; + b(); + } catch (e) { + } + console.log(a); + } + expect: { + var a = "PASS", b; + try { + b = c.p = 0; + a = "FAIL"; + b(); + } catch (e) { + } + console.log(a); + } + expect_stdout: "PASS" +} + +issue_2891_2: { + options = { + collapse_vars: true, + } + input: { + "use strict"; + var a = "PASS", b; + try { + b = c = 0; + a = "FAIL"; + b(); + } catch (e) { + } + console.log(a); + } + expect: { + "use strict"; + var a = "PASS", b; + try { + b = c = 0; + a = "FAIL"; + b(); + } catch (e) { + } + console.log(a); + } + expect_stdout: true +} + +issue_2908: { + options = { + collapse_vars: true, + } + input: { + var a = 0, b = 0; + function f(c) { + if (1 == c) return; + a++; + if (2 == c) b = a; + } + f(0); + f(2); + console.log(b); + } + expect: { + var a = 0, b = 0; + function f(c) { + if (1 == c) return; + a++; + if (2 == c) b = a; + } + f(0); + f(2); + console.log(b); + } + expect_stdout: "2" +} + +issue_2914_1: { + options = { + collapse_vars: true, + } + input: { + function read(input) { + var i = 0; + var e = 0; + var t = 0; + while (e < 32) { + var n = input[i++]; + t |= (127 & n) << e; + if (0 === (128 & n)) + return t; + e += 7; + } + } + console.log(read([129])); + } + expect: { + function read(input) { + var i = 0; + var e = 0; + var t = 0; + while (e < 32) { + var n = input[i++]; + t |= (127 & n) << e; + if (0 === (128 & n)) + return t; + e += 7; + } + } + console.log(read([129])); + } + expect_stdout: "1" +} + +issue_2914_2: { + options = { + collapse_vars: true, + } + input: { + function read(input) { + var i = 0; + var e = 0; + var t = 0; + while (e < 32) { + var n = input[i++]; + t = (127 & n) << e; + if (0 === (128 & n)) + return t; + e += 7; + } + } + console.log(read([129])); + } + expect: { + function read(input) { + var i = 0; + var e = 0; + var t = 0; + while (e < 32) { + var n = input[i++]; + if (0 === (128 & n)) + return t = (127 & n) << e; + e += 7; + } + } + console.log(read([129])); + } + expect_stdout: "0" +} + +issue_805: { + options = { + collapse_vars: true, + pure_getters: "strict", + reduce_vars: true, + } + input: { + function f() { + function Foo(){} + Foo.prototype = {}; + Foo.prototype.bar = 42; + return Foo; + } + } + expect: { + function f() { + function Foo(){} + (Foo.prototype = {}).bar = 42; + return Foo; + } + } +} + +issue_2931: { + options = { + collapse_vars: true, + unused: true, + } + input: { + console.log(function() { + var a = function() { + return; + }(); + return a; + }()); + } + expect: { + console.log(function() { + return function() { + return; + }(); + }()); + } + expect_stdout: "undefined" +} + +issue_2954_1: { + options = { + collapse_vars: true, + } + input: { + var a = "PASS", b; + try { + do { + b = function() { + throw 0; + }(); + a = "FAIL"; + b && b.c; + } while (0); + } catch (e) { + } + console.log(a); + } + expect: { + var a = "PASS", b; + try { + do { + b = function() { + throw 0; + }(); + a = "FAIL"; + b && b.c; + } while (0); + } catch (e) { + } + console.log(a); + } + expect_stdout: "PASS" +} + +issue_2954_2: { + options = { + collapse_vars: true, + } + input: { + var a = "FAIL_1", b; + try { + throw 0; + } catch (e) { + do { + b = function() { + throw new Error("PASS"); + }(); + a = "FAIL_2"; + b && b.c; + } while (0); + } + console.log(a); + } + expect: { + var a = "FAIL_1", b; + try { + throw 0; + } catch (e) { + do { + a = "FAIL_2"; + (b = function() { + throw new Error("PASS"); + }()) && b.c; + } while (0); + } + console.log(a); + } + expect_stdout: Error("PASS") +} + +issue_2954_3: { + options = { + collapse_vars: true, + } + input: { + var a = "FAIL_1", b; + try { + } finally { + do { + b = function() { + throw new Error("PASS"); + }(); + a = "FAIL_2"; + b && b.c; + } while (0); + } + console.log(a); + } + expect: { + var a = "FAIL_1", b; + try { + } finally { + do { + a = "FAIL_2"; + (b = function() { + throw new Error("PASS"); + }()) && b.c; + } while (0); + } + console.log(a); + } + expect_stdout: Error("PASS") +} + +collapse_rhs_conditional_1: { + options = { + collapse_vars: true, + } + input: { + var a = "PASS", b = "FAIL"; + b = a; + "function" == typeof f && f(a); + console.log(a, b); + } + expect: { + var a = "PASS", b = "FAIL"; + b = a; + "function" == typeof f && f(a); + console.log(a, b); + } + expect_stdout: "PASS PASS" +} + +collapse_rhs_conditional_2: { + options = { + collapse_vars: true, + } + input: { + var a = "FAIL", b; + while ((a = "PASS", --b) && "PASS" == b); + console.log(a, b); + } + expect: { + var a = "FAIL", b; + while ((a = "PASS", --b) && "PASS" == b); + console.log(a, b); + } + expect_stdout: "PASS NaN" +} + +collapse_rhs_lhs_1: { + options = { + collapse_vars: true, + } + input: { + var c = 0; + new function() { + this[c++] = 1; + c += 1; + }(); + console.log(c); + } + expect: { + var c = 0; + new function() { + this[c++] = 1; + c += 1; + }(); + console.log(c); + } + expect_stdout: "2" +} + +collapse_rhs_lhs_2: { + options = { + collapse_vars: true, + } + input: { + var b = 1; + (function f(f) { + f = b; + f[b] = 0; + })(); + console.log("PASS"); + } + expect: { + var b = 1; + (function f(f) { + b[b] = 0; + })(); + console.log("PASS"); + } + expect_stdout: "PASS" +} + +collapse_rhs_loop: { + options = { + collapse_vars: true, + } + input: { + var s; + s = "PASS"; + for (var m, r = /(.*)<\/tpl>/; m = s.match(r);) + s = s.replace(m[0], m[1]); + console.log(s); + } + expect: { + var s; + s = "PASS"; + for (var m, r = /(.*)<\/tpl>/; m = s.match(r);) + s = s.replace(m[0], m[1]); + console.log(s); + } + expect_stdout: "PASS" +} + +collapse_rhs_side_effects: { + options = { + collapse_vars: true, + } + input: { + var a = 1, c = 0; + new function f() { + this[a-- && f()] = 1; + c += 1; + }(); + console.log(c); + } + expect: { + var a = 1, c = 0; + new function f() { + this[a-- && f()] = 1; + c += 1; + }(); + console.log(c); + } + expect_stdout: "2" +} + +collapse_rhs_vardef: { + options = { + collapse_vars: true, + } + input: { + var a, b = 1; + a = --b + function c() { + var b; + c[--b] = 1; + }(); + b |= a; + console.log(a, b); + } + expect: { + var a, b = 1; + a = --b + function c() { + var b; + c[--b] = 1; + }(); + b |= a; + console.log(a, b); + } + expect_stdout: "NaN 0" +} + +collapse_rhs_array: { + options = { + collapse_vars: true, + } + input: { + var a, b; + function f() { + a = []; + b = []; + return []; + } + var c = f(); + console.log(a === b, b === c, c === a); + } + expect: { + var a, b; + function f() { + a = []; + b = []; + return []; + } + var c = f(); + console.log(a === b, b === c, c === a); + } + expect_stdout: "false false false" +} + +collapse_rhs_boolean_1: { + options = { + collapse_vars: true, + evaluate: true, + } + input: { + var a, b; + function f() { + a = !0; + b = !0; + return !0; + } + var c = f(); + console.log(a === b, b === c, c === a); + } + expect: { + var a, b; + function f() { + return b = a = !0; + } + var c = f(); + console.log(a === b, b === c, c === a); + } + expect_stdout: "true true true" +} + +collapse_rhs_boolean_2: { + options = { + collapse_vars: true, + evaluate: true, + } + input: { + var a; + (function f1() { + a = function() {}; + if (/foo/) + console.log(typeof a); + })(); + console.log(function f2() { + a = []; + return !1; + }()); + } + expect: { + var a; + (function f1() { + if (a = function() {}) + console.log(typeof a); + })(); + console.log(function f2() { + return !(a = []); + }()); + } + expect_stdout: [ + "function", + "false", + ] +} + +collapse_rhs_boolean_3: { + options = { + booleans: true, + collapse_vars: true, + conditionals: true, + evaluate: true, + } + input: { + var a, f, g, h, i, n, s, t, x, y; + if (x()) { + n = a; + } else if (y()) { + n = f(); + } else if (s) { + i = false; + n = g(true); + } else if (t) { + i = false; + n = h(true); + } else { + n = []; + } + } + expect: { + var a, f, g, h, i, n, s, t, x, y; + n = x() ? a : y() ? f() : s ? g(!(i = !1)) : t ? h(!(i = !1)) : []; + } +} + +collapse_rhs_function: { + options = { + collapse_vars: true, + } + input: { + var a, b; + function f() { + a = function() {}; + b = function() {}; + return function() {}; + } + var c = f(); + console.log(a === b, b === c, c === a); + } + expect: { + var a, b; + function f() { + a = function() {}; + b = function() {}; + return function() {}; + } + var c = f(); + console.log(a === b, b === c, c === a); + } + expect_stdout: "false false false" +} + +collapse_rhs_number: { + options = { + collapse_vars: true, + evaluate: true, + } + input: { + var a, b; + function f() { + a = 42; + b = 42; + return 42; + } + var c = f(); + console.log(a === b, b === c, c === a); + } + expect: { + var a, b; + function f() { + return b = a = 42; + } + var c = f(); + console.log(a === b, b === c, c === a); + } + expect_stdout: "true true true" +} + +collapse_rhs_object: { + options = { + collapse_vars: true, + } + input: { + var a, b; + function f() { + a = {}; + b = {}; + return {}; + } + var c = f(); + console.log(a === b, b === c, c === a); + } + expect: { + var a, b; + function f() { + a = {}; + b = {}; + return {}; + } + var c = f(); + console.log(a === b, b === c, c === a); + } + expect_stdout: "false false false" +} + +collapse_rhs_regexp: { + options = { + collapse_vars: true, + } + input: { + var a, b; + function f() { + a = /bar/; + b = /bar/; + return /bar/; + } + var c = f(); + console.log(a === b, b === c, c === a); + } + expect: { + var a, b; + function f() { + a = /bar/; + b = /bar/; + return /bar/; + } + var c = f(); + console.log(a === b, b === c, c === a); + } + expect_stdout: "false false false" +} + +collapse_rhs_string: { + options = { + collapse_vars: true, + evaluate: true, + } + input: { + var a, b; + function f() { + a = "foo"; + b = "foo"; + return "foo"; + } + var c = f(); + console.log(a === b, b === c, c === a); + } + expect: { + var a, b; + function f() { + return b = a = "foo"; + } + var c = f(); + console.log(a === b, b === c, c === a); + } + expect_stdout: "true true true" +} + +collapse_rhs_var: { + options = { + collapse_vars: true, + } + input: { + var a, b; + function f() { + a = f; + b = f; + return f; + } + var c = f(); + console.log(a === b, b === c, c === a); + } + expect: { + var a, b; + function f() { + return b = a = f; + } + var c = f(); + console.log(a === b, b === c, c === a); + } + expect_stdout: "true true true" +} + +collapse_rhs_this: { + options = { + collapse_vars: true, + } + input: { + var a, b; + function f() { + a = this; + b = this; + return this; + } + var c = f(); + console.log(a === b, b === c, c === a); + } + expect: { + var a, b; + function f() { + return b = a = this; + } + var c = f(); + console.log(a === b, b === c, c === a); + } + expect_stdout: "true true true" +} + +collapse_rhs_undefined: { + options = { + collapse_vars: true, + side_effects: true, + } + input: { + var a, b; + function f() { + a = void 0; + b = void 0; + return void 0; + } + var c = f(); + console.log(a === b, b === c, c === a); + } + expect: { + var a, b; + function f() { + b = a = void 0; + return; + } + var c = f(); + console.log(a === b, b === c, c === a); + } + expect_stdout: "true true true" +} + +issue_2974: { + options = { + booleans: true, + collapse_vars: true, + evaluate: true, + loops: true, + passes: 2, + pure_getters: "strict", + reduce_vars: true, + sequences: true, + side_effects: true, + unused: true, + } + input: { + var c = 0; + (function f(b) { + var a = 2; + do { + b && b[b]; + b && (b.null = -4); + c++; + } while (b.null && --a > 0); + })(true); + console.log(c); + } + expect: { + var c = 0; + (function(b) { + var a = 2; + for (;c++, (!0).null && --a > 0;); + })(), + console.log(c); + } + expect_stdout: "1" +} + +issue_3032: { + options = { + collapse_vars: true, + pure_getters: true, + } + input: { + console.log({ + f: function() { + this.a = 42; + return [ this.a, !1 ]; + } + }.f()[0]); + } + expect: { + console.log({ + f: function() { + this.a = 42; + return [ this.a, !1 ]; + } + }.f()[0]); + } + expect_stdout: "42" +} + +issue_3096: { + options = { + collapse_vars: true, + } + input: { + console.log(function() { + var ar = ["a", "b"]; + var first = ar.pop(); + return ar + "" + first; + }()); + } + expect: { + console.log(function() { + var ar = ["a", "b"]; + var first = ar.pop(); + return ar + "" + first; + }()); + } + expect_stdout: "ab" +} + +issue_3215_1: { + options = { + collapse_vars: true, + evaluate: true, + ie: false, + inline: true, + passes: 2, + side_effects: true, + unused: true, + } + input: { + console.log(function a() { + var a = 42; + return typeof a; + }()); + } + expect: { + console.log("number"); + } + expect_stdout: "number" +} + +issue_3215_2: { + options = { + collapse_vars: true, + evaluate: true, + ie: true, + inline: true, + passes: 2, + side_effects: true, + unused: true, + } + input: { + console.log(function a() { + var a = 42; + return typeof a; + }()); + } + expect: { + console.log(function a() { + var a = 42; + return typeof a; + }()); + } + expect_stdout: "number" +} + +issue_3215_3: { + options = { + collapse_vars: true, + evaluate: true, + ie: false, + inline: true, + passes: 2, + side_effects: true, + unused: true, + } + input: { + console.log(function() { + var a = 42; + (function a() {}); + return typeof a; + }()); + } + expect: { + console.log("number"); + } + expect_stdout: "number" +} + +issue_3215_4: { + options = { + collapse_vars: true, + evaluate: true, + ie: true, + inline: true, + passes: 2, + side_effects: true, + unused: true, + } + input: { + console.log(function() { + var a = 42; + (function a() {}); + return typeof a; + }()); + } + expect: { + console.log(function() { + var a = 42; + (function a() {}); + return typeof a; + }()); + } + expect_stdout: "number" +} + +issue_3238_1: { + options = { + collapse_vars: true, + unsafe: true, + } + input: { + function f(a) { + var b, c; + if (a) { + b = Object.create(null); + c = Object.create(null); + } + return b === c; + } + console.log(f(0), f(1)); + } + expect: { + function f(a) { + var b, c; + if (a) { + b = Object.create(null); + c = Object.create(null); + } + return b === c; + } + console.log(f(0), f(1)); + } + expect_stdout: "true false" +} + +issue_3238_2: { + options = { + collapse_vars: true, + unsafe: true, + } + input: { + function f(a) { + var b, c; + if (a) { + b = Error(); + c = Error(); + } + return b === c; + } + console.log(f(0), f(1)); + } + expect: { + function f(a) { + var b, c; + if (a) { + b = Error(); + c = Error(); + } + return b === c; + } + console.log(f(0), f(1)); + } + expect_stdout: "true false" +} + +issue_3238_3: { + options = { + collapse_vars: true, + unsafe: true, + } + input: { + function f(a) { + var b, c; + if (a) { + b = new Date(); + c = new Date(); + } + return b === c; + } + console.log(f(0), f(1)); + } + expect: { + function f(a) { + var b, c; + if (a) { + b = new Date(); + c = new Date(); + } + return b === c; + } + console.log(f(0), f(1)); + } + expect_stdout: "true false" +} + +issue_3238_4: { + options = { + collapse_vars: true, + unsafe: true, + } + input: { + function f(a) { + var b, c; + if (a) { + b = a && {}; + c = a && {}; + } + return b === c; + } + console.log(f(0), f(1)); + } + expect: { + function f(a) { + var b, c; + if (a) { + b = a && {}; + c = a && {}; + } + return b === c; + } + console.log(f(0), f(1)); + } + expect_stdout: "true false" +} + +issue_3238_5: { + options = { + collapse_vars: true, + unsafe: true, + } + input: { + function f(a) { + var b, c; + if (a) { + b = a ? [] : 42; + c = a ? [] : 42; + } + return b === c; + } + console.log(f(0), f(1)); + } + expect: { + function f(a) { + var b, c; + if (a) { + b = a ? [] : 42; + c = a ? [] : 42; + } + return b === c; + } + console.log(f(0), f(1)); + } + expect_stdout: "true false" +} + +issue_3238_6: { + options = { + collapse_vars: true, + unsafe: true, + } + input: { + function f(a) { + var b, c; + if (a) { + b = a && 0 || []; + c = a && 0 || []; + } + return b === c; + } + console.log(f(0), f(1)); + } + expect: { + function f(a) { + var b, c; + if (a) { + b = a && 0 || []; + c = a && 0 || []; + } + return b === c; + } + console.log(f(0), f(1)); + } + expect_stdout: "true false" +} + +issue_3247: { + options = { + collapse_vars: true, + } + input: { + function f(o) { + console.log(o.p); + } + var a; + a = Object({ p: "PASS" }); + a.q = true; + f(a, true); + } + expect: { + function f(o) { + console.log(o.p); + } + var a; + (a = Object({ p: "PASS" })).q = true; + f(a, true); + } + expect_stdout: "PASS" +} + +issue_3305: { + options = { + collapse_vars: true, + conditionals: true, + sequences: true, + } + input: { + function calc(a) { + var x, w; + if (a) { + x = a; + w = 1; + } else { + x = 1; + w = 0; + } + return add(x, w); + } + function add(x, w) { + return x + w; + } + console.log(calc(41)); + } + expect: { + function calc(a) { + var x, w; + return w = a ? (x = a, 1) : (x = 1, 0), add(x, w); + } + function add(x, w) { + return x + w; + } + console.log(calc(41)); + } + expect_stdout: "42" +} + +issue_3314: { + options = { + collapse_vars: true, + } + input: { + function test(a, b) { + console.log(a, b); + } + var a = "FAIL", b; + b = a = "PASS"; + test(a, b); + } + expect: { + function test(a, b) { + console.log(a, b); + } + var a = "FAIL", b; + b = a = "PASS"; + test(a, b); + } + expect_stdout: "PASS PASS" +} + +issue_3327: { + options = { + collapse_vars: true, + conditionals: true, + sequences: true, + } + input: { + var a, b, l = ["PASS", 42]; + if (l.length === 1) { + a = l[0].a; + b = l[0].b; + } else { + a = l[0]; + b = l[1]; + } + function echo(a, b) { + console.log(a, b); + } + echo(a, b); + } + expect: { + var a, b, l = ["PASS", 42]; + function echo(a, b) { + console.log(a, b); + } + b = 1 === l.length ? (a = l[0].a, l[0].b) : (a = l[0], l[1]), + echo(a,b); + } + expect_stdout: "PASS 42" +} + +assign_left: { + options = { + collapse_vars: true, + } + input: { + console.log(function(a, b) { + (b = a, b.p).q = "PASS"; + return a.p.q; + }({p: {}})); + } + expect: { + console.log(function(a, b) { + a.p.q = "PASS"; + return a.p.q; + }({p: {}})); + } + expect_stdout: "PASS" +} + +sub_property: { + options = { + collapse_vars: true, + } + input: { + console.log(function(a, b) { + return a[b = a, b.length - 1]; + }([ "FAIL", "PASS" ])); + } + expect: { + console.log(function(a, b) { + return a[a.length - 1]; + }([ "FAIL", "PASS" ])); + } + expect_stdout: "PASS" +} + +assign_undeclared: { + options = { + collapse_vars: true, + toplevel: true, + unused: true, + } + input: { + var A = (console.log(42), function() {}); + B = new A(); + console.log(typeof B); + } + expect: { + console.log(42); + B = new function() {}(); + console.log(typeof B); + } + expect_stdout: [ + "42", + "object", + ] +} + +Infinity_assignment: { + options = { + collapse_vars: true, + pure_getters: "strict", + unsafe: true, + } + input: { + var Infinity; + Infinity = 42; + console.log(Infinity); + } + expect: { + var Infinity; + Infinity = 42; + console.log(Infinity); + } + expect_stdout: true +} + +issue_3439_1: { + options = { + collapse_vars: true, + unused: true, + } + input: { + console.log(typeof function(a) { + function a() {} + return a; + }(42)); + } + expect: { + console.log(typeof function(a) { + function a() {} + return a; + }(42)); + } + expect_stdout: "function" +} + +issue_3439_2: { + options = { + collapse_vars: true, + unused: true, + } + input: { + console.log(typeof function() { + var a = 42; + function a() {} + return a; + }()); + } + expect: { + console.log(typeof function() { + return 42; + }()); + } + expect_stdout: "number" +} + +cond_sequence_return_1: { + options = { + collapse_vars: true, + } + input: { + console.log(function(n) { + var c = 0; + for (var k in [0, 1]) + if (c++, k == n) return c; + }(1)); + } + expect: { + console.log(function(n) { + var c = 0; + for (var k in [0, 1]) + if (c++, k == n) return c; + }(1)); + } + expect_stdout: "2" +} + +cond_sequence_return_2: { + options = { + collapse_vars: true, + } + input: { + console.log(function(n) { + var c = 0; + for (var k in [0, 1]) + if (c += 1, k == n) return c; + }(1)); + } + expect: { + console.log(function(n) { + var c = 0; + for (var k in [0, 1]) + if (c += 1, k == n) return c; + }(1)); + } + expect_stdout: "2" +} + +issue_3520: { + options = { + collapse_vars: true, + reduce_vars: true, + unused: true, + } + input: { + var a = 0; + var b = function(c) { + for (var i = 2; --i >= 0;) { + (function f() { + c = 0; + var i = void 0; + var f = f && f[i]; + })(); + a += b; + c && b++; + } + }(b = 1); + console.log(a); + } + expect: { + var a = 0; + var b = function(c) { + for (var i = 2; --i >= 0;) { + (function() { + c = 0; + var f = f && f[void 0]; + })(); + a += b; + c && b++; + } + }(b = 1); + console.log(a); + } + expect_stdout: "2" +} + +issue_3526_1: { + options = { + collapse_vars: true, + } + input: { + var b = function() { + this.a = "FAIL"; + }(); + var a = "PASS"; + var b; + var c = b; + console.log(a); + } + expect: { + var b = function() { + this.a = "FAIL"; + }(); + var a = "PASS"; + var b; + var c = b; + console.log(a); + } + expect_stdout: "PASS" +} + +issue_3526_2: { + options = { + collapse_vars: true, + } + input: { + function f() { + this.a = "FAIL"; + } + var b = f(); + var a = "PASS"; + var b; + var c = b; + console.log(a); + } + expect: { + function f() { + this.a = "FAIL"; + } + var b = f(); + var a = "PASS"; + var b; + var c = b; + console.log(a); + } + expect_stdout: "PASS" +} + +issue_3562: { + options = { + collapse_vars: true, + conditionals: true, + sequences: true, + } + input: { + function f(a) { + console.log("PASS", a); + } + function g(b) { + console.log("FAIL", b); + } + var h; + var c; + if (console) { + h = f; + c = "PASS"; + } else { + h = g; + c = "FAIL"; + } + h(c); + } + expect: { + function f(a) { + console.log("PASS", a); + } + function g(b) { + console.log("FAIL", b); + } + var h; + var c; + c = console ? (h = f, "PASS") : (h = g, "FAIL"), + h(c); + } + expect_stdout: "PASS PASS" +} + +dot_throw_assign_sequence: { + options = { + collapse_vars: true, + } + input: { + var a = "FAIL"; + try { + var b; + b[0] = (a = "PASS", 0); + a = 1 + a; + } catch (c) { + } + console.log(a); + } + expect: { + var a = "FAIL"; + try { + var b; + b[0] = (a = "PASS", 0); + a = 1 + a; + } catch (c) { + } + console.log(a); + } + expect_stdout: "PASS" +} + +call_assign_order: { + options = { + collapse_vars: true, + } + input: { + var a, b = 1, c = 0, log = console.log; + (function() { + a = b = "PASS"; + })((b = "FAIL", c++)); + log(a, b); + } + expect: { + var a, b = 1, c = 0, log = console.log; + (function() { + a = b = "PASS"; + })((b = "FAIL", c++)); + log(a, b); + } + expect_stdout: "PASS PASS" +} + +issue_3573: { + options = { + collapse_vars: true, + } + input: { + var c = 0; + (function(b) { + while (--b) { + b = NaN; + switch (0 / this < 0) { + case c++, false: + case c++, NaN: + } + } + })(3); + console.log(c); + } + expect: { + var c = 0; + (function(b) { + while (--b) { + b = NaN; + switch (0 / this < 0) { + case c++, false: + case c++, NaN: + } + } + })(3); + console.log(c); + } + expect_stdout: "1" +} + +issue_3581_1: { + options = { + collapse_vars: true, + } + input: { + var a = "PASS", b = "FAIL"; + try { + b = "PASS"; + if (a) throw 0; + b = 1 + b; + a = "FAIL"; + } catch (e) {} + console.log(a, b); + } + expect: { + var a = "PASS", b = "FAIL"; + try { + b = "PASS"; + if (a) throw 0; + b = 1 + b; + a = "FAIL"; + } catch (e) {} + console.log(a, b); + } + expect_stdout: "PASS PASS" +} + +issue_3581_2: { + options = { + collapse_vars: true, + } + input: { + (function() { + var a = "PASS", b = "FAIL"; + try { + b = "PASS"; + if (a) return; + b = 1 + b; + a = "FAIL"; + } finally { + console.log(a, b); + } + })(); + } + expect: { + (function() { + var a = "PASS", b = "FAIL"; + try { + b = "PASS"; + if (a) return; + b = 1 + b; + a = "FAIL"; + } finally { + console.log(a, b); + } + })(); + } + expect_stdout: "PASS PASS" +} + +issue_3596: { + options = { + collapse_vars: true, + pure_getters: "strict", + } + input: { + console.log(function f() { + return f[[ ][f.undefined = 42, 0]] += !1; + }()); + } + expect: { + console.log(function f() { + return f[[ ][f.undefined = 42, 0]] += !1; + }()); + } + expect_stdout: "42" +} + +local_value_replacement: { + options = { + collapse_vars: true, + unused: true, + } + input: { + function f(a, b) { + (a = b) && g(a); + } + function g(c) { + console.log(c); + } + f("FAIL", "PASS"); + } + expect: { + function f(a, b) { + b && g(b); + } + function g(c) { + console.log(c); + } + f("FAIL", "PASS"); + } + expect_stdout: "PASS" +} + +array_in_object_1: { + options = { + collapse_vars: true, + } + input: { + var a = 2; + console.log({ + p: [ a, a-- ], + q: a, + }.q, a); + } + expect: { + var a = 2; + console.log({ + p: [ a, a-- ], + q: a, + }.q, a); + } + expect_stdout: "1 1" +} + +array_in_object_2: { + options = { + collapse_vars: true, + } + input: { + var a = 2; + console.log({ + p: [ a, (a--, 42) ], + q: a, + }.q, a); + } + expect: { + var a = 2; + console.log({ + p: [ a, 42 ], + q: --a, + }.q, a); + } + expect_stdout: "1 1" +} + +array_in_conditional: { + options = { + collapse_vars: true, + } + input: { + var a = 1, b = 2, c; + console.log(c && [ b = a ], a, b); + } + expect: { + var a = 1, b = 2, c; + console.log(c && [ b = a ], a, b); + } + expect_stdout: "undefined 1 2" +} + +object_in_conditional: { + options = { + collapse_vars: true, + } + input: { + var a = 1, b = 2, c; + console.log(c && { + p: b = a + }, a, b); + } + expect: { + var a = 1, b = 2, c; + console.log(c && { + p: b = a + }, a, b); + } + expect_stdout: "undefined 1 2" +} + +sequence_in_iife_1: { + options = { + collapse_vars: true, + } + input: { + var a = "foo", b = 42; + (function() { + var c = (b = a, b); + })(); + console.log(a, b); + } + expect: { + var a = "foo", b = 42; + (function() { + var c = b = a; + })(); + console.log(a, b); + } + expect_stdout: "foo foo" +} + +sequence_in_iife_2: { + options = { + collapse_vars: true, + inline: true, + passes: 2, + side_effects: true, + unsafe: true, + unused: true, + } + input: { + var a = "foo", b = 42; + (function() { + var c = (b = a, b); + })(); + console.log(a, b); + } + expect: { + var a = "foo", b = 42; + console.log(b = a, b); + } + expect_stdout: "foo foo" +} + +sequence_in_iife_3: { + options = { + collapse_vars: true, + inline: true, + passes: 2, + side_effects: true, + toplevel: true, + unsafe: true, + unused: true, + } + input: { + var a = "foo", b = 42; + (function() { + var c = (b = a, b); + })(); + console.log(a, b); + } + expect: { + var a = "foo"; + console.log(a, a); + } + expect_stdout: "foo foo" +} + +retain_assign: { + options = { + collapse_vars: true, + } + input: { + var a = 42, b, c = "FAIL"; + b = a; + b++ && (c = "PASS"); + console.log(c); + } + expect: { + var a = 42, b, c = "FAIL"; + b = a; + b++ && (c = "PASS"); + console.log(c); + } + expect_stdout: "PASS" +} + +getter_side_effect: { + options = { + collapse_vars: true, + } + input: { + var c = "FAIL"; + (function(a) { + var b; + (b = a) && { + get foo() { + a = 0; + } + }.foo; + b && (c = "PASS"); + })(42); + console.log(c); + } + expect: { + var c = "FAIL"; + (function(a) { + var b; + (b = a) && { + get foo() { + a = 0; + } + }.foo; + b && (c = "PASS"); + })(42); + console.log(c); + } + expect_stdout: "PASS" +} + +setter_side_effect: { + options = { + collapse_vars: true, + } + input: { + var c = "FAIL"; + (function(a) { + var b; + (b = a) && ({ + set foo(v) { + a = v; + } + }.foo = 0); + b && (c = "PASS"); + })(42); + console.log(c); + } + expect: { + var c = "FAIL"; + (function(a) { + var b; + (b = a) && ({ + set foo(v) { + a = v; + } + }.foo = 0); + b && (c = "PASS"); + })(42); + console.log(c); + } + expect_stdout: "PASS" +} + +substitution_assign: { + options = { + collapse_vars: true, + unsafe: true, + } + input: { + function f1(a, b) { + f1 = b = a; + console.log(a, b); + } + function f2(a, b) { + a = 1 + (b = a); + console.log(a, b); + } + function f3(a, b) { + b = 1 + (b = a); + console.log(a, b); + } + f1(42, "foo"); + f2(42, "foo"); + f3(42, "foo"); + } + expect: { + function f1(a, b) { + console.log(f1 = a, a); + } + function f2(a, b) { + a = 1 + (b = a); + console.log(a, b); + } + function f3(a, b) { + b = 1 + (b = a); + console.log(a, b); + } + f1(42, "foo"); + f2(42, "foo"); + f3(42, "foo"); + } + expect_stdout: [ + "42 42", + "43 42", + "42 43", + ] +} + +substitution_arithmetic: { + options = { + collapse_vars: true, + } + input: { + function f1(a, b) { + console.log((b = a) + a, b); + } + function f2(a, b) { + console.log(a - (b = a), b); + } + function f3(a, b) { + console.log(a / (b = a) + b, b); + } + f1(42, "foo"); + f2(42, "foo"); + f3(42, "foo"); + } + expect: { + function f1(a, b) { + console.log(a + a, a); + } + function f2(a, b) { + console.log(a - a, a); + } + function f3(a, b) { + console.log(a / a + a, a); + } + f1(42, "foo"); + f2(42, "foo"); + f3(42, "foo"); + } + expect_stdout: [ + "84 42", + "0 42", + "43 42", + ] +} + +substitution_logical_1: { + options = { + collapse_vars: true, + } + input: { + function f1(a, b) { + console.log((b = a) && a, b); + } + function f2(a, b) { + console.log(a && (b = a), b); + } + f1(42, "foo"); + f1(null, true); + f2(42, "foo"); + f2(null, true); + } + expect: { + function f1(a, b) { + console.log(a && a, a); + } + function f2(a, b) { + console.log(a && (b = a), b); + } + f1(42, "foo"); + f1(null, true); + f2(42, "foo"); + f2(null, true); + } + expect_stdout: [ + "42 42", + "null null", + "42 42", + "null true" + ] +} + +substitution_logical_2: { + options = { + collapse_vars: true, + } + input: { + function f1(a, b) { + console.log((b = a) && a && b); + } + function f2(a, b) { + console.log((b = a) && a || b); + } + function f3(a, b) { + console.log((b = a) || a && b); + } + function f4(a, b) { + console.log((b = a) || a || b); + } + f1(42, "foo"); + f1(null, true); + f2(42, "foo"); + f2(null, true); + f3(42, "foo"); + f3(null, true); + f4(42, "foo"); + f4(null, true); + } + expect: { + function f1(a, b) { + console.log(a && a && a); + } + function f2(a, b) { + console.log(a && a || a); + } + function f3(a, b) { + console.log(a || a && a); + } + function f4(a, b) { + console.log(a || a || a); + } + f1(42, "foo"); + f1(null, true); + f2(42, "foo"); + f2(null, true); + f3(42, "foo"); + f3(null, true); + f4(42, "foo"); + f4(null, true); + } + expect_stdout: [ + "42", + "null", + "42", + "null", + "42", + "null", + "42", + "null", + ] +} + +substitution_logical_3: { + options = { + collapse_vars: true, + } + input: { + function f1(a, b) { + console.log(a && (b = a) && b); + } + function f2(a, b) { + console.log(a && (b = a) || b); + } + function f3(a, b) { + console.log(a || (b = a) && b); + } + function f4(a, b) { + console.log(a || (b = a) || b); + } + f1(42, "foo"); + f1(null, true); + f2(42, "foo"); + f2(null, true); + f3(42, "foo"); + f3(null, true); + f4(42, "foo"); + f4(null, true); + } + expect: { + function f1(a, b) { + console.log(a && a && a); + } + function f2(a, b) { + console.log(a && (b = a) || b); + } + function f3(a, b) { + console.log(a || a && a); + } + function f4(a, b) { + console.log(a || a || a); + } + f1(42, "foo"); + f1(null, true); + f2(42, "foo"); + f2(null, true); + f3(42, "foo"); + f3(null, true); + f4(42, "foo"); + f4(null, true); + } + expect_stdout: [ + "42", + "null", + "42", + "true", + "42", + "null", + "42", + "null", + ] +} + +substitution_conditional: { + options = { + collapse_vars: true, + } + input: { + function f1(a, b) { + console.log((b = a) ? a : b, a, b); + } + function f2(a, b) { + console.log(a ? b = a : b, a, b); + } + function f3(a, b) { + console.log(a ? a : b = a, a, b); + } + f1("foo", "bar"); + f1(null, true); + f2("foo", "bar"); + f2(null, true); + f3("foo", "bar"); + f3(null, true); + } + expect: { + function f1(a, b) { + console.log(a ? a : a, a, a); + } + function f2(a, b) { + console.log(a ? b = a : b, a, b); + } + function f3(a, b) { + console.log(a ? a : b = a, a, b); + } + f1("foo", "bar"); + f1(null, true); + f2("foo", "bar"); + f2(null, true); + f3("foo", "bar"); + f3(null, true); + } + expect_stdout: [ + "foo foo foo", + "null null null", + "foo foo foo", + "true null true", + "foo foo bar", + "null null null", + ] +} + +substitution_unary: { + options = { + collapse_vars: true, + } + input: { + function f1(a, b) { + console.log(typeof (b = a), a, b); + } + function f2(a, b) { + console.log(void (b = a), a, b); + } + function f3(a, b) { + console.log(delete (b = a), a, b); + } + f1(42, "foo"); + f2(42, "foo"); + f3(42, "foo"); + } + expect: { + function f1(a, b) { + console.log(typeof a, a, a); + } + function f2(a, b) { + console.log(void a, a, a); + } + function f3(a, b) { + console.log(delete (b = a), a, b); + } + f1(42, "foo"); + f2(42, "foo"); + f3(42, "foo"); + } + expect_stdout: [ + "number 42 42", + "undefined 42 42", + "true 42 42", + ] +} + +issue_3626_1: { + options = { + collapse_vars: true, + } + input: { + var a = "foo", b = 42; + a.p && (b = a) && a; + console.log(a, b); + } + expect: { + var a = "foo", b = 42; + a.p && (b = a) && a; + console.log(a, b); + } + expect_stdout: "foo 42" +} + +issue_3626_2: { + options = { + collapse_vars: true, + conditionals: true, + } + input: { + var a = "foo", b = 42, c = null; + if (a && a.p) + if (b = a) + c++ + a; + console.log(a, b, c); + } + expect: { + var a = "foo", b = 42, c = null; + a && a.p && (b = a) && c++ + a; + console.log(a, b, c); + } + expect_stdout: "foo 42 null" +} + +issue_3628_1: { + options = { + collapse_vars: true, + } + input: { + var a = "bar", b; + ({ + get p() { + a = "foo"; + }, + q: b = a + }).p; + console.log(a, b); + } + expect: { + var a = "bar", b; + ({ + get p() { + a = "foo"; + }, + q: b = a + }).p; + console.log(a, b); + } + expect_stdout: "foo bar" +} + +issue_3628_2: { + options = { + collapse_vars: true, + } + input: { + var a = "bar", b; + ({ + get p() { + a = "foo"; + }, + q: (b = a, 42) + }).p; + console.log(a, b); + } + expect: { + var a = "bar", b; + ({ + get p() { + a = "foo"; + }, + q: (b = a, 42) + }).p; + console.log(a, b); + } + expect_stdout: "foo bar" +} + +issue_3641: { + options = { + collapse_vars: true, + } + input: { + var a, b; + try { + a = "foo"; + b = (a += (A.p = 0, "bar")) % 0; + } catch (e) {} + console.log(a, b); + } + expect: { + var a, b; + try { + a = "foo"; + b = (a += (A.p = 0, "bar")) % 0; + } catch (e) {} + console.log(a, b); + } + expect_stdout: "foo undefined" +} + +issue_3651: { + options = { + collapse_vars: true, + toplevel: true, + } + input: { + var a, b = "PASS"; + try { + a = function() { + try { + var c = 1; + while (0 < --c); + } catch (e) {} finally { + throw 42; + } + }(); + b = "FAIL"; + a.p; + } catch (e) { + console.log(b); + } + } + expect: { + var a, b = "PASS"; + try { + a = function() { + try { + var c = 1; + while (0 < --c); + } catch (e) {} finally { + throw 42; + } + }(); + b = "FAIL"; + a.p; + } catch (e) { + console.log(b); + } + } + expect_stdout: "PASS" +} + +issue_3671: { + options = { + collapse_vars: true, + } + input: { + var a = 0; + try { + a++; + A += 0; + a = 1 + a; + } catch (e) { + console.log(a); + } + } + expect: { + var a = 0; + try { + a++; + A += 0; + a = 1 + a; + } catch (e) { + console.log(a); + } + } + expect_stdout: "1" +} + +call_1: { + options = { + collapse_vars: true, + } + input: { + (function(a) { + a = console; + (function() {})(); + a.log("PASS"); + })(); + } + expect: { + (function(a) { + (function() {})(); + (a = console).log("PASS"); + })(); + } + expect_stdout: "PASS" +} + +call_1_symbol: { + options = { + collapse_vars: true, + reduce_vars: true, + } + input: { + (function(a) { + function f() {} + a = console; + f(); + a.log(typeof f); + })(); + } + expect: { + (function(a) { + function f() {} + f(); + (a = console).log(typeof f); + })(); + } + expect_stdout: "function" +} + +call_2: { + options = { + collapse_vars: true, + } + input: { + (function(a) { + a = console; + (function() { + return 42; + console.log("FAIL"); + })(); + a.log("PASS"); + })(); + } + expect: { + (function(a) { + (function() { + return 42; + console.log("FAIL"); + })(); + (a = console).log("PASS"); + })(); + } + expect_stdout: "PASS" +} + +call_2_symbol: { + options = { + collapse_vars: true, + reduce_vars: true, + } + input: { + (function(a) { + function f() { + return 42; + console.log("FAIL"); + } + a = console; + f(); + a.log(typeof f); + })(); + } + expect: { + (function(a) { + function f() { + return 42; + console.log("FAIL"); + } + f(); + (a = console).log(typeof f); + })(); + } + expect_stdout: "function" +} + +call_3: { + options = { + collapse_vars: true, + } + input: { + (function(a) { + a = console; + (function() { + a = { + log: function() { + console.log("PASS"); + } + } + })(); + a.log("FAIL"); + })(); + } + expect: { + (function(a) { + a = console; + (function() { + a = { + log: function() { + console.log("PASS"); + } + } + })(); + a.log("FAIL"); + })(); + } + expect_stdout: "PASS" +} + +call_3_symbol: { + options = { + collapse_vars: true, + reduce_vars: true, + } + input: { + (function(a) { + function f() { + a = { + log: function() { + console.log(typeof f); + } + } + } + a = console; + f(); + a.log("FAIL"); + })(); + } + expect: { + (function(a) { + function f() { + a = { + log: function() { + console.log(typeof f); + } + } + } + a = console; + f(); + a.log("FAIL"); + })(); + } + expect_stdout: "function" +} + +issue_3698_1: { + options = { + collapse_vars: true, + } + input: { + var log = console.log; + var a, b = 0, c = 0; + (function() { + a = b; + })(b++, (b++, c++)); + log(a, b, c); + } + expect: { + var log = console.log; + var a, b = 0, c = 0; + (function() { + a = b; + })(b++, (b++, c++)); + log(a, b, c); + } + expect_stdout: "2 2 1" +} + +issue_3698_2: { + options = { + collapse_vars: true, + reduce_vars: true, + } + input: { + var log = console.log; + var a, b = 0, c = 0, d = 1; + (function f() { + a = b; + d-- && f(); + })(b++, (b++, c++)); + log(a, b, c, d); + } + expect: { + var log = console.log; + var a, b = 0, c = 0, d = 1; + (function f() { + a = b; + d-- && f(); + })(b++, (b++, c++)); + log(a, b, c, d); + } + expect_stdout: "2 2 1 -1" +} + +issue_3698_3: { + options = { + collapse_vars: true, + reduce_vars: true, + } + input: { + var a = 0, b = 0; + (function f(c) { + { + b++; + var bar_1 = (b = 1 + b, c = 0); + a-- && f(); + } + })(); + console.log(b); + } + expect: { + var a = 0, b = 0; + (function f(c) { + var bar_1 = (b = 1 + ++b, c = 0); + a-- && f(); + })(); + console.log(b); + } + expect_stdout: "2" +} + +issue_3700: { + options = { + collapse_vars: true, + } + input: { + var a = "FAIL"; + try { + a = "PASS"; + (function() { + throw 0; + })(); + a = 1 + a; + } catch (e) { + } + console.log(a); + } + expect: { + var a = "FAIL"; + try { + a = "PASS"; + (function() { + throw 0; + })(); + a = 1 + a; + } catch (e) { + } + console.log(a); + } + expect_stdout: "PASS" +} + +issue_3744: { + options = { + collapse_vars: true, + inline: true, + reduce_vars: true, + unused: true, + } + input: { + (function f(a) { + ({ + get p() { + switch (1) { + case 0: + f((a = 2, 3)); + case 1: + console.log(function g(b) { + return b || "PASS"; + }()); + } + } + }).p; + })(); + } + expect: { + (function f(a) { + ({ + get p() { + switch (1) { + case 0: + f(); + case 1: + console.log(b || "PASS"); + } + var b; + } + }).p; + })(); + } + expect_stdout: "PASS" +} + +assign_value_def: { + options = { + collapse_vars: true, + reduce_vars: true, + unused: true, + } + input: { + function f(a) { + while (1) { + var b = a[0], c = a[1]; + d = b; + e = c; + if (c[0] - e[0] > c[1] - d[1]) break; + return "PASS"; + } + var d, e; + return "FAIL"; + } + console.log(f([ + [ 1, 2 ], + [ 3, 4 ], + ])); + } + expect: { + function f(a) { + while (1) { + var b = a[0], c = a[1]; + if (c[0] - c[0] > c[1] - b[1]) break; + return "PASS"; + } + return "FAIL"; + } + console.log(f([ + [ 1, 2 ], + [ 3, 4 ], + ])); + } + expect_stdout: "PASS" +} + +join_vars_value_def: { + options = { + collapse_vars: true, + join_vars: true, + reduce_vars: true, + unused: true, + } + input: { + function f(a) { + while (1) { + var b = a[0], c = a[1]; + d = b; + e = c; + if (c[0] - e[0] > c[1] - d[1]) break; + return "PASS"; + } + var d, e; + return "FAIL"; + } + console.log(f([ + [ 1, 2 ], + [ 3, 4 ], + ])); + } + expect: { + function f(a) { + while (1) { + var b = a[0], c = a[1]; + if (c[0] - c[0] > c[1] - b[1]) break; + return "PASS"; + } + return "FAIL"; + } + console.log(f([ + [ 1, 2 ], + [ 3, 4 ], + ])); + } + expect_stdout: "PASS" +} + +var_value_def: { + options = { + collapse_vars: true, + reduce_vars: true, + unused: true, + } + input: { + function f(a) { + while (1) { + var b = a[0], c = a[1], d = b, e = c; + if (c[0] - e[0] > c[1] - d[1]) break; + return "PASS"; + } + var d, e; + return "FAIL"; + } + console.log(f([ + [ 1, 2 ], + [ 3, 4 ], + ])); + } + expect: { + function f(a) { + while (1) { + var b = a[0], c = a[1]; + if (c[0] - c[0] > c[1] - b[1]) break; + return "PASS"; + } + return "FAIL"; + } + console.log(f([ + [ 1, 2 ], + [ 3, 4 ], + ])); + } + expect_stdout: "PASS" +} + +mangleable_var: { + options = { + collapse_vars: true, + reduce_vars: true, + unused: true, + } + input: { + function f(a) { + var b = a(), c = a(), d = b; + return c.p(c, d); + } + console.log(f(function() { + return { + p: function() { + return "PASS" + }, + }; + })); + } + expect: { + function f(a) { + var b = a(), c = a(); + return c.p(c, b); + } + console.log(f(function() { + return { + p: function() { + return "PASS"; + } + }; + })); + } + expect_stdout: "PASS" +} + +mangleable_assignment_1: { + options = { + collapse_vars: true, + reduce_vars: true, + unused: true, + } + input: { + var o = { + p: function() { + return 6; + }, + }; + (function() { + var a, b = a = o.p(); + console.log(a * (b / a + b)); + })(); + } + expect: { + var o = { + p: function() { + return 6; + }, + }; + (function() { + var a; + a = o.p(); + console.log(a * (a / a + a)); + })(); + } + expect_stdout: "42" +} + +mangleable_assignment_2: { + options = { + collapse_vars: true, + reduce_vars: true, + unused: true, + } + input: { + var o = { + p: function() { + return 6; + }, + }; + (function(a, b) { + b = a = o.p(); + console.log(a * (b / a + b)); + })(); + } + expect: { + var o = { + p: function() { + return 6; + }, + }; + (function(a, b) { + a = o.p(); + console.log(a * (a / a + a)); + })(); + } + expect_stdout: "42" +} + +issue_3884_1: { + options = { + collapse_vars: true, + evaluate: true, + reduce_vars: true, + side_effects: true, + toplevel: true, + unused: true, + } + input: { + var a = 100, b = 1; + { + a++ + a || a; + b <<= a; + } + console.log(a, b); + } + expect: { + var a = 100; + ++a; + console.log(a, 32); + } + expect_stdout: "101 32" +} + +issue_3884_2: { + options = { + collapse_vars: true, + evaluate: true, + passes: 3, + reduce_vars: true, + side_effects: true, + toplevel: true, + unused: true, + } + input: { + var a = 100, b = 1; + { + a++ + a || a; + b <<= a; + } + console.log(a, b); + } + expect: { + console.log(101, 32); + } + expect_stdout: "101 32" +} + +issue_3891: { + options = { + collapse_vars: true, + passes: 2, + reduce_vars: true, + side_effects: true, + unused: true, + } + input: { + function log(a) { + console.log(typeof a); + } + log(function f() { + try { + do { + var b = function() {}(); + } while (f = 0, b.p); + } catch (e) { + var f; + b; + } + }); + } + expect: { + function log(a) { + console.log(typeof a); + } + log(function() { + try { + do {} while ((void 0).p); + } catch (e) {} + }); + } + expect_stdout: "function" +} + +issue_3894: { + options = { + collapse_vars: true, + inline: true, + reduce_vars: true, + unused: true, + } + input: { + function log(msg) { + console.log(msg ? "FAIL" : "PASS"); + } + var a, c; + (function(b) { + a = c = 0, + log(b); + })(-0); + log(a); + log(c); + } + expect: { + function log(msg) { + console.log(msg ? "FAIL" : "PASS"); + } + var a, c; + void log(-(a = c = 0)); + log(a); + log(c); + } + expect_stdout: [ + "PASS", + "PASS", + "PASS", + ] +} + +issue_3897: { + options = { + collapse_vars: true, + } + input: { + var a = 0; + (function() { + function f(b) { + b = a = 1 + a; + a = 1 + a; + console.log(b); + } + f(); + })(); + console.log(a); + } + expect: { + var a = 0; + (function() { + function f(b) { + b = a = 1 + a; + a = 1 + a; + console.log(b); + } + f(); + })(); + console.log(a); + } + expect_stdout: [ + "1", + "2", + ] +} + +issue_3908: { + options = { + collapse_vars: true, + conditionals: true, + reduce_vars: true, + side_effects: true, + toplevel: true, + unused: true, + } + input: { + if (console) { + var o = { + p: !1 + }, a = o; + } + console.log("PASS"); + } + expect: { + console && 0; + console.log("PASS"); + } + expect_stdout: "PASS" +} + +issue_3927: { + options = { + collapse_vars: true, + } + input: { + var a = 0; + console.log(function(b) { + try { + try { + if (a + (b = "PASS", true)) return; + b.p; + } finally { + return b; + } + } catch (e) { + } + }()); + } + expect: { + var a = 0; + console.log(function(b) { + try { + try { + if (a + (b = "PASS", true)) return; + b.p; + } finally { + return b; + } + } catch (e) {} + }()); + } + expect_stdout: "PASS" +} + +operator_in: { + options = { + collapse_vars: true, + } + input: { + function log(msg) { + console.log(msg); + } + var a = "FAIL"; + try { + a = "PASS"; + 0 in null; + log("FAIL", a); + } catch (e) {} + log(a); + } + expect: { + function log(msg) { + console.log(msg); + } + var a = "FAIL"; + try { + a = "PASS"; + 0 in null; + log("FAIL", a); + } catch (e) {} + log(a); + } + expect_stdout: "PASS" +} + +issue_3971: { + options = { + collapse_vars: true, + evaluate: true, + reduce_vars: true, + side_effects: true, + toplevel: true, + } + input: { + var a = 0 == typeof f, b = 0; + { + var a = void (a++ + (b |= a)); + } + console.log(b); + } + expect: { + var a = 0 == typeof f, b = 0; + var a = void (b |= ++a); + console.log(b); + } + expect_stdout: "1" +} + +issue_3976: { + options = { + collapse_vars: true, + conditionals: true, + evaluate: true, + inline: true, + reduce_vars: true, + unused: true, + } + input: { + function f() { + console.log("FAIL"); + } + (function(a) { + function g() { + if ((a = 0) || f(0)) { + f(); + } else { + f(); + } + if (h(a = 0)); + } + function h() { + g(); + } + })(); + console.log("PASS"); + } + expect: { + function f() { + console.log("FAIL"); + } + void 0; + console.log("PASS"); + } + expect_stdout: "PASS" +} + +issue_4012: { + options = { + collapse_vars: true, + dead_code: true, + evaluate: true, + } + input: { + (function(a) { + try { + throw 2; + } catch (b) { + a = "PASS"; + if (--b) + return; + if (3); + } finally { + console.log(a); + } + })(); + } + expect: { + (function(a) { + try { + throw 2; + } catch (b) { + a = "PASS"; + if (--b) + return; + if (3); + } finally { + console.log(a); + } + })(); + } + expect_stdout: "PASS" +} + +global_assign: { + options = { + collapse_vars: true, + } + input: { + this.A = "FAIL"; + A = "PASS"; + B = "FAIL"; + console.log(A); + } + expect: { + this.A = "FAIL"; + A = "PASS"; + B = "FAIL"; + console.log(A); + } + expect_stdout: "PASS" +} + +global_read: { + options = { + collapse_vars: true, + } + input: { + var a = 0; + a = this.A; + A = 1; + a ? console.log("FAIL") : console.log("PASS"); + } + expect: { + var a = 0; + a = this.A; + A = 1; + a ? console.log("FAIL") : console.log("PASS"); + } + expect_stdout: "PASS" +} + +issue_4038: { + options = { + collapse_vars: true, + } + input: { + var a = 0; + a = this; + a = a.A; + A = 1; + a ? console.log("FAIL") : console.log("PASS"); + } + expect: { + var a = 0; + a = (a = this).A; + A = 1; + a ? console.log("FAIL") : console.log("PASS"); + } + expect_stdout: "PASS" +} + +issue_4040: { + options = { + collapse_vars: true, + toplevel: true, + } + input: { + var a = console.log("PASS") && a.p; + delete NaN; + } + expect: { + var a = console.log("PASS") && a.p; + delete NaN; + } + expect_stdout: "PASS" +} + +issue_4047_1: { + options = { + collapse_vars: true, + evaluate: true, + inline: true, + reduce_vars: true, + sequences: true, + toplevel: true, + unused: true, + } + input: { + var b = 1; + console.log(+function(a) { + b = a; + (a >>= 0) && console.log("PASS"); + }(--b + (0 !== typeof A))); + } + expect: { + var b = 1; + var a; + console.log((a = --b + (0 !== typeof A), +void ((a >>= 0) && console.log("PASS")))); + } + expect_stdout: [ + "PASS", + "NaN", + ] +} + +issue_4047_2: { + options = { + collapse_vars: true, + evaluate: true, + inline: true, + passes: 2, + reduce_vars: true, + sequences: true, + toplevel: true, + unused: true, + } + input: { + var b = 1; + console.log(+function(a) { + b = a; + (a >>= 0) && console.log("PASS"); + }(--b + (0 !== typeof A))); + } + expect: { + var a; + console.log((a = +(0 !== typeof A), +void ((a >>= 0) && console.log("PASS")))); + } + expect_stdout: [ + "PASS", + "NaN", + ] +} + +issue_4051: { + options = { + collapse_vars: true, + } + input: { + try { + var a = (b = b.p, "FAIL"), b = b; + } catch (e) {} + console.log(a); + } + expect: { + try { + var a = (b = b.p, "FAIL"), b = b; + } catch (e) {} + console.log(a); + } + expect_stdout: "undefined" +} + +issue_4070: { + options = { + collapse_vars: true, + pure_getters: "strict", + reduce_vars: true, + } + input: { + console.log(function f() { + function g() {} + g.p++; + return f.p = g.p; + }()); + } + expect: { + console.log(function f() { + function g() {} + return f.p = ++g.p; + }()); + } + expect_stdout: "NaN" +} + +issue_4242: { + options = { + collapse_vars: true, + conditionals: true, + reduce_vars: true, + unused: true, + } + input: { + console.log(function() { + if (console) + var a = function(){}, b = (!1 === console || a)(); + }()); + } + expect: { + console.log(function() { + console && (!1 === console || function(){})(); + }()); + } + expect_stdout: "undefined" +} + +issue_4248: { + options = { + collapse_vars: true, + } + input: { + var a = 0; + try { + a = 1; + b[1]; + } catch (e) { + console.log(a); + } + } + expect: { + var a = 0; + try { + a = 1; + b[1]; + } catch (e) { + console.log(a); + } + } + expect_stdout: "1" +} + +issue_4430_1: { + options = { + collapse_vars: true, + pure_getters: "strict", + } + input: { + function f(a) { + switch (a = 1, arguments[0]) { + case 1: + return "PASS"; + case 2: + return "FAIL"; + } + } + console.log(f(2)); + } + expect: { + function f(a) { + switch (a = 1, arguments[0]) { + case 1: + return "PASS"; + case 2: + return "FAIL"; + } + } + console.log(f(2)); + } + expect_stdout: "PASS" +} + +issue_4430_2: { + options = { + collapse_vars: true, + pure_getters: "strict", + } + input: { + function f(a) { + switch (a = 0, arguments[0]) { + case 0: + return "PASS"; + case 1: + return "FAIL"; + } + } + console.log(f(1)); + } + expect: { + function f(a) { + switch (arguments[a = 0]) { + case 0: + return "PASS"; + case 1: + return "FAIL"; + } + } + console.log(f(1)); + } + expect_stdout: "PASS" +} + +collapse_and_assign: { + options = { + collapse_vars: true, + } + input: { + var log = console.log; + var a = { + p: "PASS", + }; + console && (a = a.p); + log(a); + } + expect: { + var log = console.log; + var a = { + p: "PASS", + }; + log(a = console ? a.p : a); + } + expect_stdout: "PASS" +} + +collapse_and_assign_property: { + options = { + collapse_vars: true, + pure_getters: "strict", + reduce_vars: true, + unused: true, + } + input: { + console.log(function f() { + f && (f.p = "PASS"); + return f.p; + }()); + } + expect: { + console.log(function f() { + return f.p = f ? "PASS" : f.p; + }()); + } + expect_stdout: "PASS" +} + +collapse_or_assign: { + options = { + collapse_vars: true, + } + input: { + var log = console.log; + var a = { + p: "PASS", + }; + a.q || (a = a.p); + log(a); + } + expect: { + var log = console.log; + var a = { + p: "PASS", + }; + log(a = a.q ? a: a.p); + } + expect_stdout: "PASS" +} + +issue_4586_1: { + options = { + collapse_vars: true, + } + input: { + var a = 42; + (function f(b) { + var b = a; + if (b === arguments[0]) + console.log("PASS"); + })(console); + } + expect: { + var a = 42; + (function f(b) { + var b = a; + if (b === arguments[0]) + console.log("PASS"); + })(console); + } + expect_stdout: "PASS" +} + +issue_4586_2: { + options = { + collapse_vars: true, + } + input: { + var a = 42; + (function f(b) { + b = a; + if (b === arguments[0]) + console.log("PASS"); + })(console); + } + expect: { + var a = 42; + (function f(b) { + if ((b = a) === arguments[0]) + console.log("PASS"); + })(console); + } + expect_stdout: "PASS" +} + +issue_4732_1: { + options = { + booleans: true, + collapse_vars: true, + evaluate: true, + reduce_vars: true, + unused: true, + } + input: { + var a = 0; + (function(b) { + var b = a++; + var c = b ? b && console.log("PASS") : 0; + })(a++); + } + expect: { + var a = 0; + (function(b) { + (b = a++) && console.log("PASS"); + })(a++); + } + expect_stdout: "PASS" +} + +issue_4732_2: { + options = { + collapse_vars: true, + conditionals: true, + evaluate: true, + reduce_vars: true, + unused: true, + } + input: { + var a = 0; + (function(b) { + var b = a++; + var c = b ? b && console.log("PASS") : 0; + })(a++); + } + expect: { + var a = 0; + (function(b) { + (b = a++) && b && console.log("PASS"); + })(a++); + } + expect_stdout: "PASS" +} + +dot_in_try: { + options = { + collapse_vars: true, + } + input: { + var o, a = 6, b = 7, c; + try { + c = a * b; + o.p(c); + } catch (e) { + console.log(c); + } + } + expect: { + var o, a = 6, b = 7, c; + try { + c = a * b; + o.p(c); + } catch (e) { + console.log(c); + } + } + expect_stdout: "42" +} + +dot_non_local: { + options = { + collapse_vars: true, + } + input: { + var o, a = 6, b = 7, c; + function f() { + c = a * b; + o.p(c); + } + try { + f(); + } catch (e) { + console.log(c); + } + } + expect: { + var o, a = 6, b = 7, c; + function f() { + c = a * b; + o.p(c); + } + try { + f(); + } catch (e) { + console.log(c); + } + } + expect_stdout: "42" +} + +issue_4806: { + options = { + collapse_vars: true, + reduce_vars: true, + toplevel: true, + } + input: { + var a, o = { + f: function() { + console.log(this === o ? "FAIL" : "PASS"); + }, + }; + (a = 42, o.f)(42); + } + expect: { + var a, o = { + f: function() { + console.log(this === o ? "FAIL" : "PASS"); + }, + }; + (0, o.f)(a = 42); + } + expect_stdout: "PASS" +} + +issue_4852: { + options = { + collapse_vars: true, + unsafe: true, + } + input: { + var a = "PASS"; + (function(b) { + switch (b = a) { + case 42: + try { + console; + } catch (b) { + b.p; + } + case console.log(b): + } + })("FAIL"); + } + expect: { + var a = "PASS"; + (function(b) { + switch (a) { + case 42: + try { + console; + } catch (b) { + b.p; + } + case console.log(a): + } + })("FAIL"); + } + expect_stdout: "PASS" +} + +issue_4865: { + options = { + collapse_vars: true, + } + input: { + var NaN; + var a = NaN = "PASS"; + console.log(a, NaN); + } + expect: { + var NaN; + var a = NaN = "PASS"; + console.log(a, NaN); + } + expect_stdout: true +} + +issue_4868: { + options = { + collapse_vars: true, + unused: true, + } + input: { + var a; + (function(b) { + console.log(b[0]); + })(a = [ "PASS" ], a = [ "FAIL" ]); + } + expect: { + var a; + (function(b) { + console.log(b[0]); + })(a = [ "PASS" ], a = [ "FAIL" ]); + } + expect_stdout: "PASS" +} + +issue_4874: { + options = { + collapse_vars: true, + evaluate: true, + reduce_vars: true, + toplevel: true, + unused: true, + } + input: { + var a; + a = null; + (function(b) { + for (var c in b = b && b[console.log("PASS")]) + console; + })(a = 42); + } + expect: { + null; + (function(b) { + for (var c in 42, 42[console.log("PASS")]) + console; + })(); + } + expect_stdout: "PASS" +} + +issue_4891: { + options = { + collapse_vars: true, + reduce_vars: true, + toplevel: true, + } + input: { + var a = 0, b; + a++; + console.log(b = a, b); + b--; + a.a += 0; + console.log(b); + } + expect: { + var a = 0, b; + a++; + console.log(a, b = a); + b--; + a.a += 0; + console.log(b); + } + expect_stdout: [ + "1 1", + "0", + ] +} + +issue_4895: { + options = { + collapse_vars: true, + toplevel: true, + } + input: { + var a, b; + (function f() { + a = 42; + })(); + console.log((b = a) || b, b += 0); + } + expect: { + var a, b; + (function f() { + a = 42; + })(); + console.log((b = a) || b, b += 0); + } + expect_stdout: "42 42" +} + +issue_4908: { + options = { + collapse_vars: true, + join_vars: true, + reduce_vars: true, + toplevel: true, + } + input: { + var a = 0; + var b; + console || a++; + var c = d = a, d = [ c && c, d += 42 ]; + console.log(d[1]); + } + expect: { + var a = 0, b, c = (console || a++, a), d = [ (d = a) && d, d += 42 ]; + console.log(d[1]); + } + expect_stdout: "42" +} + +issue_4910: { + options = { + collapse_vars: true, + reduce_vars: true, + toplevel: true, + } + input: { + var a = "foo", b; + var c = b = a; + 1 && c[a = "bar"]; + console.log(a, b); + } + expect: { + var a = "foo", b; + var c = b = a; + 1 && b[a = "bar"]; + console.log(a, b); + } + expect_stdout: "bar foo" +} + +issue_4914: { + options = { + collapse_vars: true, + pure_getters: "strict", + } + input: { + console.log(typeof function f() { + f.__proto__ = 42; + return f.__proto__; + }()); + } + expect: { + console.log(typeof function f() { + f.__proto__ = 42; + return f.__proto__; + }()); + } + expect_stdout: "function" +} + +issue_4918: { + options = { + collapse_vars: true, + } + input: { + var a = "FAIL"; + ({ + get 42() { + console.log(a); + } + }[a = "PASS", 42] += "PASS"); + } + expect: { + var a = "FAIL"; + ({ + get 42() { + console.log(a); + } + }[a = "PASS", 42] += "PASS"); + } + expect_stdout: "PASS" +} + +issue_4920_1: { + options = { + collapse_vars: true, + toplevel: true, + } + input: { + var a = "PASS", b; + ({ + get PASS() { + a = "FAIL"; + }, + })[b = a]; + console.log(b); + } + expect: { + var a = "PASS", b; + ({ + get PASS() { + a = "FAIL"; + }, + })[b = a]; + console.log(b); + } + expect_stdout: "PASS" +} + +issue_4920_2: { + options = { + collapse_vars: true, + toplevel: true, + } + input: { + var o = { + get PASS() { + a = "FAIL"; + }, + }; + var a = "PASS", b; + o[b = a]; + console.log(b); + } + expect: { + var o; + var a = "PASS", b; + ({ + get PASS() { + a = "FAIL"; + }, + })[b = a]; + console.log(b); + } + expect_stdout: "PASS" +} + +issue_4920_3: { + options = { + collapse_vars: true, + toplevel: true, + } + input: { + var log = console.log; + var o = { + get PASS() { + a = "FAIL"; + }, + }; + var a = "PASS", b; + o[b = a]; + log(b); + } + expect: { + var log = console.log; + var o; + var a = "PASS", b; + ({ + get PASS() { + a = "FAIL"; + }, + })[b = a]; + log(b); + } + expect_stdout: "PASS" +} + +issue_4920_4: { + options = { + collapse_vars: true, + toplevel: true, + } + input: { + var log = console.log; + var o = { + get [(a = "FAIL 1", "PASS")]() { + a = "FAIL 2"; + }, + }; + var a = "PASS", b; + o[b = a]; + log(b); + } + expect: { + var log = console.log; + var o = { + get [(a = "FAIL 1", "PASS")]() { + a = "FAIL 2"; + }, + }; + var a = "PASS", b; + o[b = a]; + log(b); + } + expect_stdout: "PASS" + node_version: ">=4" +} + +issue_4935: { + options = { + collapse_vars: true, + reduce_vars: true, + toplevel: true, + } + input: { + var a = 1; + var b; + var c = b = a; + console || c(a++); + --b; + console.log(a, b); + } + expect: { + var a = 1; + var b; + var c = b = a; + console || a(a++); + --b; + console.log(a, b); + } + expect_stdout: "1 0" +} + +inline_throw: { + options = { + collapse_vars: true, + inline: true, + keep_fargs: false, + unused: true, + } + input: { + try { + (function() { + return function(a) { + return function(b) { + throw b; + }(a); + }; + })()("PASS"); + } catch (e) { + console.log(e); + } + } + expect: { + try { + (function(a) { + throw a; + return; + })("PASS"); + } catch (e) { + console.log(e); + } + } + expect_stdout: "PASS" +} + +issue_4977_1: { + options = { + collapse_vars: true, + unsafe: true, + } + input: { + var a = "FAIL"; + var o = { + get p() { + return a; + } + }; + a = "PASS"; + console.log(o.p, a); + } + expect: { + var a = "FAIL"; + var o = { + get p() { + return a; + } + }; + a = "PASS"; + console.log(o.p, a); + } + expect_stdout: "PASS PASS" +} + +issue_4977_2: { + options = { + collapse_vars: true, + reduce_vars: true, + unsafe: true, + } + input: { + var a, o = { + get p() { + return a = "PASS"; + }, + }; + if (console) { + var a = "FAIL"; + console.log(o.p, a); + } + } + expect: { + var a, o = { + get p() { + return a = "PASS"; + }, + }; + if (console) { + var a = "FAIL"; + console.log(o.p, a); + } + } + expect_stdout: "PASS PASS" +} + +issue_5112_1: { + options = { + collapse_vars: true, + } + input: { + console.log(function(a) { + try { + try { + if (console + (a = "PASS", "")) + return "FAIL 1"; + a.p; + } catch (e) {} + } finally { + return a; + } + }("FAIL 2")); + } + expect: { + console.log(function(a) { + try { + try { + if (console + (a = "PASS", "")) + return "FAIL 1"; + a.p; + } catch (e) {} + } finally { + return a; + } + }("FAIL 2")); + } + expect_stdout: "PASS" +} + +issue_5112_2: { + options = { + collapse_vars: true, + } + input: { + console.log(function(a) { + try { + return function() { + try { + if (console + (a = "PASS", "")) + return "FAIL 1"; + a.p; + } catch (e) {} + }(); + } finally { + return a; + } + }("FAIL 2")); + } + expect: { + console.log(function(a) { + try { + return function() { + try { + if (console + (a = "PASS", "")) + return "FAIL 1"; + a.p; + } catch (e) {} + }(); + } finally { + return a; + } + }("FAIL 2")); + } + expect_stdout: "PASS" +} + +issue_5182: { + options = { + arrows: true, + collapse_vars: true, + evaluate: true, + hoist_props: true, + inline: true, + merge_vars: true, + passes: 4, + reduce_vars: true, + sequences: true, + side_effects: true, + toplevel: true, + unused: true, + } + input: { + var con = console; + global.log = con.log; + var jump = function(x) { + console.log("JUMP:", x * 10); + return x + x; + }; + var jump2 = jump; + var run = function(x) { + console.log("RUN:", x * -10); + return x * x; + }; + var run2 = run; + var bar = (x, y) => { + console.log("BAR:", x + y); + return x - y; + }; + var bar2 = bar; + var obj = { + foo: bar2, + go: run2, + not_used: jump2, + }; + console.log(obj.foo(1, 2), global.log("PASS")); + } + expect: { + var con = console; + global.log = con.log, + console.log((console.log("BAR:", 3), -1), global.log("PASS")); + } + expect_stdout: [ + "BAR: 3", + "PASS", + "-1 undefined", + ] + node_version: ">=4" +} + +issue_5273: { + options = { + collapse_vars: true, + evaluate: true, + inline: true, + reduce_vars: true, + sequences: true, + toplevel: true, + } + input: { + var a = "10", b = 1; + function f(c, d) { + return d; + } + f((b += a, b *= a), f); + console.log(b); + } + expect: { + var a = "10", b = 1; + function f(c, d) { + return d; + } + b = 1100, + f, + console.log(b); + } + expect_stdout: "1100" +} + +issue_5276: { + options = { + collapse_vars: true, + pure_getters: "strict", + reduce_vars: true, + toplevel: true, + } + input: { + var a = A = "PASS"; + a.p += null; + a.p -= 42; + console.log(a); + } + expect: { + var a = A = "PASS"; + a.p += null; + a.p -= 42; + console.log(a); + } + expect_stdout: "PASS" +} + +issue_5277: { + options = { + collapse_vars: true, + reduce_vars: true, + unused: true, + } + input: { + console.log(function() { + var a = function() { + a += null; + a -= 42; + }; + }()); + } + expect: { + console.log(function() {}()); + } + expect_stdout: "undefined" +} + +issue_5309_1: { + options = { + collapse_vars: true, + conditionals: true, + toplevel: true, + } + input: { + if (console) + var a = (console.log("PASS"), b), b = a; + else + console.log("FAIL"); + } + expect: { + var a, b; + console ? (console.log("PASS"), b = b) : console.log("FAIL"); + } + expect_stdout: "PASS" +} + +issue_5309_2: { + options = { + collapse_vars: true, + toplevel: true, + } + input: { + var a, b; + console ? (a = (console.log("PASS"), b), b = a) : console.log("FAIL"); + } + expect: { + var a, b; + console ? (console.log("PASS"), b = b) : console.log("FAIL"); + } + expect_stdout: "PASS" +} + +issue_5394: { + options = { + collapse_vars: true, + evaluate: true, + } + input: { + try { + throw A.p = (console.log("FAIL"), []), !1; + } catch (e) { + console.log(typeof e); + } + } + expect: { + try { + throw !(A.p = (console.log("FAIL"), [])); + } catch (e) { + console.log(typeof e); + } + } + expect_stdout: "object" +} + +issue_5396: { + options = { + collapse_vars: true, + merge_vars: true, + reduce_vars: true, + side_effects: true, + unused: true, + } + input: { + var a, b; + function f() {} + b = 0; + new function g(c) { + var d = a && g(e), e = ++d, i = [ 42 ]; + for (var j in i) + console.log("PASS"), + i; + }(); + } + expect: { + var a, b; + function f() {} + b = 0; + (function g(c) { + a && g(); + for (var j in [ 42 ]) + console.log("PASS"); + })(); + } + expect_stdout: "PASS" +} + +issue_5568: { + options = { + collapse_vars: true, + evaluate: true, + reduce_vars: true, + toplevel: true, + unused: true, + } + input: { + A = "FAIL"; + var a = (A = "PASS", !1); + for (var b in a); + console.log(A); + } + expect: { + A = "FAIL"; + for (var b in !(A = "PASS")); + console.log(A); + } + expect_stdout: "PASS" +} + +issue_5638_1: { + options = { + collapse_vars: true, + reduce_vars: true, + toplevel: true, + } + input: { + var a; + console; + a = [ 42 ]; + console || FAIL(a); + console.log(a++); + } + expect: { + var a; + console; + a = [ 42 ]; + console || FAIL(a); + console.log(a++); + } + expect_stdout: "42" +} + +issue_5638_2: { + options = { + collapse_vars: true, + reduce_vars: true, + toplevel: true, + } + input: { + var a; + console; + a = [ 6 ]; + console || FAIL(a); + console.log(a *= 7); + } + expect: { + var a; + console; + a = [ 6 ]; + console || FAIL(a); + console.log(a *= 7); + } + expect_stdout: "42" +} + +issue_5638_3: { + options = { + collapse_vars: true, + pure_getters: "strict", + reduce_vars: true, + toplevel: true, + } + input: { + var log = console.log; + var a = { foo: 42 }, b; + for (var k in a) { + b = a[k]; + log(k || b, b++); + } + } + expect: { + var log = console.log; + var a = { foo: 42 }, b; + for (var k in a) { + b = a[k]; + log(k || b, b++); + } + } + expect_stdout: "foo 42" +} + +issue_5638_4: { + options = { + collapse_vars: true, + pure_getters: "strict", + reduce_vars: true, + toplevel: true, + } + input: { + var log = console.log; + var a = { foo: 6 }, b; + for (var k in a) { + b = a[k]; + log(k || b, b *= 7); + } + } + expect: { + var log = console.log; + var a = { foo: 6 }, b; + for (var k in a) { + b = a[k]; + log(k || b, b *= 7); + } + } + expect_stdout: "foo 42" +} + +issue_5643: { + options = { + collapse_vars: true, + reduce_vars: true, + toplevel: true, + } + input: { + var a = 3, b; + a *= 7; + b = !!this; + console || console.log(b); + console.log(a * ++b); + } + expect: { + var a = 3, b; + a *= 7; + b = !!this; + console || console.log(b); + console.log(a * ++b); + } + expect_stdout: "42" +} + +issue_5719: { + options = { + collapse_vars: true, + reduce_vars: true, + toplevel: true, + } + input: { + var a = 42, b; + switch (b = a) { + case a: + case b: + case a++: + } + console.log(a === b++ ? "PASS" : "FAIL"); + } + expect: { + var a = 42, b; + switch (b = a) { + case a: + case b: + case a++: + } + console.log(a === b++ ? "PASS" : "FAIL"); + } + expect_stdout: "PASS" +} + +issue_5779: { + options = { + collapse_vars: true, + evaluate: true, + pure_getters: "strict", + reduce_vars: true, + toplevel: true, + } + input: { + var a = A = "foo"; + a.p = 42; + if (a && !a.p) + console.log("PASS"); + } + expect: { + var a = A = "foo"; + a.p = 42; + if (a, !a.p) + console.log("PASS"); + } + expect_stdout: "PASS" +} diff --git a/test/compress/comparisons.js b/test/compress/comparisons.js new file mode 100644 index 00000000000..bc860ce9a0a --- /dev/null +++ b/test/compress/comparisons.js @@ -0,0 +1,543 @@ +comparisons: { + options = { + comparisons: true, + } + input: { + var obj1, obj2; + var result1 = obj1 <= obj2; + var result2 = obj1 < obj2; + var result3 = obj1 >= obj2; + var result4 = obj1 > obj2; + } + expect: { + var obj1, obj2; + var result1 = obj1 <= obj2; + var result2 = obj1 < obj2; + var result3 = obj2 <= obj1; + var result4 = obj2 < obj1; + } +} + +unsafe_comps: { + options = { + comparisons: true, + conditionals: true, + unsafe_comps: true, + } + input: { + var obj1, obj2; + obj1 <= obj2 ? f1() : g1(); + obj1 < obj2 ? f2() : g2(); + obj1 >= obj2 ? f3() : g3(); + obj1 > obj2 ? f4() : g4(); + } + expect: { + var obj1, obj2; + (obj2 < obj1 ? g1 : f1)(); + (obj1 < obj2 ? f2 : g2)(); + (obj1 < obj2 ? g3 : f3)(); + (obj2 < obj1 ? f4 : g4)(); + } +} + +unsafe_in_instanceof: { + options = { + side_effects: true, + unsafe_comps: true, + } + input: { + var a; + 42 in a; + f() instanceof "foo"; + } + expect: { + var a; + f(); + } +} + +dont_change_in_or_instanceof_expressions: { + input: { + 1 in 1; + null in null; + 1 instanceof 1; + null instanceof null; + } + expect: { + 1 in 1; + null in null; + 1 instanceof 1; + null instanceof null; + } +} + +self_comparison_1: { + options = { + comparisons: true, + } + input: { + a === a; + a !== b; + b.c === a.c; + b.c !== b.c; + } + expect: { + a == a; + a !== b; + b.c === a.c; + b.c != b.c; + } +} + +self_comparison_2: { + options = { + comparisons: true, + reduce_funcs: true, + reduce_vars: true, + toplevel: true, + } + input: { + function f() {} + var o = {}; + console.log(f != f, o === o); + } + expect: { + function f() {} + var o = {}; + console.log(false, true); + } + expect_stdout: "false true" +} + +self_comparison_3: { + options = { + comparisons: true, + } + input: { + var a; + function f() { + var b = a; + a = null; + return b; + } + for (var i = 0; i < 2; i++) + console.log(f() === f()); + } + expect: { + var a; + function f() { + var b = a; + a = null; + return b; + } + for (var i = 0; i < 2; i++) + console.log(f() === f()); + } + expect_stdout: [ + "false", + "true", + ] +} + +self_comparison_4: { + options = { + booleans: true, + comparisons: true, + reduce_vars: true, + toplevel: true, + unused: true, + } + input: { + var o = {}; + console.log(o == o, o != o); + console.log(o === o, o !== o); + } + expect: { + console.log(!0, !1); + console.log(!0, !1); + } + expect_stdout: [ + "true false", + "true false", + ] +} + +issue_2857_1: { + options = { + comparisons: true, + } + input: { + function f1(a) { + a === undefined || a === null; + a === undefined || a !== null; + a !== undefined || a === null; + a !== undefined || a !== null; + a === undefined && a === null; + a === undefined && a !== null; + a !== undefined && a === null; + a !== undefined && a !== null; + } + function f2(a) { + a === null || a === undefined; + a === null || a !== undefined; + a !== null || a === undefined; + a !== null || a !== undefined; + a === null && a === undefined; + a === null && a !== undefined; + a !== null && a === undefined; + a !== null && a !== undefined; + } + } + expect: { + function f1(a) { + null == a; + void 0 === a || null !== a; + void 0 !== a || null === a; + void 0 !== a || null !== a; + void 0 === a && null === a; + void 0 === a && null !== a; + void 0 !== a && null === a; + null != a; + } + function f2(a) { + null == a; + null === a || void 0 !== a; + null !== a || void 0 === a; + null !== a || void 0 !== a; + null === a && void 0 === a; + null === a && void 0 !== a; + null !== a && void 0 === a; + null != a; + } + } +} + +issue_2857_2: { + options = { + comparisons: true, + } + input: { + function f(a, p) { + a === undefined || a === null || p; + a === undefined || a !== null || p; + a !== undefined || a === null || p; + a !== undefined || a !== null || p; + a === undefined && a === null || p; + a === undefined && a !== null || p; + a !== undefined && a === null || p; + a !== undefined && a !== null || p; + } + } + expect: { + function f(a, p) { + null == a || p; + void 0 === a || null !== a || p; + void 0 !== a || null === a || p; + void 0 !== a || null !== a || p; + void 0 === a && null === a || p; + void 0 === a && null !== a || p; + void 0 !== a && null === a || p; + null != a || p; + } + } +} + +issue_2857_3: { + options = { + comparisons: true, + } + input: { + function f(a, p) { + a === undefined || a === null && p; + a === undefined || a !== null && p; + a !== undefined || a === null && p; + a !== undefined || a !== null && p; + a === undefined && a === null && p; + a === undefined && a !== null && p; + a !== undefined && a === null && p; + a !== undefined && a !== null && p; + } + } + expect: { + function f(a, p) { + void 0 === a || null === a && p; + void 0 === a || null !== a && p; + void 0 !== a || null === a && p; + void 0 !== a || null !== a && p; + void 0 === a && null === a && p; + void 0 === a && null !== a && p; + void 0 !== a && null === a && p; + null != a && p; + } + } +} + +issue_2857_4: { + options = { + comparisons: true, + conditionals: true, + } + input: { + function f(a, p) { + p || a === undefined || a === null; + p || a === undefined || a !== null; + p || a !== undefined || a === null; + p || a !== undefined || a !== null; + p || a === undefined && a === null; + p || a === undefined && a !== null; + p || a !== undefined && a === null; + p || a !== undefined && a !== null; + } + } + expect: { + function f(a, p) { + p || null == a; + p || void 0 === a || null !== a; + p || void 0 !== a || null === a; + p || void 0 !== a || null !== a; + p || void 0 === a && null === a; + p || void 0 === a && null !== a; + p || void 0 !== a && null === a; + p || null != a; + } + } +} + +issue_2857_5: { + options = { + comparisons: true, + conditionals: true, + } + input: { + function f(a, p) { + p && a === undefined || a === null; + p && a === undefined || a !== null; + p && a !== undefined || a === null; + p && a !== undefined || a !== null; + p && a === undefined && a === null; + p && a === undefined && a !== null; + p && a !== undefined && a === null; + p && a !== undefined && a !== null; + } + } + expect: { + function f(a, p) { + p && void 0 === a || null === a; + p && void 0 === a || null !== a; + p && void 0 !== a || null === a; + p && void 0 !== a || null !== a; + p && void 0 === a && null === a; + p && void 0 === a && null !== a; + p && void 0 !== a && null === a; + p && null != a; + } + } +} + +issue_2857_6: { + options = { + comparisons: true, + pure_getters: "strict", + reduce_vars: true, + } + input: { + function f(a) { + if (({}).b === undefined || {}.b === null) + return a.b !== undefined && a.b !== null; + } + console.log(f({ + a: [ null ], + get b() { + return this.a.shift(); + } + })); + } + expect: { + function f(a) { + if (null == {}.b) + return void 0 !== a.b && null !== a.b; + } + console.log(f({ + a: [ null ], + get b() { + return this.a.shift(); + } + })); + } + expect_stdout: "true" +} + +is_boolean_unsafe: { + options = { + comparisons: true, + unsafe: true, + } + input: { + console.log(/foo/.test("bar") === [].isPrototypeOf({})); + } + expect: { + console.log(/foo/.test("bar") == [].isPrototypeOf({})); + } + expect_stdout: "true" +} + +is_number_unsafe: { + options = { + comparisons: true, + unsafe: true, + } + input: { + console.log(Math.acos(42) !== "foo".charCodeAt(4)); + } + expect: { + console.log(Math.acos(42) != "foo".charCodeAt(4)); + } + expect_stdout: "true" +} + +is_boolean_var: { + options = { + comparisons: true, + reduce_vars: true, + } + input: { + console.log(function(a, b) { + for (var i = 0, c = !b; i < a.length; i++) + if (!a[i] === c) + return i; + }([ false, true ], 42)); + } + expect: { + console.log(function(a, b) { + for (var i = 0, c = !b; i < a.length; i++) + if (!a[i] == c) + return i; + }([ false, true ], 42)); + } + expect_stdout: "1" +} + +is_defined: { + options = { + comparisons: true, + } + input: { + console.log(function a() { + return void 0 === a; + }()); + } + expect: { + console.log(function a() { + return a, false; + }()); + } + expect_stdout: "false" + expect_warnings: [ + "WARN: Expression always defined [test/compress/comparisons.js:2,19]", + ] +} + +unsafe_indexOf: { + options = { + booleans: true, + comparisons: true, + unsafe: true, + } + input: { + var a = Object.keys({ foo: 42 }); + if (a.indexOf("bar") < 0) console.log("PASS"); + if (0 > a.indexOf("bar")) console.log("PASS"); + if (a.indexOf("foo") >= 0) console.log("PASS"); + if (0 <= a.indexOf("foo")) console.log("PASS"); + if (a.indexOf("foo") > -1) console.log("PASS"); + if (-1 < a.indexOf("foo")) console.log("PASS"); + if (a.indexOf("bar") == -1) console.log("PASS"); + if (-1 == a.indexOf("bar")) console.log("PASS"); + if (a.indexOf("bar") === -1) console.log("PASS"); + if (-1 === a.indexOf("bar")) console.log("PASS"); + if (a.indexOf("foo") != -1) console.log("PASS"); + if (-1 != a.indexOf("foo")) console.log("PASS"); + if (a.indexOf("foo") !== -1) console.log("PASS"); + if (-1 !== a.indexOf("foo")) console.log("PASS"); + } + expect: { + var a = Object.keys({ foo: 42 }); + if (!~a.indexOf("bar")) console.log("PASS"); + if (!~a.indexOf("bar")) console.log("PASS"); + if (~a.indexOf("foo")) console.log("PASS"); + if (~a.indexOf("foo")) console.log("PASS"); + if (~a.indexOf("foo")) console.log("PASS"); + if (~a.indexOf("foo")) console.log("PASS"); + if (!~a.indexOf("bar")) console.log("PASS"); + if (!~a.indexOf("bar")) console.log("PASS"); + if (!~a.indexOf("bar")) console.log("PASS"); + if (!~a.indexOf("bar")) console.log("PASS"); + if (~a.indexOf("foo")) console.log("PASS"); + if (~a.indexOf("foo")) console.log("PASS"); + if (~a.indexOf("foo")) console.log("PASS"); + if (~a.indexOf("foo")) console.log("PASS"); + } + expect_stdout: [ + "PASS", + "PASS", + "PASS", + "PASS", + "PASS", + "PASS", + "PASS", + "PASS", + "PASS", + "PASS", + "PASS", + "PASS", + "PASS", + "PASS", + ] +} + +issue_3413: { + options = { + comparisons: true, + evaluate: true, + side_effects: true, + } + input: { + var b; + void 0 !== ("" < b || void 0) || console.log("PASS"); + } + expect: { + var b; + void 0 === ("" < b || void 0) && console.log("PASS"); + } + expect_stdout: "PASS" +} + +nullish_assign: { + options = { + comparisons: true, + } + input: { + var a; + void 0 !== (a = "PASS".split("")) && null !== a && console.log(a.join("-")); + } + expect: { + var a; + null != (a = "PASS".split("")) && console.log(a.join("-")); + } + expect_stdout: "P-A-S-S" +} + +nullish_chain: { + options = { + comparisons: true, + conditionals: true, + } + input: { + var a; + A || B || void 0 === a || null === a || C; + } + expect: { + var a; + A || B || null == a || C; + } +} diff --git a/test/compress/concat-strings.js b/test/compress/concat-strings.js index 79192987601..ac252b06c40 100644 --- a/test/compress/concat-strings.js +++ b/test/compress/concat-strings.js @@ -1,7 +1,7 @@ concat_1: { options = { - evaluate: true - }; + evaluate: true, + } input: { var a = "foo" + "bar" + x() + "moo" + "foo" + y() + "x" + "y" + "z" + q(); var b = "foo" + 1 + x() + 2 + "boo"; @@ -11,6 +11,9 @@ concat_1: { var d = 1 + x() + 2 + 3 + "boo"; var e = 1 + x() + 2 + "X" + 3 + "boo"; + + // be careful with concatenation with "\0" with octal-looking strings. + var f = "\0" + 360 + "\0" + 8 + "\0"; } expect: { var a = "foobar" + x() + "moofoo" + y() + "xyz" + q(); @@ -18,5 +21,303 @@ concat_1: { var c = 1 + x() + 2 + "boo"; var d = 1 + x() + 2 + 3 + "boo"; var e = 1 + x() + 2 + "X3boo"; + var f = "\x00360\x008\0"; + } +} + +concat_2: { + options = { + strings: true, + } + input: { + console.log( + 1 + (2 + 3), + 1 + (2 + "3"), + 1 + ("2" + 3), + 1 + ("2" + "3"), + "1" + (2 + 3), + "1" + (2 + "3"), + "1" + ("2" + 3), + "1" + ("2" + "3") + ); + } + expect: { + console.log( + 1 + (2 + 3), + 1 + (2 + "3"), + 1 + "2" + 3, + 1 + "2" + "3", + "1" + (2 + 3), + "1" + 2 + "3", + "1" + "2" + 3, + "1" + "2" + "3" + ); + } + expect_stdout: true +} + +concat_3: { + options = { + strings: true, + } + input: { + console.log( + 1 + 2 + (3 + 4 + 5), + 1 + 2 + (3 + 4 + "5"), + 1 + 2 + (3 + "4" + 5), + 1 + 2 + (3 + "4" + "5"), + 1 + 2 + ("3" + 4 + 5), + 1 + 2 + ("3" + 4 + "5"), + 1 + 2 + ("3" + "4" + 5), + 1 + 2 + ("3" + "4" + "5") + ); + } + expect: { + console.log( + 1 + 2 + (3 + 4 + 5), + 1 + 2 + (3 + 4 + "5"), + 1 + 2 + (3 + "4") + 5, + 1 + 2 + (3 + "4") + "5", + 1 + 2 + "3" + 4 + 5, + 1 + 2 + "3" + 4 + "5", + 1 + 2 + "3" + "4" + 5, + 1 + 2 + "3" + "4" + "5" + ); + } + expect_stdout: true +} + +concat_4: { + options = { + strings: true, + } + input: { + console.log( + 1 + "2" + (3 + 4 + 5), + 1 + "2" + (3 + 4 + "5"), + 1 + "2" + (3 + "4" + 5), + 1 + "2" + (3 + "4" + "5"), + 1 + "2" + ("3" + 4 + 5), + 1 + "2" + ("3" + 4 + "5"), + 1 + "2" + ("3" + "4" + 5), + 1 + "2" + ("3" + "4" + "5") + ); + } + expect: { + console.log( + 1 + "2" + (3 + 4 + 5), + 1 + "2" + (3 + 4) + "5", + 1 + "2" + 3 + "4" + 5, + 1 + "2" + 3 + "4" + "5", + 1 + "2" + "3" + 4 + 5, + 1 + "2" + "3" + 4 + "5", + 1 + "2" + "3" + "4" + 5, + 1 + "2" + "3" + "4" + "5" + ); + } + expect_stdout: true +} + +concat_5: { + options = { + strings: true, + } + input: { + console.log( + "1" + 2 + (3 + 4 + 5), + "1" + 2 + (3 + 4 + "5"), + "1" + 2 + (3 + "4" + 5), + "1" + 2 + (3 + "4" + "5"), + "1" + 2 + ("3" + 4 + 5), + "1" + 2 + ("3" + 4 + "5"), + "1" + 2 + ("3" + "4" + 5), + "1" + 2 + ("3" + "4" + "5") + ); + } + expect: { + console.log( + "1" + 2 + (3 + 4 + 5), + "1" + 2 + (3 + 4) + "5", + "1" + 2 + 3 + "4" + 5, + "1" + 2 + 3 + "4" + "5", + "1" + 2 + "3" + 4 + 5, + "1" + 2 + "3" + 4 + "5", + "1" + 2 + "3" + "4" + 5, + "1" + 2 + "3" + "4" + "5" + ); + } + expect_stdout: true +} + +concat_6: { + options = { + strings: true, + } + input: { + console.log( + "1" + "2" + (3 + 4 + 5), + "1" + "2" + (3 + 4 + "5"), + "1" + "2" + (3 + "4" + 5), + "1" + "2" + (3 + "4" + "5"), + "1" + "2" + ("3" + 4 + 5), + "1" + "2" + ("3" + 4 + "5"), + "1" + "2" + ("3" + "4" + 5), + "1" + "2" + ("3" + "4" + "5") + ); + } + expect: { + console.log( + "1" + "2" + (3 + 4 + 5), + "1" + "2" + (3 + 4) + "5", + "1" + "2" + 3 + "4" + 5, + "1" + "2" + 3 + "4" + "5", + "1" + "2" + "3" + 4 + 5, + "1" + "2" + "3" + 4 + "5", + "1" + "2" + "3" + "4" + 5, + "1" + "2" + "3" + "4" + "5" + ); + } + expect_stdout: true +} + +concat_7: { + options = { + strings: true, + } + input: { + console.log( + "" + 1, + "" + "1", + "" + 1 + 2, + "" + 1 + "2", + "" + "1" + 2, + "" + "1" + "2", + "" + (x += "foo") + ); + } + expect: { + console.log( + "" + 1, + "1", + "" + 1 + 2, + 1 + "2", + "1" + 2, + "1" + "2", + x += "foo" + ); + } + expect_stdout: true +} + +concat_8: { + options = { + strings: true, + } + input: { + console.log( + 1 + "", + "1" + "", + 1 + 2 + "", + 1 + "2" + "", + "1" + 2 + "", + "1" + "2" + "", + (x += "foo") + "" + ); + } + expect: { + console.log( + 1 + "", + "1", + 1 + 2 + "", + 1 + "2", + "1" + 2, + "1" + "2", + x += "foo" + ); + } + expect_stdout: true +} + +concat_9: { + options = { + booleans: true, + evaluate: true, + reduce_vars: true, + strings: true, + toplevel: true, + } + input: { + var a = "foo"; + console.log( + 12 + (34 + a), + null + (34 + a), + 12 + (null + a), + false + (34 + a), + 12 + (false + a), + "bar" + (34 + a), + 12 + ("bar" + a) + ); + } + expect: { + var a = "foo"; + console.log( + "1234" + a, + "null34" + a, + "12null" + a, + !1 + (34 + a), + 12 + (!1 + a), + "bar34" + a, + "12bar" + a + ); + } + expect_stdout: true +} + +concat_sequence: { + options = { + collapse_vars: true, + strings: true, + toplevel: true, + unused: true, + } + input: { + var a; + console.log(12 + (a = null, "34" + a)); + } + expect: { + console.log(12 + "34" + null); + } + expect_stdout: "1234null" +} + +issue_3689: { + options = { + strings: true, + } + input: { + console.log(function(a) { + return a + ("" + (a[0] = 0)); + }([])); + } + expect: { + console.log(function(a) { + return a + ("" + (a[0] = 0)); + }([])); + } + expect_stdout: "00" +} + +issue_5145: { + options = { + strings: true, + } + input: { + var a = []; + console.log("" + a + ((a[0] = 4) + "2")); + } + expect: { + var a = []; + console.log("" + a + (a[0] = 4) + "2"); } + expect_stdout: "42" } diff --git a/test/compress/conditionals.js b/test/compress/conditionals.js index 9af1630a5f2..3f09d9ee13c 100644 --- a/test/compress/conditionals.js +++ b/test/compress/conditionals.js @@ -1,7 +1,7 @@ ifs_1: { options = { - conditionals: true - }; + conditionals: true, + } input: { if (foo) bar(); if (!foo); else bar(); @@ -18,8 +18,8 @@ ifs_1: { ifs_2: { options = { - conditionals: true - }; + conditionals: true, + } input: { if (foo) { x(); @@ -41,26 +41,29 @@ ifs_2: { } expect: { foo ? x() : bar ? y() : baz && z(); - foo ? x() : bar ? y() : baz ? z() : t(); + (foo ? x : bar ? y : baz ? z : t)(); } } ifs_3_should_warn: { options = { - conditionals : true, - dead_code : true, - evaluate : true, - booleans : true - }; + booleans: true, + conditionals: true, + dead_code: true, + evaluate: true, + side_effects: true, + } input: { - if (x && !(x + "1") && y) { // 1 + var x, y; + // 1 + if (x && !(x + "1") && y) { var qq; foo(); } else { bar(); } - - if (x || !!(x + "1") || y) { // 2 + // 2 + if (x || !!(x + "1") || y) { foo(); } else { var jj; @@ -68,15 +71,32 @@ ifs_3_should_warn: { } } expect: { - var qq; bar(); // 1 - var jj; foo(); // 2 + var x, y; + // 1 + var qq; bar(); + // 2 + foo(); var jj; } + expect_warnings: [ + "WARN: + in boolean context always true [test/compress/conditionals.js:3,18]", + "WARN: Boolean && always false [test/compress/conditionals.js:3,12]", + "WARN: Condition left of && always false [test/compress/conditionals.js:3,12]", + "WARN: Condition always false [test/compress/conditionals.js:3,12]", + "WARN: Dropping side-effect-free statement [test/compress/conditionals.js:3,12]", + "WARN: Dropping unreachable code [test/compress/conditionals.js:3,34]", + "WARN: + in boolean context always true [test/compress/conditionals.js:10,19]", + "WARN: Boolean || always true [test/compress/conditionals.js:10,12]", + "WARN: Condition left of || always true [test/compress/conditionals.js:10,12]", + "WARN: Condition always true [test/compress/conditionals.js:10,12]", + "WARN: Dropping side-effect-free statement [test/compress/conditionals.js:10,12]", + "WARN: Dropping unreachable code [test/compress/conditionals.js:12,15]", + ] } ifs_4: { options = { - conditionals: true - }; + conditionals: true, + } input: { if (foo && bar) { x(foo)[10].bar.baz = something(); @@ -84,16 +104,18 @@ ifs_4: { x(foo)[10].bar.baz = something_else(); } expect: { - x(foo)[10].bar.baz = (foo && bar) ? something() : something_else(); + foo && bar + ? x(foo)[10].bar.baz = something() + : x(foo)[10].bar.baz = something_else(); } } ifs_5: { options = { - if_return: true, - conditionals: true, comparisons: true, - }; + conditionals: true, + if_return: true, + } input: { function f() { if (foo) return; @@ -127,58 +149,340 @@ ifs_5: { ifs_6: { options = { + comparisons: true, conditionals: true, - comparisons: true - }; + } input: { + var x, y; if (!foo && !bar && !baz && !boo) { x = 10; } else { x = 20; } + if (y) { + x[foo] = 10; + } else { + x[foo] = 20; + } + if (foo) { + x[bar] = 10; + } else { + x[bar] = 20; + } } expect: { + var x, y; x = foo || bar || baz || boo ? 20 : 10; + x[foo] = y ? 10 : 20; + foo ? x[bar] = 10 : x[bar] = 20; + } +} + +ifs_7: { + options = { + conditionals: true, + } + input: { + if (A); else; + if (A) while (B); else; + if (A); else while (C); + if (A) while (B); else while (C); + } + expect: { + A; + if (A) while (B); + if (!A) while (C); + if (A) while (B); else while (C); + } +} + +merge_tail_1: { + options = { + conditionals: true, + } + input: { + function f(a) { + var b = "foo"; + if (a) { + while (console.log("bar")); + console.log(b); + } else { + while (console.log("baz")); + console.log(b); + } + } + f(); + f(42); + } + expect: { + function f(a) { + var b = "foo"; + if (a) + while (console.log("bar")); + else + while (console.log("baz")); + console.log(b); + } + f(); + f(42); + } + expect_stdout: [ + "baz", + "foo", + "bar", + "foo", + ] +} + +merge_tail_2: { + options = { + conditionals: true, + } + input: { + function f(a) { + var b = "foo"; + if (a) { + while (console.log("bar")); + console.log(b); + } else { + c = "baz"; + while (console.log(c)); + while (console.log("bar")); + console.log(b); + var c; + } + } + f(); + f(42); + } + expect: { + function f(a) { + var b = "foo"; + if (!a) { + c = "baz"; + while (console.log(c)); + var c; + } + while (console.log("bar")); + console.log(b); + } + f(); + f(42); + } + expect_stdout: [ + "baz", + "bar", + "foo", + "bar", + "foo", + ] +} + +merge_tail_3: { + options = { + conditionals: true, + reduce_vars: true, + unused: true, + } + input: { + (function(a, b) { + if (b = a.shift()) + console.log(b); + else { + if (b = a.shift()) + while (console.log("foo")); + console.log(b); + } + })([ false, "bar" ]); + } + expect: { + (function(a, b) { + if (!(b = a.shift()) && (b = a.shift())) + while (console.log("foo")); + console.log(b); + })([ false, "bar" ]); + } + expect_stdout: [ + "foo", + "bar", + ] +} + +merge_tail_sequence_1: { + options = { + conditionals: true, + } + input: { + function f(a) { + var b = "foo"; + if (a) { + while (console.log("bar")); + console.log(b); + } else { + c = "baz"; + while (console.log(c)); + console.log("bar"), + console.log(b); + var c; + } + } + f(); + f(42); + } + expect: { + function f(a) { + var b = "foo"; + if (a) + while (console.log("bar")); + else { + c = "baz"; + while (console.log(c)); + console.log("bar"); + var c; + } + console.log(b); + } + f(); + f(42); + } + expect_stdout: [ + "baz", + "bar", + "foo", + "bar", + "foo", + ] +} + +merge_tail_sequence_2: { + options = { + conditionals: true, + } + input: { + function f(a) { + var b = "foo"; + if (a) { + console.log("bar"); + console.log(b); + } else { + c = "baz"; + while (console.log(c)); + console.log("bar"), + console.log(b); + var c; + } + } + f(); + f(42); + } + expect: { + function f(a) { + var b = "foo"; + if (!a) { + c = "baz"; + while (console.log(c)); + var c; + } + console.log("bar"); + console.log(b); + } + f(); + f(42); } + expect_stdout: [ + "baz", + "bar", + "foo", + "bar", + "foo", + ] +} + +merge_tail_sequence_3: { + options = { + conditionals: true, + reduce_vars: true, + unused: true, + } + input: { + (function(a, b) { + if (b = a.shift()) + console.log("foo"), + console.log(b); + else { + if (b = a.shift()) + while (console.log("bar")); + console.log(b); + } + })([ false, "baz" ]); + } + expect: { + (function(a, b) { + if (b = a.shift()) + console.log("foo"); + else if (b = a.shift()) + while (console.log("bar")); + console.log(b); + })([ false, "baz" ]); + } + expect_stdout: [ + "bar", + "baz", + ] } cond_1: { options = { - conditionals: true - }; + conditionals: true, + } input: { - if (some_condition()) { - do_something(x); - } else { - do_something(y); + function foo(do_something, some_condition) { + if (some_condition) { + do_something(x); + } else { + do_something(y); + } + if (some_condition) { + side_effects(x); + } else { + side_effects(y); + } } } expect: { - do_something(some_condition() ? x : y); + function foo(do_something, some_condition) { + do_something(some_condition ? x : y); + some_condition ? side_effects(x) : side_effects(y); + } } } cond_2: { options = { - conditionals: true - }; + conditionals: true, + } input: { - if (some_condition()) { - x = new FooBar(1); - } else { - x = new FooBar(2); + function foo(x, FooBar, some_condition) { + if (some_condition) { + x = new FooBar(1); + } else { + x = new FooBar(2); + } } } expect: { - x = new FooBar(some_condition() ? 1 : 2); + function foo(x, FooBar, some_condition) { + x = new FooBar(some_condition ? 1 : 2); + } } } cond_3: { options = { - conditionals: true - }; + conditionals: true, + } input: { + var FooBar; if (some_condition()) { new FooBar(1); } else { @@ -186,30 +490,39 @@ cond_3: { } } expect: { + var FooBar; some_condition() ? new FooBar(1) : FooBar(2); } } cond_4: { options = { - conditionals: true - }; + conditionals: true, + } input: { + var do_something; if (some_condition()) { do_something(); } else { do_something(); } + if (some_condition()) { + side_effects(); + } else { + side_effects(); + } } expect: { + var do_something; some_condition(), do_something(); + some_condition(), side_effects(); } } cond_5: { options = { - conditionals: true - }; + conditionals: true, + } input: { if (some_condition()) { if (some_other_condition()) { @@ -228,16 +541,56 @@ cond_5: { } } expect: { - some_condition() && some_other_condition() ? do_something() : alternate(); + (some_condition() && some_other_condition() ? do_something : alternate)(); some_condition() && some_other_condition() && do_something(); } } +cond_6: { + options = { + booleans: true, + conditionals: true, + } + input: { + x ? a : b; + x ? a : a; + + x ? y ? a : b : c; + x ? y ? a : a : b; + x ? y ? a : b : b; + x ? y ? a : b : a; + x ? y ? a : a : a; + + x ? a : y ? b : c; + x ? a : y ? a : b; + x ? a : y ? b : b; + x ? a : y ? b : a; + x ? a : y ? a : a; + } + expect: { + x ? a : b; + x, a; + + x ? y ? a : b : c; + x ? (y, a) : b; + x && y ? a : b; + !x || y ? a : b; + x && y, a; + + x ? a : y ? b : c; + x || y ? a : b; + x ? a : (y, b); + !x && y ? b : a; + !x && y, a; + } +} + cond_7: { options = { conditionals: true, - evaluate : true - }; + evaluate: true, + side_effects: true, + } input: { var x, y, z, a, b; // compress these @@ -288,7 +641,7 @@ cond_7: { x = 'foo'; x = 'foo'; x = (condition(), 20); - x = z ? 'fuji' : (condition(), 'fuji'); + x = (z || condition(), 'fuji'); x = (condition(), 'foobar'); x = y ? a : b; x = y ? 'foo' : 'fo'; @@ -298,9 +651,10 @@ cond_7: { cond_7_1: { options = { conditionals: true, - evaluate : true - }; + evaluate: true, + } input: { + var x; // access to global should be assumed to have side effects if (y) { x = 1+1; @@ -309,60 +663,2428 @@ cond_7_1: { } } expect: { + var x; x = (y, 2); } } cond_8: { options = { + booleans: false, conditionals: true, - evaluate : true - }; + evaluate: true, + } input: { + var a; // compress these a = condition ? true : false; - a = !condition ? true : false; - a = condition() ? true : false; + a = condition ? !0 : !1; + a = !condition ? !null : !2; + a = condition() ? !0 : !-3.5; + if (condition) { a = true; } else { a = false; } - a = condition ? false : true; + if (condition) { + a = !0; + } else { + a = !1; + } + a = condition ? false : true; a = !condition ? false : true; - a = condition() ? false : true; + a = condition ? !3 : !0; + a = !condition ? !2 : !0; + a = condition() ? !1 : !0; + if (condition) { a = false; } else { a = true; } + if (condition) { + a = !1; + } else { + a = !0; + } + // don't compress these a = condition ? 1 : false; - a = !condition ? true : 0; + a = condition ? 1 : 0; + } + expect: { + var a; + a = !!condition; + a = !condition; + a = !!condition(); + + a = !!condition; + a = !condition; + a = !!condition(); + + a = !!condition; + a = !!condition; + + a = !condition; + a = !!condition; + a = !condition(); + + a = !condition; + a = !!condition; + a = !condition(); + + a = !condition; + a = !condition; + a = !!condition && 1; + a = !condition || 0; a = condition ? 1 : 0; + } +} + +cond_8b: { + options = { + booleans: true, + conditionals: true, + evaluate: true, + } + input: { + var a; + // compress these + a = condition ? true : false; + a = !condition ? true : false; + a = condition() ? true : false; + + a = condition ? !0 : !1; + a = !condition ? !null : !2; + a = condition() ? !0 : !-3.5; + + if (condition) { + a = true; + } else { + a = false; + } + + if (condition) { + a = !0; + } else { + a = !1; + } + a = condition ? false : true; + a = !condition ? false : true; + a = condition() ? false : true; + + a = condition ? !3 : !0; + a = !condition ? !2 : !0; + a = condition() ? !1 : !0; + + if (condition) { + a = false; + } else { + a = true; + } + + if (condition) { + a = !1; + } else { + a = !0; + } + + a = condition ? 1 : false; + a = !condition ? true : 0; + a = condition ? 1 : 0; } expect: { + var a; + a = !!condition; + a = !condition; + a = !!condition(); + a = !!condition; a = !condition; a = !!condition(); + + a = !!condition; a = !!condition; + a = !condition; a = !!condition; a = !condition(); + a = !condition; - a = condition ? 1 : false; - a = condition ? 0 : true; + a = !!condition; + a = !condition(); + + a = !condition; + a = !condition; + + a = !!condition && 1; + a = !condition || 0; a = condition ? 1 : 0; } -} \ No newline at end of file +} + +cond_8c: { + options = { + booleans: false, + conditionals: true, + evaluate: false, + } + input: { + var a; + // compress these + a = condition ? true : false; + a = !condition ? true : false; + a = condition() ? true : false; + + a = condition ? !0 : !1; + a = !condition ? !null : !2; + a = condition() ? !0 : !-3.5; + + if (condition) { + a = true; + } else { + a = false; + } + + if (condition) { + a = !0; + } else { + a = !1; + } + + a = condition ? false : true; + a = !condition ? false : true; + a = condition() ? false : true; + + a = condition ? !3 : !0; + a = !condition ? !2 : !0; + a = condition() ? !1 : !0; + + if (condition) { + a = false; + } else { + a = true; + } + + if (condition) { + a = !1; + } else { + a = !0; + } + + a = condition ? 1 : false; + a = !condition ? true : 0; + a = condition ? 1 : 0; + } + expect: { + var a; + a = !!condition; + a = !condition; + a = !!condition(); + + a = !!condition; + a = !condition; + a = !!condition() || !-3.5; + + a = !!condition; + a = !!condition; + + a = !condition; + a = !!condition; + a = !condition(); + + a = !condition; + a = !!condition; + a = !condition(); + + a = !condition; + a = !condition; + + a = !!condition && 1; + a = !condition || 0; + a = condition ? 1 : 0; + } +} + +cond_9: { + options = { + conditionals: true, + } + input: { + function f(x, y) { + g() ? x(1) : x(2); + x ? (y || x)() : (y || x)(); + x ? y(a, b) : y(d, b, c); + x ? y(a, b, c) : y(a, b, c); + x ? y(a, b, c) : y(a, b, f); + x ? y(a, b, c) : y(a, e, c); + x ? y(a, b, c) : y(a, e, f); + x ? y(a, b, c) : y(d, b, c); + x ? y(a, b, c) : y(d, b, f); + x ? y(a, b, c) : y(d, e, c); + x ? y(a, b, c) : y(d, e, f); + } + } + expect: { + function f(x, y) { + g() ? x(1) : x(2); + x, (y || x)(); + x ? y(a, b) : y(d, b, c); + x, y(a, b, c); + y(a, b, x ? c : f); + y(a, x ? b : e, c); + x ? y(a, b, c) : y(a, e, f); + y(x ? a : d, b, c); + x ? y(a, b, c) : y(d, b, f); + x ? y(a, b, c) : y(d, e, c); + x ? y(a, b, c) : y(d, e, f); + } + } +} + +cond_10: { + options = { + conditionals: true, + if_return: true, + } + input: { + function f(a) { + if (1 == a) return "foo"; + if (2 == a) return "foo"; + if (3 == a) return "foo"; + if (4 == a) return 42; + if (5 == a) return "foo"; + if (6 == a) return "foo"; + return "bar"; + } + console.log(f(1), f(2), f(3), f(4), f(5), f(6), f(7)); + } + expect: { + function f(a) { + return 1 == a || 2 == a || 3 == a ? "foo" : 4 == a ? 42 : 5 == a || 6 == a ? "foo" : "bar"; + } + console.log(f(1), f(2), f(3), f(4), f(5), f(6), f(7)); + } + expect_stdout: "foo foo foo 42 foo foo bar" +} + +cond_11: { + options = { + conditionals: true, + } + input: { + var o = { + p: "foo", + q: function() { + return this.p; + } + }; + function f() { + return "bar"; + } + function g(a) { + return a ? f() : o.q(); + } + console.log(g(0), g(1)); + } + expect: { + var o = { + p: "foo", + q: function() { + return this.p; + } + }; + function f() { + return "bar"; + } + function g(a) { + return a ? f() : o.q(); + } + console.log(g(0), g(1)); + } + expect_stdout: "foo bar" +} + +cond_12: { + options = { + conditionals: true, + } + input: { + x ? y && a : a; + x ? y || a : a; + x ? a : y && a; + x ? a : y || a; + } + expect: { + (!x || y) && a; + x && y || a; + (x || y) && a; + !x && y || a; + } +} + +cond_13: { + options = { + conditionals: true, + } + input: { + x ? y(a) : z(a); + x ? y.f(a) : z.f(a); + x ? y.f(a) : z.g(a); + x ? y.f()(a) : z.g()(a); + x ? y.f.u(a) : z.g.u(a); + x ? y.f().u(a) : z.g().u(a); + } + expect: { + (x ? y : z)(a); + (x ? y : z).f(a); + x ? y.f(a) : z.g(a); + (x ? y.f() : z.g())(a); + (x ? y.f : z.g).u(a); + (x ? y.f() : z.g()).u(a); + } +} + +cond_14: { + options = { + booleans: true, + conditionals: true, + side_effects: true, + } + input: { + function f(a) { + if (a) + if (a) + console.log("PASS"); + else + console.log("FAIL"); + } + f(null); + f(42); + } + expect: { + function f(a) { + a && console.log("PASS"); + } + f(null); + f(42); + } + expect_stdout: "PASS" +} + +ternary_boolean_consequent: { + options = { + booleans: true, + collapse_vars: true, + comparisons: true, + conditionals: true, + dead_code: true, + evaluate: true, + hoist_funs: true, + if_return: true, + join_vars: true, + keep_fargs: true, + loops: true, + properties: true, + sequences: true, + side_effects: true, + unused: true, + } + input: { + function f1() { return a == b ? true : x; } + function f2() { return a == b ? false : x; } + function f3() { return a < b ? !0 : x; } + function f4() { return a < b ? !1 : x; } + function f5() { return c ? !0 : x; } + function f6() { return c ? false : x; } + function f7() { return !c ? true : x; } + function f8() { return !c ? !1 : x; } + } + expect: { + function f1() { return a == b || x; } + function f2() { return a != b && x; } + function f3() { return a < b || x; } + function f4() { return !(a < b) && x; } + function f5() { return !!c || x; } + function f6() { return !c && x; } + function f7() { return !c || x; } + function f8() { return !!c && x; } + } +} + +ternary_boolean_alternative: { + options = { + booleans: true, + collapse_vars: true, + comparisons: true, + conditionals: true, + dead_code: true, + evaluate: true, + hoist_funs: true, + if_return: true, + join_vars: true, + keep_fargs: true, + loops: true, + properties: true, + sequences: true, + side_effects: true, + unused: true, + } + input: { + function f1() { return a == b ? x : true; } + function f2() { return a == b ? x : false; } + function f3() { return a < b ? x : !0; } + function f4() { return a < b ? x : !1; } + function f5() { return c ? x : true; } + function f6() { return c ? x : !1; } + function f7() { return !c ? x : !0; } + function f8() { return !c ? x : false; } + } + expect: { + function f1() { return a != b || x; } + function f2() { return a == b && x; } + function f3() { return !(a < b) || x; } + function f4() { return a < b && x; } + function f5() { return !c || x; } + function f6() { return !!c && x; } + function f7() { return !!c || x; } + function f8() { return !c && x; } + } +} + +trivial_boolean_ternary_expressions : { + options = { + booleans: true, + conditionals: true, + evaluate: true, + side_effects: true, + } + input: { + f('foo' in m ? true : false); + f('foo' in m ? false : true); + + f(g ? true : false); + f(foo() ? true : false); + f("bar" ? true : false); + f(5 ? true : false); + f(5.7 ? true : false); + f(x - y ? true : false); + + f(x == y ? true : false); + f(x === y ? !0 : !1); + f(x < y ? !0 : false); + f(x <= y ? true : false); + f(x > y ? true : !1); + f(x >= y ? !0 : !1); + + f(g ? false : true); + f(foo() ? false : true); + f("bar" ? false : true); + f(5 ? false : true); + f(5.7 ? false : true); + f(x - y ? false : true); + + f(x == y ? !1 : !0); + f(x === y ? false : true); + + f(x < y ? false : true); + f(x <= y ? false : !0); + f(x > y ? !1 : true); + f(x >= y ? !1 : !0); + } + expect: { + f('foo' in m); + f(!('foo' in m)); + + f(!!g); + f(!!foo()); + f(!0); + f(!0); + f(!0); + f(!!(x - y)); + + f(x == y); + f(x === y); + f(x < y); + f(x <= y); + f(x > y); + f(x >= y); + + f(!g); + f(!foo()); + f(!1); + f(!1); + f(!1); + f(!(x - y)); + + f(x != y); + f(x !== y); + + f(!(x < y)); + f(!(x <= y)); + f(!(x > y)); + f(!(x >= y)); + } +} + +extendscript_1: { + beautify = { + extendscript: true, + } + input: { + var alert = console.log; + function f(a, b) { + return a ? b ? "foo" : "bar" : "baz"; + } + alert(f()); + alert(f(42)); + alert(f(null, true)); + alert(f([], {})); + } + expect_exact: 'var alert=console.log;function f(a,b){return a?(b?"foo":"bar"):"baz"}alert(f());alert(f(42));alert(f(null,true));alert(f([],{}));' + expect_stdout: [ + "baz", + "bar", + "baz", + "foo", + ] +} + +extendscript_2: { + beautify = { + extendscript: true, + } + input: { + var alert = console.log; + function f(a, b) { + return a ? "foo" : b ? "bar" : "baz"; + } + alert(f()); + alert(f(42)); + alert(f(null, true)); + alert(f([], {})); + } + expect_exact: 'var alert=console.log;function f(a,b){return a?"foo":(b?"bar":"baz")}alert(f());alert(f(42));alert(f(null,true));alert(f([],{}));' + expect_stdout: [ + "baz", + "foo", + "bar", + "foo", + ] +} + +issue_1154: { + options = { + booleans: true, + conditionals: true, + evaluate: true, + side_effects: true, + } + input: { + function f1(x) { return x ? -1 : -1; } + function f2(x) { return x ? +2 : +2; } + function f3(x) { return x ? ~3 : ~3; } + function f4(x) { return x ? !4 : !4; } + function f5(x) { return x ? void 5 : void 5; } + function f6(x) { return x ? typeof 6 : typeof 6; } + + function g1() { return g() ? -1 : -1; } + function g2() { return g() ? +2 : +2; } + function g3() { return g() ? ~3 : ~3; } + function g4() { return g() ? !4 : !4; } + function g5() { return g() ? void 5 : void 5; } + function g6() { return g() ? typeof 6 : typeof 6; } + } + expect: { + function f1(x) { return -1; } + function f2(x) { return 2; } + function f3(x) { return -4; } + function f4(x) { return !1; } + function f5(x) { return; } + function f6(x) { return "number"; } + + function g1() { return g(), -1; } + function g2() { return g(), 2; } + function g3() { return g(), -4; } + function g4() { return g(), !1; } + function g5() { return void g(); } + function g6() { return g(), "number"; } + } +} + +no_evaluate: { + options = { + conditionals: true, + evaluate: false, + side_effects: true, + } + input: { + function f(b) { + a = b ? !0 : !0; + a = b ? ~1 : ~1; + a = b ? -2 : -2; + a = b ? +3 : +3; + } + } + expect: { + function f(b) { + a = !0; + a = ~1; + a = -2; + a = +3; + } + } +} + +equality_conditionals_false: { + options = { + conditionals: false, + sequences: true, + } + input: { + function f(a, b, c) { + console.log( + a == (b ? a : a), + a == (b ? a : c), + a != (b ? a : a), + a != (b ? a : c), + a === (b ? a : a), + a === (b ? a : c), + a !== (b ? a : a), + a !== (b ? a : c) + ); + } + f(0, 0, 0); + f(0, true, 0); + f(1, 2, 3); + f(1, null, 3); + f(NaN); + f(NaN, "foo"); + } + expect: { + function f(a, b, c) { + console.log( + a == (b ? a : a), + a == (b ? a : c), + a != (b ? a : a), + a != (b ? a : c), + a === (b ? a : a), + a === (b ? a : c), + a !== (b ? a : a), + a !== (b ? a : c) + ); + } + f(0, 0, 0), + f(0, true, 0), + f(1, 2, 3), + f(1, null, 3), + f(NaN), + f(NaN, "foo"); + } + expect_stdout: true +} + +equality_conditionals_true: { + options = { + conditionals: true, + sequences: true, + } + input: { + function f(a, b, c) { + console.log( + a == (b ? a : a), + a == (b ? a : c), + a != (b ? a : a), + a != (b ? a : c), + a === (b ? a : a), + a === (b ? a : c), + a !== (b ? a : a), + a !== (b ? a : c) + ); + } + f(0, 0, 0); + f(0, true, 0); + f(1, 2, 3); + f(1, null, 3); + f(NaN); + f(NaN, "foo"); + } + expect: { + function f(a, b, c) { + console.log( + (b, a == a), + a == (b ? a : c), + (b, a != a), + a != (b ? a : c), + (b, a === a), + a === (b ? a : c), + (b, a !== a), + a !== (b ? a : c) + ); + } + f(0, 0, 0), + f(0, true, 0), + f(1, 2, 3), + f(1, null, 3), + f(NaN), + f(NaN, "foo"); + } + expect_stdout: true +} + +issue_1645_1: { + options = { + conditionals: true, + } + input: { + var a = 100, b = 10; + (b = a) ? a++ + (b += a) ? b += a : b += a : b ^= a; + console.log(a, b); + } + expect: { + var a = 100, b = 10; + (b = a) ? (a++ + (b += a), b += a) : b ^= a; + console.log(a,b); + } + expect_stdout: true +} + +issue_1645_2: { + options = { + conditionals: true, + } + input: { + var a = 0; + function f() { + return a++; + } + f() ? a += 2 : a += 4; + console.log(a); + } + expect: { + var a = 0; + function f(){ + return a++; + } + f() ? a += 2 : a += 4; + console.log(a); + } + expect_stdout: true +} + +condition_matches_consequent: { + options = { + conditionals: true, + } + input: { + function foo(x, y) { + return x ? x : y; + } + function bar() { + return g ? g : h; + } + var g = 4; + var h = 5; + console.log(foo(3, null), foo(0, 7), foo(true, false), bar()); + } + expect: { + function foo(x, y) { + return x || y; + } + function bar() { + return g || h; + } + var g = 4; + var h = 5; + console.log(foo(3, null), foo(0, 7), foo(true, false), bar()); + } + expect_stdout: "3 7 true 4" +} + +condition_matches_alternative: { + options = { + conditionals: true, + } + input: { + function foo(x, y) { + return x.p ? y[0] : x.p; + } + function bar() { + return g ? h : g; + } + var g = 4; + var h = 5; + console.log(foo({ p: 3 }, [ null ]), foo({ p: 0 }, [ 7 ]), foo({ p: true } , [ false ]), bar()); + } + expect: { + function foo(x, y) { + return x.p && y[0]; + } + function bar() { + return g && h; + } + var g = 4; + var h = 5; + console.log(foo({ p: 3 }, [ null ]), foo({ p: 0 }, [ 7 ]), foo({ p: true } , [ false ]), bar()); + } + expect_stdout: "null 0 false 5" +} + +condition_sequence_1: { + options = { + conditionals: true, + sequences: true, + } + input: { + function f(x, y) { + return (console.log(x), x) ? x : y; + } + console.log(f("foo", "bar")); + console.log(f(null, "baz")); + console.log(f(42)); + console.log(f()); + } + expect: { + function f(x, y) { + return console.log(x), x || y; + } + console.log(f("foo", "bar")), + console.log(f(null, "baz")), + console.log(f(42)), + console.log(f()); + } + expect_stdout: [ + "foo", + "foo", + "null", + "baz", + "42", + "42", + "undefined", + "undefined", + ] +} + +condition_sequence_2: { + options = { + conditionals: true, + sequences: true, + } + input: { + function f(x, y) { + return (console.log(y), y) ? x : y; + } + console.log(f("foo", "bar")); + console.log(f(null, "baz")); + console.log(f(42)); + console.log(f()); + } + expect: { + function f(x, y) { + return console.log(y), y && x; + } + console.log(f("foo", "bar")), + console.log(f(null, "baz")), + console.log(f(42)), + console.log(f()); + } + expect_stdout: [ + "bar", + "foo", + "baz", + "null", + "undefined", + "undefined", + "undefined", + "undefined", + ] +} + +combine_tail_sequence: { + options = { + conditionals: true, + } + input: { + var n = { + f: function() { + console.log("foo"); + return this.p; + }, + p: "FAIL 1", + }; + var o = { + f: function() { + console.log("foz"); + return this.p; + }, + p: "FAIL 2", + }; + var p = "PASS"; + function g(a) { + return a + ? (console.log("baa"), (console.log("bar"), (console.log("baz"), n).f)()) + : (console.log("moo"), (console.log("mor"), (console.log("moz"), o).f)()); + } + console.log(g()); + console.log(g(42)); + } + expect: { + var n = { + f: function() { + console.log("foo"); + return this.p; + }, + p: "FAIL 1", + }; + var o = { + f: function() { + console.log("foz"); + return this.p; + }, + p: "FAIL 2", + }; + var p = "PASS"; + function g(a) { + return (0, (a + ? (console.log("baa"), console.log("bar"), console.log("baz"), n) + : (console.log("moo"), console.log("mor"), console.log("moz"), o)).f)(); + } + console.log(g()); + console.log(g(42)); + } + expect_stdout: [ + "moo", + "mor", + "moz", + "foz", + "PASS", + "baa", + "bar", + "baz", + "foo", + "PASS", + ] +} + +consequent_sequence_1: { + options = { + conditionals: true, + } + input: { + function f(x, y, a) { + return x ? (console.log("seq"), y && a) : a; + } + console.log(f(false, false, 1)); + console.log(f(false, true, 2)); + console.log(f(true, false, 3)); + console.log(f(true, true, 4)); + } + expect: { + function f(x, y, a) { + return (!x || (console.log("seq"), y)) && a; + } + console.log(f(false, false, 1)); + console.log(f(false, true, 2)); + console.log(f(true, false, 3)); + console.log(f(true, true, 4)); + } + expect_stdout: [ + "1", + "2", + "seq", + "false", + "seq", + "4", + ] +} + +consequent_sequence_2: { + options = { + conditionals: true, + } + input: { + function f(x, y, a) { + return x ? (console.log("seq"), y || a) : a; + } + console.log(f(false, false, 1)); + console.log(f(false, true, 2)); + console.log(f(true, false, 3)); + console.log(f(true, true, 4)); + } + expect: { + function f(x, y, a) { + return x && (console.log("seq"), y) || a; + } + console.log(f(false, false, 1)); + console.log(f(false, true, 2)); + console.log(f(true, false, 3)); + console.log(f(true, true, 4)); + } + expect_stdout: [ + "1", + "2", + "seq", + "3", + "seq", + "true", + ] +} + +consequent_sequence_3: { + options = { + conditionals: true, + } + input: { + function f(x, y, a, b) { + return x ? (console.log("seq"), y ? a : b) : b; + } + console.log(f(false, false, 1, -1)); + console.log(f(false, true, 2, -2)); + console.log(f(true, false, 3, -3)); + console.log(f(true, true, 4, -4)); + } + expect: { + function f(x, y, a, b) { + return x && (console.log("seq"), y) ? a : b; + } + console.log(f(false, false, 1, -1)); + console.log(f(false, true, 2, -2)); + console.log(f(true, false, 3, -3)); + console.log(f(true, true, 4, -4)); + } + expect_stdout: [ + "-1", + "-2", + "seq", + "-3", + "seq", + "4", + ] +} + +consequent_sequence_4: { + options = { + conditionals: true, + } + input: { + function f(x, y, a, b) { + return x ? (console.log("seq"), y ? a : b) : a; + } + console.log(f(false, false, 1, -1)); + console.log(f(false, true, 2, -2)); + console.log(f(true, false, 3, -3)); + console.log(f(true, true, 4, -4)); + } + expect: { + function f(x, y, a, b) { + return !x || (console.log("seq"), y) ? a : b; + } + console.log(f(false, false, 1, -1)); + console.log(f(false, true, 2, -2)); + console.log(f(true, false, 3, -3)); + console.log(f(true, true, 4, -4)); + } + expect_stdout: [ + "1", + "2", + "seq", + "-3", + "seq", + "4", + ] +} + +alternative_sequence_1: { + options = { + conditionals: true, + } + input: { + function f(x, y, a) { + return x ? a : (console.log("seq"), y && a); + } + console.log(f(false, false, 1)); + console.log(f(false, true, 2)); + console.log(f(true, false, 3)); + console.log(f(true, true, 4)); + } + expect: { + function f(x, y, a) { + return (x || (console.log("seq"), y)) && a; + } + console.log(f(false, false, 1)); + console.log(f(false, true, 2)); + console.log(f(true, false, 3)); + console.log(f(true, true, 4)); + } + expect_stdout: [ + "seq", + "false", + "seq", + "2", + "3", + "4", + ] +} + +alternative_sequence_2: { + options = { + conditionals: true, + } + input: { + function f(x, y, a) { + return x ? a : (console.log("seq"), y || a); + } + console.log(f(false, false, 1)); + console.log(f(false, true, 2)); + console.log(f(true, false, 3)); + console.log(f(true, true, 4)); + } + expect: { + function f(x, y, a) { + return !x && (console.log("seq"), y) || a; + } + console.log(f(false, false, 1)); + console.log(f(false, true, 2)); + console.log(f(true, false, 3)); + console.log(f(true, true, 4)); + } + expect_stdout: [ + "seq", + "1", + "seq", + "true", + "3", + "4", + ] +} + +alternative_sequence_3: { + options = { + conditionals: true, + } + input: { + function f(x, y, a, b) { + return x ? a : (console.log("seq"), y ? a : b); + } + console.log(f(false, false, 1, -1)); + console.log(f(false, true, 2, -2)); + console.log(f(true, false, 3, -3)); + console.log(f(true, true, 4, -4)); + } + expect: { + function f(x, y, a, b) { + return x || (console.log("seq"), y) ? a : b; + } + console.log(f(false, false, 1, -1)); + console.log(f(false, true, 2, -2)); + console.log(f(true, false, 3, -3)); + console.log(f(true, true, 4, -4)); + } + expect_stdout: [ + "seq", + "-1", + "seq", + "2", + "3", + "4", + ] +} + +alternative_sequence_4: { + options = { + conditionals: true, + } + input: { + function f(x, y, a, b) { + return x ? b : (console.log("seq"), y ? a : b); + } + console.log(f(false, false, 1, -1)); + console.log(f(false, true, 2, -2)); + console.log(f(true, false, 3, -3)); + console.log(f(true, true, 4, -4)); + } + expect: { + function f(x, y, a, b) { + return !x && (console.log("seq"), y) ? a : b; + } + console.log(f(false, false, 1, -1)); + console.log(f(false, true, 2, -2)); + console.log(f(true, false, 3, -3)); + console.log(f(true, true, 4, -4)); + } + expect_stdout: [ + "seq", + "-1", + "seq", + "2", + "-3", + "-4", + ] +} + +delete_conditional_1: { + options = { + booleans: true, + conditionals: true, + evaluate: true, + side_effects: true, + } + input: { + console.log(delete (1 ? undefined : x)); + console.log(delete (1 ? void 0 : x)); + console.log(delete (1 ? Infinity : x)); + console.log(delete (1 ? 1 / 0 : x)); + console.log(delete (1 ? NaN : x)); + console.log(delete (1 ? 0 / 0 : x)); + } + expect: { + console.log(!0); + console.log(!0); + console.log(!0); + console.log(!0); + console.log(!0); + console.log(!0); + } + expect_stdout: true +} + +delete_conditional_2: { + options = { + booleans: true, + conditionals: true, + evaluate: true, + keep_infinity: true, + side_effects: true, + } + input: { + console.log(delete (0 ? x : undefined)); + console.log(delete (0 ? x : void 0)); + console.log(delete (0 ? x : Infinity)); + console.log(delete (0 ? x : 1 / 0)); + console.log(delete (0 ? x : NaN)); + console.log(delete (0 ? x : 0 / 0)); + } + expect: { + console.log(!0); + console.log(!0); + console.log(!0); + console.log(!0); + console.log(!0); + console.log(!0); + } + expect_stdout: true +} + +issue_2535_1: { + options = { + booleans: true, + conditionals: true, + evaluate: true, + passes: 2, + side_effects: true, + } + input: { + if (true || x()) y(); + if (true && x()) y(); + if (x() || true) y(); + if (x() && true) y(); + if (false || x()) y(); + if (false && x()) y(); + if (x() || false) y(); + if (x() && false) y(); + } + expect: { + y(); + x() && y(); + x(), y(); + x() && y(); + x() && y(); + x() && y(); + x(); + } +} + +issue_2535_2: { + options = { + booleans: true, + conditionals: true, + evaluate: true, + side_effects: true, + } + input: { + function x() {} + function y() { + return "foo"; + } + console.log((x() || true) || y()); + console.log((y() || true) || x()); + console.log((x() || true) && y()); + console.log((y() || true) && x()); + console.log((x() && true) || y()); + console.log((y() && true) || x()); + console.log((x() && true) && y()); + console.log((y() && true) && x()); + console.log((x() || false) || y()); + console.log((y() || false) || x()); + console.log((x() || false) && y()); + console.log((y() || false) && x()); + console.log((x() && false) || y()); + console.log((y() && false) || x()); + console.log((x() && false) && y()); + console.log((y() && false) && x()); + } + expect: { + function x() {} + function y() { + return "foo"; + } + console.log(x() || !0); + console.log(y() || !0); + console.log((x(), y())); + console.log((y(), x())); + console.log(!!x() || y()); + console.log(!!y() || x()); + console.log(x() && y()); + console.log(y() && x()); + console.log(x() || y()); + console.log(y() || x()); + console.log(!!x() && y()); + console.log(!!y() && x()); + console.log((x(), y())); + console.log((y(), x())); + console.log(x() && !1); + console.log(y() && !1); + } + expect_stdout: [ + "true", + "foo", + "foo", + "undefined", + "foo", + "true", + "undefined", + "undefined", + "foo", + "foo", + "false", + "undefined", + "foo", + "undefined", + "undefined", + "false", + ] +} + +issue_2560: { + options = { + conditionals: true, + inline: true, + reduce_funcs: true, + reduce_vars: true, + toplevel: true, + unused: true, + } + input: { + function log(x) { + console.log(x); + } + function foo() { + return log; + } + function bar() { + if (x !== (x = foo())) { + x(1); + } else { + x(2); + } + } + var x = function() { + console.log("init"); + }; + bar(); + bar(); + } + expect: { + function log(x) { + console.log(x); + } + function bar() { + x !== (x = log) ? x(1) : x(2); + } + var x = function() { + console.log("init"); + }; + bar(); + bar(); + } + expect_stdout: [ + "1", + "2", + ] +} + +hoist_decl: { + options = { + conditionals: true, + join_vars: true, + sequences: true, + } + input: { + if (x()) { + var a; + y(); + } else { + z(); + var b; + } + } + expect: { + var a, b; + (x() ? y : z)(); + } +} + +to_and_or: { + options = { + conditionals: true, + } + input: { + var values = [ + 0, + null, + true, + "foo", + false, + -1 / 0, + void 0, + ]; + values.forEach(function(x) { + values.forEach(function(y) { + values.forEach(function(z) { + console.log(x ? y || z : z); + }); + }); + }); + } + expect: { + var values = [ + 0, + null, + true, + "foo", + false, + -1 / 0, + void 0, + ]; + values.forEach(function(x) { + values.forEach(function(y) { + values.forEach(function(z) { + console.log(x && y || z); + }); + }); + }); + } + expect_stdout: true +} + +cond_seq_assign_1: { + options = { + conditionals: true, + sequences: true, + } + input: { + function f(a) { + var t; + if (a) { + t = "foo"; + t = "bar"; + } else { + console.log(t); + t = 42; + } + console.log(t); + } + f(f); + f(); + } + expect: { + function f(a) { + var t; + t = a ? (t = "foo", "bar") : (console.log(t), 42), + console.log(t); + } + f(f), + f(); + } + expect_stdout: [ + "bar", + "undefined", + "42", + ] +} + +cond_seq_assign_2: { + options = { + conditionals: true, + sequences: true, + } + input: { + function f(a) { + var t; + if (a) { + t = "foo"; + a = "bar"; + } else { + console.log(t); + t = 42; + } + console.log(t); + } + f(f); + f(); + } + expect: { + function f(a) { + var t; + a ? (t = "foo", a = "bar") : (console.log(t), t = 42), + console.log(t); + } + f(f), + f(); + } + expect_stdout: [ + "foo", + "undefined", + "42", + ] +} + +cond_seq_assign_3: { + options = { + assignments: true, + conditionals: true, + } + input: { + var c = 0; + if (this) + c = 1 + c, c = c + 1; + else + c = 1 + c, c = c + 1; + console.log(c); + } + expect: { + var c = 0; + this, c = 1 + c, c += 1; + console.log(c); + } + expect_stdout: "2" +} + +issue_3271: { + options = { + conditionals: true, + } + input: { + function f(a) { + var i = 0, b = []; + if (a) { + b[i++] = 4, + b[i++] = 1; + } else { + b[i++] = 3, + b[i++] = 2, + b[i++] = 1; + } + return b; + } + console.log(f(0).pop(), f(1).pop()); + } + expect: { + function f(a) { + var i = 0, b = []; + a ? b[i++] = 4 : (b[i++] = 3, b[i++] = 2), + b[i++] = 1; + return b; + } + console.log(f(0).pop(), f(1).pop()); + } + expect_stdout: "1 1" +} + +iife_condition: { + options = { + conditionals: true, + side_effects: true, + } + input: { + if (function() { + return console; + }()) + console.log("PASS"); + } + expect: { + !function() { + return console; + }() || console.log("PASS"); + } + expect_stdout: "PASS" +} + +angularjs_chain: { + options = { + conditionals: true, + passes: 2, + side_effects: true, + } + input: { + function nonComputedMember(left, right, context, create) { + var lhs = left(); + if (create && create !== 1) { + if (lhs && lhs[right] == null) { + lhs[right] = {}; + } + } + var value = lhs != null ? lhs[right] : undefined; + if (context) { + return { context: lhs, name: right, value: value }; + } else { + return value; + } + } + } + expect: { + function nonComputedMember(left, right, context, create) { + var lhs = left(); + create && 1 !== create && lhs && null == lhs[right] && (lhs[right] = {}); + var value = null != lhs ? lhs[right] : void 0; + return context ? { + context: lhs, + name: right, + value: value + } : value; + } + } +} + +issue_3576: { + options = { + conditionals: true, + evaluate: true, + pure_getters: "strict", + reduce_vars: true, + } + input: { + var c = "FAIL"; + (function(a) { + (a = -1) ? (a && (a.a = 0)) : (a && (a.a = 0)); + a && a[c = "PASS"]++; + })(); + console.log(c); + } + expect: { + var c = "FAIL"; + (function(a) { + a = -1, a, a.a = 0; + a, a[c = "PASS"]++; + })(); + console.log(c); + } + expect_stdout: "PASS" +} + +issue_3668_1: { + options = { + conditionals: true, + if_return: true, + } + input: { + function f() { + try { + var undefined = typeof f; + if (!f) return undefined; + return; + } catch (e) { + return "FAIL"; + } + } + console.log(f()); + } + expect: { + function f() { + try { + var undefined = typeof f; + if (!f) return undefined; + } catch (e) { + return "FAIL"; + } + } + console.log(f()); + } + expect_stdout: "undefined" +} + +issue_3668_2: { + options = { + conditionals: true, + if_return: true, + } + input: { + function f() { + try { + var undefined = typeof f; + if (!f) return undefined; + return; + } catch (e) { + return "FAIL"; + } + FAIL; + } + console.log(f()); + } + expect: { + function f() { + try { + var undefined = typeof f; + return f ? void 0 : undefined; + } catch (e) { + return "FAIL"; + } + FAIL; + } + console.log(f()); + } + expect_stdout: "undefined" +} + +conditional_assignments_1: { + options = { + conditionals: true, + sequences: true, + } + input: { + function f(a, b, c, d) { + a = b; + if (c) a = d; + return a; + } + function g(a, b, c, d) { + a = b; + if (c); else a = d; + return a; + } + console.log(f(0, "FAIL", 1, "PASS"), g(0, "PASS", 1, "FAIL")); + } + expect: { + function f(a, b, c, d) { + return a = c ? d : b, a; + } + function g(a, b, c, d) { + return a = c ? b : d, a; + } + console.log(f(0, "FAIL", 1, "PASS"), g(0, "PASS", 1, "FAIL")); + } + expect_stdout: "PASS PASS" +} + +conditional_assignments_2: { + options = { + conditionals: true, + sequences: true, + } + input: { + function f1(b, c, d) { + a = b; + if (c) a = d; + return a; + } + function f2(a, c, d) { + a = b; + if (c) a = d; + return a; + } + function f3(a, b, d) { + a = b; + if (c) a = d; + return a; + } + function f4(a, b, c) { + a = b; + if (c) a = d; + return a; + } + } + expect: { + function f1(b, c, d) { + return a = c ? d : b, a; + } + function f2(a, c, d) { + return a = b, c && (a = d), a; + } + function f3(a, b, d) { + return a = b, c && (a = d), a; + } + function f4(a, b, c) { + return a = b, c && (a = d), a; + } + } +} + +conditional_assignments_3: { + options = { + conditionals: true, + sequences: true, + } + input: { + console.log(function(a, b) { + a = "PASS"; + if (b) a = a; + return a; + }(0, 1)); + } + expect: { + console.log(function(a, b) { + return a = "PASS", b && (a = a), a; + }(0, 1)); + } + expect_stdout: "PASS" +} + +issue_3808_1: { + options = { + conditionals: true, + side_effects: true, + } + input: { + var a; + a = "PASS", [] + "" && (a = "FAIL"); + console.log(a); + } + expect: { + var a; + a = [] + "" ? "FAIL" : "PASS"; + console.log(a); + } + expect_stdout: "PASS" +} + +issue_3808_2: { + options = { + conditionals: true, + side_effects: true, + } + input: { + var a; + console.log((a = "PASS", [] + "" && (a = "FAIL")), a); + } + expect: { + var a; + console.log((a = "PASS", [] + "" && (a = "FAIL")), a); + } + expect_stdout: " PASS" +} + +object_super: { + options = { + conditionals: true, + } + input: { + Object.setPrototypeOf({ + f(a) { + a ? this.g("FAIL") : super.g("FAIL"); + }, + g(b) { + console.log(b); + }, + }, { + g() { + console.log("PASS"); + }, + }).f(); + } + expect: { + Object.setPrototypeOf({ + f(a) { + a ? this.g("FAIL") : super.g("FAIL"); + }, + g(b) { + console.log(b); + }, + }, { + g() { + console.log("PASS"); + }, + }).f(); + } + expect_stdout: "PASS" + node_version: ">=4" +} + +issue_5232_1: { + options = { + conditionals: true, + } + input: { + (function() { + if (Math) { + function f() {} + for (var a in [ 42 ]) + console.log(typeof f); + } else { + var b = null; + return true; + } + })(); + } + expect: { + (function() { + var b; + if (!Math) + return b = null, true; + function f() {} + for (var a in [ 42 ]) console.log(typeof f); + })(); + } + expect_stdout: "function" +} + +issue_5232_2: { + options = { + conditionals: true, + } + input: { + console.log(function() { + if (!Math); + else { + var b = null; + return "PASS"; + } + }()); + } + expect: { + console.log(function() { + var b; + if (Math) + return b = null, "PASS"; + }()); + } + expect_stdout: "PASS" +} + +issue_5232_3: { + options = { + conditionals: true, + } + input: { + console.log(function() { + return function() { + if (console) + console.log("PASS"); + else { + var a = null; + return "FAIL"; + } + }; + }()()); + } + expect: { + console.log(function() { + return function() { + var a; + if (!console) + return a = null, "FAIL"; + console.log("PASS"); + }; + }()()); + } + expect_stdout: [ + "PASS", + "undefined", + ] +} + +issue_5334_1: { + options = { + conditionals: true, + hoist_props: true, + reduce_vars: true, + side_effects: true, + toplevel: true, + unused: true, + } + input: { + function f() { + if (console.log("PASS")) + var o = true, o = { + p: o += console.log("FAIL"), + }; + } + f(); + } + expect: { + (function() { + var o; + console.log("PASS") && (o = true, o = { + p: o += console.log("FAIL"), + }); + })(); + } + expect_stdout: "PASS" +} + +issue_5334_2: { + options = { + conditionals: true, + hoist_props: true, + inline: true, + passes: 3, + reduce_vars: true, + side_effects: true, + toplevel: true, + unused: true, + } + input: { + function f() { + if (console.log("PASS")) + var o = true, o = { + p: o += console.log("FAIL"), + }; + } + f(); + } + expect: { + console.log("PASS") && console.log("FAIL"); + } + expect_stdout: "PASS" +} + +issue_5544_1: { + options = { + conditionals: true, + } + input: { + var a; + if (a) + switch (42) { + case console.log("FAIL"): + case console: + } + else + switch (false) { + case console.log("PASS"): + case console: + } + } + expect: { + var a; + if (a) + switch (42) { + case console.log("FAIL"): + case console: + } + else + switch (false) { + case console.log("PASS"): + case console: + } + } + expect_stdout: "PASS" +} + +issue_5544_2: { + options = { + conditionals: true, + } + input: { + var a; + if (a) + switch (42) { + case console.log("FAIL"): + case console: + } + else + switch (42) { + case console.log("PASS"): + case console: + } + } + expect: { + var a; + if (a) + switch (42) { + case console.log("FAIL"): + case console: + } + else + switch (42) { + case console.log("PASS"): + case console: + } + } + expect_stdout: "PASS" +} + +issue_5546_1: { + options = { + conditionals: true, + } + input: { + var a; + if (a) + try { + console; + } finally { + console.log("FAIL"); + } + else + try { + console; + } finally { + console.log("PASS"); + } + } + expect: { + var a; + if (a) + try { + console; + } finally { + console.log("FAIL"); + } + else + try { + console; + } finally { + console.log("PASS"); + } + } + expect_stdout: "PASS" +} + +issue_5546_2: { + options = { + conditionals: true, + } + input: { + var a; + if (a) + try { + console; + } catch (e) {} + else + try { + console; + } finally { + console.log("PASS"); + } + } + expect: { + var a; + if (a) + try { + console; + } catch (e) {} + else + try { + console; + } finally { + console.log("PASS"); + } + } + expect_stdout: "PASS" +} + +issue_5546_3: { + options = { + conditionals: true, + } + input: { + var a; + if (a) + try { + FAIL; + } catch (e) { + console.log("FAIL"); + } + else + try { + FAIL; + } catch (e) { + console.log("PASS"); + } + } + expect: { + var a; + if (a) + try { + FAIL; + } catch (e) { + console.log("FAIL"); + } + else + try { + FAIL; + } catch (e) { + console.log("PASS"); + } + } + expect_stdout: "PASS" +} + +issue_5666_1: { + options = { + conditionals: true, + reduce_vars: true, + unused: true, + } + input: { + var a; + (function() { + var b = a; + a ? a = b : (b++, a = b); + })(); + console.log(a); + } + expect: { + var a; + (function() { + var b = a; + a = (a ? 0 : b++, b); + })(); + console.log(a); + } + expect_stdout: "NaN" +} + +issue_5666_2: { + options = { + conditionals: true, + reduce_vars: true, + unused: true, + } + input: { + var a = "foo"; + (function() { + var b = a; + a ? (b++, a = b) : a = b; + })(); + console.log(a); + } + expect: { + var a = "foo"; + (function() { + var b = a; + a = (a ? b++ : 0, b); + })(); + console.log(a); + } + expect_stdout: "NaN" +} + +issue_5673_1: { + options = { + conditionals: true, + reduce_vars: true, + unused: true, + } + input: { + var a = "PASS", b = null; + console.log(function(c) { + return c || (b ? c : (c = a) && c); + }()); + } + expect: { + var a = "PASS", b = null; + console.log(function(c) { + return c || (b || (c = a)) && c; + }()); + } + expect_stdout: "PASS" +} + +issue_5673_2: { + options = { + conditionals: true, + reduce_vars: true, + unused: true, + } + input: { + var a = "PASS"; + console.log(function(b) { + return (b = a) ? b : (b = a) && b; + }()); + } + expect: { + var a = "PASS"; + console.log(function(b) { + return a || (b = a) && b; + }()); + } + expect_stdout: "PASS" +} + +issue_5694: { + options = { + conditionals: true, + } + input: { + FORCE_EXEC = "async()=>{}"; + var a = "foo"; + // Node.js v0.12~6 (vm): foo + console.log((NaN = a) ? NaN : 42); + } + expect: { + FORCE_EXEC = "async()=>{}"; + var a = "foo"; + console.log((NaN = a) ? NaN : 42); + } + expect_stdout: "NaN" +} + +issue_5712: { + options = { + booleans: true, + conditionals: true, + evaluate: true, + } + input: { + var a = 0; + a || (++a).toString() && a && console.log("PASS"); + } + expect: { + var a = 0; + a || (++a).toString() && a && console.log("PASS"); + } + expect_stdout: "PASS" +} + +issue_5722: { + options = { + conditionals: true, + evaluate: true, + keep_fnames: true, + side_effects: true, + } + input: { + var a = true; + a && function f() { + return 42; + }(a++) ? null + (console.log("PASS") && a++) : ""; + } + expect: { + var a = true; + a && (void a++, console.log("PASS")) && a++; + } + expect_stdout: "PASS" +} diff --git a/test/compress/const.js b/test/compress/const.js new file mode 100644 index 00000000000..8c56980fe17 --- /dev/null +++ b/test/compress/const.js @@ -0,0 +1,2206 @@ +mangle_block: { + mangle = { + toplevel: false, + } + input: { + var o = "PASS"; + { + const a = "FAIL"; + } + console.log(o); + } + expect: { + var o = "PASS"; + { + const a = "FAIL"; + } + console.log(o); + } + expect_stdout: "PASS" +} + +mangle_block_toplevel: { + mangle = { + toplevel: true, + } + input: { + var o = "PASS"; + { + const a = "FAIL"; + } + console.log(o); + } + expect: { + var o = "PASS"; + { + const c = "FAIL"; + } + console.log(o); + } + expect_stdout: "PASS" +} + +mangle_catch_1: { + mangle = {} + input: { + try { + throw "eeeee"; + } catch (c) { + const e = typeof d; + } + console.log(typeof a, typeof b); + } + expect: { + try { + throw "eeeee"; + } catch (o) { + const e = typeof d; + } + console.log(typeof a, typeof b); + } + expect_stdout: "undefined undefined" +} + +mangle_catch_2: { + mangle = {} + input: { + console.log(function f() { + try {} catch (e) { + const f = 0; + } + }()); + } + expect: { + console.log(function o() { + try {} catch (c) { + const o = 0; + } + }()); + } + expect_stdout: "undefined" +} + +retain_block: { + options = {} + input: { + { + const a = "FAIL"; + } + var a = "PASS"; + console.log(a); + } + expect: { + { + const a = "FAIL"; + } + var a = "PASS"; + console.log(a); + } + expect_stdout: true +} + +retain_catch: { + options = { + dead_code: true, + } + input: { + try {} catch (a) { + const a = "aa"; + } + } + expect: { + try {} catch (a) { + const a = "aa"; + } + } + expect_stdout: true +} + +if_dead_branch: { + options = { + conditionals: true, + dead_code: true, + evaluate: true, + } + input: { + console.log(function() { + if (0) { + const a = 0; + } + return typeof a; + }()); + } + expect: { + console.log(function() { + 0; + { + const a = void 0; + } + return typeof a; + }()); + } + expect_stdout: "undefined" +} + +retain_tail_1: { + options = { + conditionals: true, + } + input: { + function f(a) { + var b = "foo"; + if (a) { + const b = "bar"; + while (console.log("baz")); + console.log(b); + } else { + while (console.log("moo")); + console.log(b); + } + } + f(); + f(42); + } + expect: { + function f(a) { + var b = "foo"; + if (a) { + const b = "bar"; + while (console.log("baz")); + console.log(b); + } else { + while (console.log("moo")); + console.log(b); + } + } + f(); + f(42); + } + expect_stdout: true +} + +retain_tail_2: { + options = { + conditionals: true, + } + input: { + function f(a) { + var b = "foo"; + if (a) { + while (console.log("bar")); + console.log(b); + } else { + const b = "baz"; + while (console.log("moo")); + console.log(b); + } + } + f(); + f(42); + } + expect: { + function f(a) { + var b = "foo"; + if (a) { + while (console.log("bar")); + console.log(b); + } else { + const b = "baz"; + while (console.log("moo")); + console.log(b); + } + } + f(); + f(42); + } + expect_stdout: true +} + +merge_vars_1: { + options = { + merge_vars: true, + toplevel: true, + } + input: { + const a = console; + console.log(typeof a); + var b = typeof a; + console.log(b); + } + expect: { + const a = console; + console.log(typeof a); + var b = typeof a; + console.log(b); + } + expect_stdout: [ + "object", + "object", + ] +} + +merge_vars_2: { + options = { + inline: true, + merge_vars: true, + toplevel: true, + } + input: { + var a = 0; + (function() { + var b = function f() { + const c = a && f; + c.var += 0; + }(); + console.log(b); + })(1 && --a); + } + expect: { + var a = 0; + 1 && --a, + b = function f() { + const c = a && f; + c.var += 0; + }(), + void console.log(b); + var b; + } + expect_stdout: "undefined" +} + +merge_vars_3: { + options = { + merge_vars: true, + toplevel: true, + } + input: { + { + const a = 0; + var b = console; + console.log(typeof b); + } + var a = 1; + console.log(typeof a); + } + expect: { + { + const a = 0; + var b = console; + console.log(typeof b); + } + var a = 1; + console.log(typeof a); + } + expect_stdout: true +} + +merge_vars_4: { + options = { + merge_vars: true, + toplevel: true, + } + input: { + var a = 1; + console.log(typeof a); + { + var b = console; + console.log(typeof b); + const a = 0; + } + } + expect: { + var a = 1; + console.log(typeof a); + { + var b = console; + console.log(typeof b); + const a = 0; + } + } + expect_stdout: true +} + +use_before_init_1: { + options = { + reduce_vars: true, + toplevel: true, + } + input: { + a = "foo"; + const a = "bar"; + } + expect: { + a = "foo"; + const a = "bar"; + } + expect_stdout: true +} + +use_before_init_2: { + options = { + toplevel: true, + unused: true, + } + input: { + try { + a = "foo"; + } catch (e) { + console.log("PASS"); + } + const a = "bar"; + } + expect: { + try { + a = "foo"; + } catch (e) { + console.log("PASS"); + } + const a = "bar"; + } + expect_stdout: true +} + +use_before_init_3: { + options = { + side_effects: true, + } + input: { + try { + a; + } catch (e) { + console.log("PASS"); + } + const a = 42; + } + expect: { + try { + a; + } catch (e) { + console.log("PASS"); + } + const a = 42; + } + expect_stdout: true +} + +use_before_init_4: { + options = { + reduce_vars: true, + } + input: { + try { + console.log(a); + } catch (e) { + console.log("PASS"); + } + const a = "FAIL"; + } + expect: { + try { + console.log(a); + } catch (e) { + console.log("PASS"); + } + const a = "FAIL"; + } + expect_stdout: true +} + +collapse_block: { + options = { + collapse_vars: true, + pure_getters: "strict", + unsafe: true, + } + input: { + { + const a = typeof console; + console.log(a); + } + } + expect: { + { + const a = typeof console; + console.log(a); + } + } + expect_stdout: "object" +} + +reduce_block_1: { + options = { + reduce_vars: true, + } + input: { + { + const a = typeof console; + console.log(a); + } + } + expect: { + { + const a = typeof console; + console.log(a); + } + } + expect_stdout: "object" +} + +reduce_block_2: { + options = { + reduce_vars: true, + } + input: { + { + const a = typeof console; + console.log(a); + } + console.log(typeof a); + } + expect: { + { + const a = typeof console; + console.log(a); + } + console.log(typeof a); + } + expect_stdout: true +} + +reduce_block_2_toplevel: { + options = { + reduce_vars: true, + toplevel: true, + } + input: { + { + const a = typeof console; + console.log(a); + } + console.log(typeof a); + } + expect: { + { + const a = typeof console; + console.log(a); + } + console.log(typeof a); + } + expect_stdout: true +} + +hoist_props: { + options = { + hoist_props: true, + reduce_vars: true, + } + input: { + { + const o = { + p: "PASS", + }; + console.log(o.p); + } + } + expect: { + { + const o = { + p: "PASS", + }; + console.log(o.p); + } + } + expect_stdout: "PASS" +} + +loop_block_1: { + options = { + loops: true, + } + input: { + do { + const o = console; + console.log(typeof o.log); + } while (!console); + } + expect: { + do { + const o = console; + console.log(typeof o.log); + } while (!console); + } + expect_stdout: "function" +} + +loop_block_2: { + options = { + loops: true, + } + input: { + do { + const o = {}; + (function() { + console.log(typeof this, o.p++); + })(); + } while (!console); + } + expect: { + do { + const o = {}; + (function() { + console.log(typeof this, o.p++); + })(); + } while (!console); + } + expect_stdout: "object NaN" +} + +do_continue: { + options = { + loops: true, + } + input: { + try { + do { + { + const a = 0; + continue; + } + } while ([ A ]); + } catch (e) { + console.log("PASS"); + } + } + expect: { + try { + do { + const a = 0; + continue; + } while ([ A ]); + } catch (e) { + console.log("PASS"); + } + } + expect_stdout: "PASS" +} + +catch_ie8_1: { + options = { + ie: true, + unused: true, + } + input: { + try {} catch (a) {} + console.log(function a() { + const a = 0; + }()); + } + expect: { + try {} catch (a) {} + console.log(function() { + }()); + } + expect_stdout: "undefined" +} + +catch_ie8_2: { + options = { + dead_code: true, + ie: true, + passes: 2, + toplevel: true, + unused: true, + } + input: { + try {} catch (a) { + const b = 0; + } + try {} catch (b) {} + console.log(function() { + return this; + }().b); + } + expect: { + console.log(function() { + return this; + }().b); + } + expect_stdout: "undefined" +} + +dead_block_after_return: { + options = { + dead_code: true, + } + input: { + (function(a) { + console.log(a); + return; + { + const a = 0; + } + })(); + } + expect: { + (function(a) { + console.log(a); + return; + { + const a = void 0; + } + })(); + } + expect_stdout: true +} + +if_return_3: { + options = { + if_return: true, + } + input: { + var a = "PASS"; + function f(b) { + if (console) { + const b = a; + return b; + } else + while (console.log("FAIL 1")); + return b; + } + console.log(f("FAIL 2")); + } + expect: { + var a = "PASS"; + function f(b) { + if (console) { + const b = a; + return b; + } else + while (console.log("FAIL 1")); + return b; + } + console.log(f("FAIL 2")); + } + expect_stdout: true +} + +do_if_continue_1: { + options = { + if_return: true, + } + input: { + do { + if (console) { + console.log("PASS"); + { + const a = 0; + var b; + continue; + } + } + } while (b); + } + expect: { + do { + if (console) { + console.log("PASS"); + { + const a = 0; + var b; + } + } + } while (b); + } + expect_stdout: "PASS" +} + +do_if_continue_2: { + options = { + if_return: true, + } + input: { + do { + if (console) { + console.log("PASS"); + { + const a = 0; + A = 0; + continue; + } + } + } while (A); + } + expect: { + do { + if (console) { + console.log("PASS"); + { + const a = 0; + A = 0; + } + } + } while (A); + } + expect_stdout: "PASS" +} + +drop_unused: { + options = { + evaluate: true, + side_effects: true, + unused: true, + } + input: { + function f(a) { + const b = a, c = b; + 0 && c.p++; + } + console.log(f()); + } + expect: { + function f(a) { + const b = a; + b; + } + console.log(f()); + } + expect_stdout: "undefined" +} + +legacy_scope: { + options = { + toplevel: true, + unused: true, + } + input: { + { + const a = 42; + } + var a; + } + expect: { + { + const a = 42; + } + var a; + } + expect_stdout: true +} + +hoist_vars: { + options = { + hoist_vars: true, + } + input: { + { + const a = "FAIL"; + var b = 42; + } + var a = "PASS"; + console.log(a, b); + } + expect: { + var b; + { + const a = "FAIL"; + b = 42; + } + var a = "PASS"; + console.log(a, b); + } + expect_stdout: true +} + +issue_4191: { + options = { + functions: true, + reduce_vars: true, + unused: true, + } + input: { + { + const a = function() {}; + } + console.log(typeof a); + } + expect: { + { + const a = function() {}; + } + console.log(typeof a); + } + expect_stdout: true +} + +issue_4193: { + options = { + dead_code: true, + } + input: { + try {} catch (e) { + var a; + } finally { + const a = 0; + } + console.log(a); + } + expect: { + var a; + { + const a = 0; + } + console.log(a); + } + expect_stdout: true +} + +issue_4195: { + mangle = { + ie: true, + } + input: { + console.log(function f(a) { + (function a() { + { + const b = f, a = 0; + b; + } + })(); + a && f; + }()); + } + expect: { + console.log(function f(o) { + (function o() { + { + const n = f, o = 0; + n; + } + })(); + o && f; + }()); + } + expect_stdout: "undefined" +} + +issue_4197: { + options = { + collapse_vars: true, + } + input: { + var a = 0; + try { + const b = function() { + a = 1; + b[1]; + }(); + } catch (e) { + console.log(a); + } + } + expect: { + var a = 0; + try { + const b = function() { + a = 1; + b[1]; + }(); + } catch (e) { + console.log(a); + } + } + expect_stdout: "1" +} + +issue_4198: { + options = { + reduce_vars: true, + } + input: { + console.log(function() { + try { + throw "PASS"; + } catch (e) { + { + const e = "FAIL"; + } + return function() { + return e; + }(); + } + }()); + } + expect: { + console.log(function() { + try { + throw "PASS"; + } catch (e) { + { + const e = "FAIL"; + } + return function() { + return e; + }(); + } + }()); + } + expect_stdout: "PASS" +} + +issue_4202: { + options = { + inline: true, + toplevel: true, + } + input: { + { + const o = {}; + (function() { + function f() { + o.p = 42; + } + f(f); + })(); + console.log(o.p++); + } + } + expect: { + { + const o = {}; + function f() { + o.p = 42; + } + f(f); + console.log(o.p++); + } + } + expect_stdout: "42" +} + +issue_4205: { + options = { + collapse_vars: true, + } + input: { + var a = function(b) { + var c = function() { + switch (0) { + case a: + return 0; + case b: + case console.log("PASS"): + } + }(); + { + const b = c; + } + }(); + } + expect: { + var a = function(b) { + var c = function() { + switch (0) { + case a: + return 0; + case b: + case console.log("PASS"): + } + }(); + { + const b = c; + } + }(); + } + expect_stdout: true +} + +issue_4207: { + options = { + reduce_funcs: true, + reduce_vars: true, + unused: true, + } + input: { + { + const a = function() {}; + console.log(a.length); + } + } + expect: { + { + const a = function() {}; + console.log(a.length); + } + } + expect_stdout: "0" +} + +issue_4218: { + options = { + reduce_funcs: true, + reduce_vars: true, + toplevel: true, + unused: true, + } + input: { + { + const a = function() {}; + var b = 0 * a; + } + console.log(typeof a, b); + } + expect: { + { + const a = function() {}; + var b = 0 * a; + } + console.log(typeof a, b); + } + expect_stdout: true +} + +issue_4210: { + options = { + reduce_vars: true, + } + input: { + (function() { + try { + throw 42; + } catch (e) { + const a = typeof e; + console.log(a); + } finally { + return a = "foo"; + } + })(); + console.log(typeof a); + } + expect: { + (function() { + try { + throw 42; + } catch (e) { + const a = typeof e; + console.log(a); + } finally { + return a = "foo"; + } + })(); + console.log(typeof a); + } + expect_stdout: true +} + +issue_4212_1: { + options = { + dead_code: true, + } + input: { + console.log({ + get b() { + const a = 0; + return a /= 0; + } + }.b); + } + expect: { + console.log({ + get b() { + const a = 0; + return a /= 0; + } + }.b); + } + expect_stdout: true +} + +issue_4212_2: { + options = { + reduce_vars: true, + } + input: { + console.log({ + get b() { + const a = 0; + return a /= 0; + } + }.b); + } + expect: { + console.log({ + get b() { + const a = 0; + return a /= 0; + } + }.b); + } + expect_stdout: true +} + +issue_4216: { + options = { + collapse_vars: true, + conditionals: true, + dead_code: true, + evaluate: true, + } + input: { + if (a = 0) { + const a = 0; + } + console.log(typeof a); + } + expect: { + a = 0; + { + const a = void 0; + } + console.log(typeof a); + } + expect_stdout: true +} + +skip_braces: { + beautify = { + beautify: true, + braces: true, + } + input: { + if (console) + const a = 42; + else + const b = null; + console.log(typeof a, typeof b); + } + expect_exact: [ + "if (console) const a = 42; else const b = null;", + "", + "console.log(typeof a, typeof b);", + ] + expect_stdout: true +} + +issue_4220: { + options = { + collapse_vars: true, + conditionals: true, + sequences: true, + toplevel: true, + } + input: { + if (console) { + var o = console; + for (var k in o); + } else { + const a = 0; + } + console.log(typeof a); + } + expect: { + if (console) { + var o; + for (var k in console); + } else { + const a = 0; + } + console.log(typeof a); + } + expect_stdout: true +} + +issue_4222: { + options = { + inline: true, + reduce_vars: true, + toplevel: true, + unused: true, + } + input: { + { + const a = function() { + return function() {}; + }; + var b = a(); + } + b(); + console.log(typeof a); + } + expect: { + { + const a = function() { + return function() {}; + }; + var b = a(); + } + b(); + console.log(typeof a); + } + expect_stdout: true +} + +issue_4225: { + options = { + side_effects: true, + } + input: { + const a = void typeof b; + const b = 42; + console.log(a, b); + } + expect: { + const a = void b; + const b = 42; + console.log(a, b); + } + expect_stdout: true +} + +issue_4229: { + options = { + ie: true, + side_effects: true, + } + input: { + (function f() { + f; + const f = 42; + })(); + } + expect: { + (function f() { + f; + const f = 42; + })(); + } + expect_stdout: true +} + +issue_4231: { + options = { + ie: true, + side_effects: true, + } + input: { + typeof a == 0; + console.log(typeof function a() { + const a = 0; + }); + } + expect: { + console.log(typeof function a() { + const a = 0; + }); + } + expect_stdout: "function" +} + +issue_4245: { + options = { + booleans: true, + } + input: { + const a = f(); + function f() { + typeof a; + } + } + expect: { + const a = f(); + function f() { + a, + 1; + } + } + expect_stdout: true +} + +issue_4248: { + options = { + collapse_vars: true, + } + input: { + var a = "FAIL"; + try { + (function() { + a = "PASS"; + b[a]; + const b = 0; + })(); + } catch (e) { + console.log(a); + } + } + expect: { + var a = "FAIL"; + try { + (function() { + a = "PASS"; + b[a]; + const b = 0; + })(); + } catch (e) { + console.log(a); + } + } + expect_stdout: "PASS" +} + +issue_4261_1: { + options = { + inline: 3, + reduce_funcs: true, + reduce_vars: true, + toplevel: true, + unused: true, + } + input: { + { + const a = 42; + (function() { + function f() { + console.log(a); + } + function g() { + while (f()); + } + (function() { + while (g()); + })(); + })(); + } + } + expect: { + { + const a = 42; + (function() { + function g() { + while (void console.log(a)); + } + (function() { + while (g()); + })(); + })(); + } + } + expect_stdout: "42" +} + +issue_4261_2: { + options = { + if_return: true, + inline: true, + reduce_funcs: true, + reduce_vars: true, + toplevel: true, + unused: true, + } + input: { + { + const a = 42; + (function() { + function f() { + console.log(a); + } + function g() { + while (f()); + } + (function() { + while (g()); + })(); + })(); + } + } + expect: { + { + const a = 42; + function g() { + while (void console.log(a)); + } + while (g()); + } + } + expect_stdout: "42" +} + +issue_4274_1: { + options = { + loops: true, + } + input: { + for (;;) { + if (console.log("PASS")) { + const a = 0; + } else { + break; + var a; + } + } + } + expect: { + for (; console.log("PASS");) { + { + const a = 0; + } + var a; + } + } + expect_stdout: true +} + +issue_4274_2: { + options = { + loops: true, + } + input: { + for (;;) { + if (!console.log("PASS")) { + break; + var a; + } else { + const a = 0; + } + } + } + expect: { + for (; console.log("PASS");) { + { + const a = 0; + } + var a; + } + } + expect_stdout: true +} + +issue_4290_1: { + options = { + unused: true, + } + input: { + const a = 0; + var a; + } + expect: { + const a = 0; + var a; + } + expect_stdout: true +} + +issue_4305_1: { + options = { + reduce_vars: true, + unused: true, + } + input: { + (function() { + const arguments = function() { + while (console.log("PASS")); + }; + arguments(); + })(); + } + expect: { + (function() { + const arguments = function() { + while (console.log("PASS")); + }; + arguments(); + })(); + } + expect_stdout: true +} + +issue_4305_2: { + options = { + reduce_vars: true, + unused: true, + } + input: { + (function(a) { + const a = function() { + while (console.log("aaaaa")); + }; + a(); + })(); + } + expect: { + (function(a) { + const a = function() { + while (console.log("aaaaa")); + }; + a(); + })(); + } + expect_stdout: true +} + +issue_4365_1: { + options = { + toplevel: true, + unused: true, + } + input: { + const arguments = 42; + } + expect: { + const arguments = 42; + } + expect_stdout: true +} + +issue_4365_2: { + options = { + toplevel: true, + varify: true, + } + input: { + const arguments = 42; + } + expect: { + const arguments = 42; + } + expect_stdout: true +} + +issue_4527: { + mangle = {} + input: { + (function() { + try { + throw 1; + } catch (a) { + try { + const a = FAIL; + } finally { + if (!b) + return console.log("aaaa"); + } + } + var b; + })(); + } + expect: { + (function() { + try { + throw 1; + } catch (a) { + try { + const a = FAIL; + } finally { + if (!t) + return console.log("aaaa"); + } + } + var t; + })(); + } + expect_stdout: "aaaa" +} + +issue_4689: { + options = { + sequences: true, + } + input: { + "use strict"; + var a = "PASS"; + console.log(a); + for (const a in 42); + } + expect: { + "use strict"; + var a = "PASS"; + console.log(a); + for (const a in 42); + } + expect_stdout: "PASS" + node_version: ">=4" +} + +issue_4691: { + options = { + conditionals: true, + if_return: true, + toplevel: true, + } + input: { + function A() {} + A.prototype.f = function() { + if (!this) + return; + const a = "PA"; + function g(b) { + h(a + b); + } + [ "SS" ].forEach(function(c) { + g(c); + }); + }; + function h(d) { + console.log(d); + } + new A().f(); + } + expect: { + function A() {} + A.prototype.f = function() { + if (this) { + const a = "PA"; + [ "SS" ].forEach(function(c) { + g(c); + }); + function g(b) { + h(a + b); + } + } + }; + function h(d) { + console.log(d); + } + new A().f(); + } + expect_stdout: "PASS" +} + +issue_4848: { + options = { + if_return: true, + } + input: { + function f(a) { + a(function() { + console.log(b); + }); + if (!console) + return; + const b = "PASS"; + } + var g; + f(function(h) { + g = h; + }); + g(); + } + expect: { + function f(a) { + a(function() { + console.log(b); + }); + if (!console) + return; + const b = "PASS"; + } + var g; + f(function(h) { + g = h; + }); + g(); + } + expect_stdout: "PASS" +} + +issue_4954_1: { + rename = true + input: { + "use strict"; + (function() { + { + const a = "foo"; + console.log(a); + } + { + const a = "bar"; + console.log(a); + } + })(); + } + expect: { + "use strict"; + (function() { + { + const a = "foo"; + console.log(a); + } + { + const b = "bar"; + console.log(b); + } + })(); + } + expect_stdout: [ + "foo", + "bar", + ] + node_version: ">=4" +} + +issue_4954_2: { + mangle = {} + input: { + "use strict"; + const a = null; + (function(b) { + for (const a in null); + for (const a in b) + console.log("PASS"); + })([ null ]); + } + expect: { + "use strict"; + const a = null; + (function(o) { + for (const n in null); + for (const n in o) + console.log("PASS"); + })([ null ]); + } + expect_stdout: "PASS" + node_version: ">=4" +} + +issue_4960: { + mangle = {} + input: { + "use strict"; + var a; + (function() { + { + const a = console.log("PASS"); + } + try {} catch (e) { + const a = console.log("FAIL"); + } + })(); + } + expect: { + "use strict"; + var a; + (function() { + { + const o = console.log("PASS"); + } + try {} catch (o) { + const c = console.log("FAIL"); + } + })(); + } + expect_stdout: "PASS" + node_version: ">=4" +} + +issue_4965_1: { + mangle = {} + input: { + "use strict"; + try { + c; + } catch (a) { + { + const a = 1; + } + { + const a = console.log(typeof c); + } + } + } + expect: { + "use strict"; + try { + c; + } catch (t) { + { + const c = 1; + } + { + const t = console.log(typeof c); + } + } + } + expect_stdout: "undefined" + node_version: ">=4" +} + +issue_4965_2: { + mangle = {} + input: { + "use strict"; + try { + throw 1; + } catch (e) { + try { + { + const e = 2; + } + } finally { + const e = 3; + console.log(typeof t); + } + } + } + expect: { + "use strict"; + try { + throw 1; + } catch (o) { + try { + { + const t = 2; + } + } finally { + const o = 3; + console.log(typeof t); + } + } + } + expect_stdout: "undefined" + node_version: ">=4" +} + +issue_5254: { + options = { + inline: true, + toplevel: true, + } + input: { + do { + (function() { + const a = console.log; + a && a("foo"); + })(); + } while (console.log("bar")); + } + expect: { + do { + const a = console.log; + a && a("foo"); + } while (console.log("bar")); + } + expect_stdout: [ + "foo", + "bar", + ] +} + +issue_5260: { + options = { + collapse_vars: true, + } + input: { + "use strict"; + var a = "foo", o; + while (console.log("bar")); + o = { + baz: function(b) { + console.log(a, b); + }, + }; + for (const a in o) + o[a](a); + } + expect: { + "use strict"; + var a = "foo", o; + while (console.log("bar")); + o = { + baz: function(b) { + console.log(a, b); + }, + }; + for (const a in o) + o[a](a); + } + expect_stdout: [ + "bar", + "foo baz", + ] + node_version: ">=4" +} + +issue_5319: { + options = { + collapse_vars: true, + merge_vars: true, + } + input: { + (function(a, c) { + var b = a, c = b; + { + const a = c; + console.log(c()); + } + })(function() { + return "PASS"; + }); + } + expect: { + (function(a, c) { + var b = a, c; + { + const a = c = b; + console.log(c()); + } + })(function() { + return "PASS"; + }); + } + expect_stdout: true +} + +issue_5338: { + options = { + unused: true, + } + input: { + const a = a; + } + expect: { + const a = a; + } + expect_stdout: true +} + +issue_5476: { + mangle = { + keep_fargs: true, + } + input: { + console.log(function(n) { + const a = 42; + }()); + } + expect: { + console.log(function(n) { + const o = 42; + }()); + } + expect_stdout: "undefined" +} + +issue_5516: { + options = { + inline: true, + reduce_funcs: true, + reduce_vars: true, + unused: true, + } + input: { + console.log(typeof function() { + try {} catch (a) { + (function f() { + a; + })(); + } + { + const a = function() {}; + return a; + } + }()); + } + expect: { + console.log(typeof function() { + try {} catch (a) { + void a; + } + { + const a = function() {}; + return a; + } + }()); + } + expect_stdout: "function" +} + +issue_5580_1: { + mangle = {} + input: { + "use strict"; + console.log(function(a, b, c) { + try { + FAIL; + } catch (e) { + return function() { + var d = e, i, j; + { + const e = j; + } + return a; + }(); + } finally { + const e = 42; + } + }("PASS")); + } + expect: { + "use strict"; + console.log(function(r, n, t) { + try { + FAIL; + } catch (o) { + return function() { + var n = o, t, c; + { + const o = c; + } + return r; + }(); + } finally { + const c = 42; + } + }("PASS")); + } + expect_stdout: "PASS" + node_version: ">=4" +} + +issue_5580_2: { + options = { + inline: true, + reduce_vars: true, + varify: true, + } + input: { + "use strict"; + (function() { + try { + throw "PASS"; + } catch (e) { + return function() { + console.log(e); + { + const e = "FAIL 1"; + } + }(); + } finally { + const e = "FAIL 2"; + } + })(); + } + expect: { + "use strict"; + (function() { + try { + throw "PASS"; + } catch (e) { + console.log(e); + { + const e = "FAIL 1"; + } + return; + } finally { + var e = "FAIL 2"; + } + })(); + } + expect_stdout: "PASS" + node_version: ">=4" +} + +issue_5591: { + options = { + dead_code: true, + if_return: true, + } + input: { + "use strict"; + function f(a) { + switch (console.log("foo")) { + case console.log("bar"): + if (console.log("baz")) + return; + else { + const a = 42; + return; + } + break; + case null: + FAIL; + } + } + f(); + } + expect: { + "use strict"; + function f(a) { + switch (console.log("foo")) { + case console.log("bar"): + if (console.log("baz")) + return; + else { + const a = 42; + return; + } + case null: + FAIL; + } + } + f(); + } + expect_stdout: [ + "foo", + "bar", + "baz", + ] + node_version: ">=4" +} + +issue_5656: { + options = { + collapse_vars: true, + merge_vars: true, + } + input: { + console.log(function(a) { + var b = a; + b++; + { + const a = b; + } + }()); + } + expect: { + console.log(function(a) { + var b = a; + { + const a = ++b; + } + }()); + } + expect_stdout: true +} + +issue_5660: { + options = { + merge_vars: true, + side_effects: true, + } + input: { + function f() { + try { + a; + var b; + return b; + } catch (e) { + var a = "FAIL"; + const b = null; + return a; + } + } + console.log(f()); + } + expect: { + function f() { + try { + var b; + return b; + } catch (e) { + var a = "FAIL"; + const b = null; + return a; + } + } + console.log(f()); + } + expect_stdout: true +} diff --git a/test/compress/dead-code.js b/test/compress/dead-code.js index 0fd066eb297..a5bb1380844 100644 --- a/test/compress/dead-code.js +++ b/test/compress/dead-code.js @@ -1,7 +1,7 @@ dead_code_1: { options = { - dead_code: true - }; + dead_code: true, + } input: { function f() { a(); @@ -25,13 +25,13 @@ dead_code_1: { dead_code_2_should_warn: { options = { - dead_code: true - }; + dead_code: true, + } input: { function f() { g(); x = 10; - throw "foo"; + throw new Error("foo"); // completely discarding the `if` would introduce some // bugs. UglifyJS v1 doesn't deal with this issue; in v2 // we copy any declarations to the upper scope. @@ -46,44 +46,1687 @@ dead_code_2_should_warn: { })(); } } + f(); } expect: { function f() { g(); x = 10; - throw "foo"; - var x; - function g(){}; + throw new Error("foo"); + { + var x; + function g(){}; + } } + f(); } + expect_stdout: true + expect_warnings: [ + "WARN: Dropping unreachable code [test/compress/dead-code.js:8,12]", + ] } dead_code_constant_boolean_should_warn_more: { options = { - dead_code : true, - loops : true, - booleans : true, - conditionals : true, - evaluate : true - }; + booleans: true, + conditionals: true, + dead_code: true, + evaluate: true, + loops: true, + side_effects: true, + } input: { while (!((foo && bar) || (x + "0"))) { console.log("unreachable"); var foo; function bar() {} } - for (var x = 10; x && (y || x) && (!typeof x); ++x) { + for (var x = 10, y; x && (y || x) && (!typeof x); ++x) { asdf(); foo(); var moo; } + bar(); } expect: { - var foo; - function bar() {} + { + var foo; + function bar() {} + } // nothing for the while // as for the for, it should keep: - var x = 10; + var x = 10, y; var moo; + bar(); + } + expect_stdout: true + expect_warnings: [ + "WARN: + in boolean context always true [test/compress/dead-code.js:1,33]", + "WARN: Boolean || always true [test/compress/dead-code.js:1,16]", + "WARN: Dropping unreachable code [test/compress/dead-code.js:1,45]", + "WARN: Boolean expression always true [test/compress/dead-code.js:6,47]", + "WARN: Boolean && always false [test/compress/dead-code.js:6,28]", + "WARN: Dropping unreachable code [test/compress/dead-code.js:6,63]", + "WARN: Dropping side-effect-free statement [test/compress/dead-code.js:1,15]", + "WARN: Dropping side-effect-free statement [test/compress/dead-code.js:6,28]", + ] + node_version: "<=4" +} + +trim_try: { + options = { + dead_code: true, + } + input: { + try { + var a; + } catch (e) { + console.log("FAIL"); + } finally { + console.log(a); + } + } + expect: { + var a; + console.log(a); + } + expect_stdout: "undefined" +} + +trim_finally_1: { + options = { + dead_code: true, + } + input: { + try { + console.log("PASS"); + } finally { + var a; + } + } + expect: { + console.log("PASS"); + var a; + } + expect_stdout: "PASS" +} + +trim_finally_2: { + options = { + dead_code: true, + } + input: { + try { + console.log("PASS"); + } catch (e) { + } finally { + var a; + } + } + expect: { + try { + console.log("PASS"); + var a; + } catch (e) { + } + } + expect_stdout: "PASS" +} + +try_catch_finally: { + options = { + conditionals: true, + dead_code: true, + evaluate: true, + passes: 2, + side_effects: true, + } + input: { + var a = 1; + !function() { + try { + if (false) throw x; + } catch (a) { + var a = 2; + console.log("FAIL"); + } finally { + a = 3; + console.log("PASS"); + } + }(); + try { + console.log(a); + } finally { + } + } + expect: { + var a = 1; + !function() { + var a; + a = 3; + console.log("PASS"); + }(); + console.log(a); + } + expect_stdout: [ + "PASS", + "1", + ] +} + +collapse_vars_assignment: { + options = { + collapse_vars: true, + dead_code: true, + passes: 2, + unused: true, + } + input: { + function f0(c) { + var a = 3 / c; + return a = a; + } + } + expect: { + function f0(c) { + return 3 / c; + } + } +} + +collapse_vars_lvalues_drop_assign: { + options = { + collapse_vars: true, + dead_code: true, + unused: true, + } + input: { + function f0(x) { var i = ++x; return x += i; } + function f1(x) { var a = (x -= 3); return x += a; } + function f2(x) { var z = x, a = ++z; return z += a; } + } + expect: { + function f0(x) { var i = ++x; return x + i; } + function f1(x) { var a = (x -= 3); return x + a; } + function f2(x) { var z = x, a = ++z; return z + a; } + } +} + +collapse_vars_misc: { + options = { + collapse_vars: true, + dead_code: true, + unused: true, + } + input: { + function f10(x) { var a = 5, b = 3; return a += b; } + function f11(x) { var a = 5, b = 3; return a += --b; } + } + expect: { + function f10(x) { return 5 + 3; } + function f11(x) { var b = 3; return 5 + --b; } + } +} + +return_assignment: { + options = { + dead_code: true, + unused: true, + } + input: { + function f1(a, b, c) { + return a = x(), b = y(), b = a && (c >>= 5); + } + function f2() { + return e = x(); + } + function f3(e) { + return e = x(); + } + function f4() { + var e; + return e = x(); + } + function f5(a) { + try { + return a = x(); + } catch (b) { + console.log(a); + } + } + function f6(a) { + try { + return a = x(); + } finally { + console.log(a); + } + } + function y() { + console.log("y"); + } + function test(inc) { + var counter = 0; + x = function() { + counter += inc; + if (inc < 0) throw counter; + return counter; + }; + [ f1, f2, f3, f4, f5, f6 ].forEach(function(f, i) { + e = null; + try { + i += 1; + console.log("result " + f(10 * i, 100 * i, 1000 * i)); + } catch (x) { + console.log("caught " + x); + } + if (null !== e) console.log("e: " + e); + }); + } + var x, e; + test(1); + test(-1); + } + expect: { + function f1(a, b, c) { + return a = x(), y(), a && (c >> 5); + } + function f2() { + return e = x(); + } + function f3(e) { + return x(); + } + function f4() { + return x(); + } + function f5(a) { + try { + return x(); + } catch (b) { + console.log(a); + } + } + function f6(a) { + try { + return a = x(); + } finally { + console.log(a); + } + } + function y() { + console.log("y"); + } + function test(inc) { + var counter = 0; + x = function() { + counter += inc; + if (inc < 0) throw counter; + return counter; + }; + [ f1, f2, f3, f4, f5, f6 ].forEach(function(f, i) { + e = null; + try { + i += 1; + console.log("result " + f(10 * i, 100 * i, 1000 * i)); + } catch (x) { + console.log("caught " + x); + } + if (null !== e) console.log("e: " + e); + }); + } + var x, e; + test(1); + test(-1); + } + expect_stdout: [ + "y", + "result 31", + "result 2", + "e: 2", + "result 3", + "result 4", + "result 5", + "6", + "result 6", + "caught -1", + "caught -2", + "caught -3", + "caught -4", + "50", + "result undefined", + "60", + "caught -6", + ] +} + +throw_assignment: { + options = { + dead_code: true, + unused: true, + } + input: { + function f1() { + throw a = x(); + } + function f2(a) { + throw a = x(); + } + function f3() { + var a; + throw a = x(); + } + function f4() { + try { + throw a = x(); + } catch (b) { + console.log(a); + } + } + function f5(a) { + try { + throw a = x(); + } catch (b) { + console.log(a); + } + } + function f6() { + var a; + try { + throw a = x(); + } catch (b) { + console.log(a); + } + } + function f7() { + try { + throw a = x(); + } finally { + console.log(a); + } + } + function f8(a) { + try { + throw a = x(); + } finally { + console.log(a); + } + } + function f9() { + var a; + try { + throw a = x(); + } finally { + console.log(a); + } + } + function test(inc) { + var counter = 0; + x = function() { + counter += inc; + if (inc < 0) throw counter; + return counter; + }; + [ f1, f2, f3, f4, f5, f6, f7, f8, f9 ].forEach(function(f, i) { + a = null; + try { + f(10 * (1 + i)); + } catch (x) { + console.log("caught " + x); + } + if (null !== a) console.log("a: " + a); + }); + } + var x, a; + test(1); + test(-1); + } + expect: { + function f1() { + throw a = x(); + } + function f2(a) { + throw x(); + } + function f3() { + throw x(); + } + function f4() { + try { + throw a = x(); + } catch (b) { + console.log(a); + } + } + function f5(a) { + try { + throw a = x(); + } catch (b) { + console.log(a); + } + } + function f6() { + var a; + try { + throw a = x(); + } catch (b) { + console.log(a); + } + } + function f7() { + try { + throw a = x(); + } finally { + console.log(a); + } + } + function f8(a) { + try { + throw a = x(); + } finally { + console.log(a); + } + } + function f9() { + var a; + try { + throw a = x(); + } finally { + console.log(a); + } + } + function test(inc) { + var counter = 0; + x = function() { + counter += inc; + if (inc < 0) throw counter; + return counter; + }; + [ f1, f2, f3, f4, f5, f6, f7, f8, f9 ].forEach(function(f, i) { + a = null; + try { + f(10 * (1 + i)); + } catch (x) { + console.log("caught " + x); + } + if (null !== a) console.log("a: " + a); + }); + } + var x, a; + test(1); + test(-1); + } + expect_stdout: [ + "caught 1", + "a: 1", + "caught 2", + "caught 3", + "4", + "a: 4", + "5", + "6", + "7", + "caught 7", + "a: 7", + "8", + "caught 8", + "9", + "caught 9", + "caught -1", + "caught -2", + "caught -3", + "null", + "50", + "undefined", + "null", + "caught -7", + "80", + "caught -8", + "undefined", + "caught -9", + ] +} + +issue_2597: { + options = { + dead_code: true, + } + input: { + function f(b) { + try { + try { + throw "foo"; + } catch (e) { + return b = true; + } + } finally { + b && (a = "PASS"); + } + } + var a = "FAIL"; + f(); + console.log(a); + } + expect: { + function f(b) { + try { + try { + throw "foo"; + } catch (e) { + return b = true; + } + } finally { + b && (a = "PASS"); + } + } + var a = "FAIL"; + f(); + console.log(a); + } + expect_stdout: "PASS" +} + +issue_2666: { + options = { + dead_code: true, + } + input: { + function f(a) { + return a = { + p: function() { + return a; + } + }; + } + console.log(typeof f().p()); + } + expect: { + function f(a) { + return a = { + p: function() { + return a; + } + }; + } + console.log(typeof f().p()); + } + expect_stdout: "object" +} + +issue_2692: { + options = { + dead_code: true, + reduce_vars: false, + } + input: { + function f(a) { + return a = g; + function g() { + return a; + } + } + console.log(typeof f()()); + } + expect: { + function f(a) { + return a = g; + function g() { + return a; + } + } + console.log(typeof f()()); + } + expect_stdout: "function" +} + +issue_2701: { + options = { + dead_code: true, + inline: false, + } + input: { + function f(a) { + return a = function() { + return function() { + return a; + }; + }(); + } + console.log(typeof f()()); + } + expect: { + function f(a) { + return a = function() { + return function() { + return a; + }; + }(); + } + console.log(typeof f()()); + } + expect_stdout: "function" +} + +issue_2749: { + options = { + dead_code: true, + inline: true, + toplevel: true, + unused: true, + } + input: { + var a = 2, c = "PASS"; + while (a--) + (function() { + return b ? c = "FAIL" : b = 1; + try { + } catch (b) { + var b; + } + })(); + console.log(c); + } + expect: { + var a = 2, c = "PASS"; + while (a--) + b = void 0, b ? c = "FAIL" : 1; + var b; + console.log(c); + } + expect_stdout: "PASS" +} + +issue_2860_1: { + options = { + dead_code: true, + evaluate: true, + reduce_vars: true, + } + input: { + console.log(function(a) { + return a ^= 1; + }()); + } + expect: { + console.log(1); + } + expect_stdout: "1" +} + +issue_2860_2: { + options = { + dead_code: true, + evaluate: true, + inline: true, + passes: 2, + reduce_vars: true, + } + input: { + console.log(function(a) { + return a ^= 1; + }()); + } + expect: { + console.log(1); + } + expect_stdout: "1" +} + +issue_2929: { + options = { + dead_code: true, + } + input: { + console.log(function(a) { + try { + return null.p = a = 1; + } catch (e) { + return a ? "PASS" : "FAIL"; + } + }()); + } + expect: { + console.log(function(a) { + try { + return null.p = a = 1; + } catch (e) { + return a ? "PASS" : "FAIL"; + } + }()); + } + expect_stdout: "PASS" +} + +issue_3402: { + options = { + dead_code: true, + evaluate: true, + functions: true, + passes: 2, + reduce_vars: true, + side_effects: true, + toplevel: true, + typeofs: true, + unused: true, + } + input: { + var f = function f() { + f = 42; + console.log(typeof f); + }; + "function" == typeof f && f(); + "function" == typeof f && f(); + console.log(typeof f); + } + expect: { + function f() { + console.log(typeof f); + } + f(); + f(); + console.log(typeof f); + } + expect_stdout: [ + "function", + "function", + "function", + ] +} + +issue_3406: { + options = { + dead_code: true, + } + input: { + console.log(function f(a) { + return delete (f = a); + }()); + } + expect: { + console.log(function f(a) { + return delete (0, a); + }()); + } + expect_stdout: "true" +} + +function_assign: { + options = { + dead_code: true, + } + input: { + console.log(function() { + var a = "PASS"; + function h(c) { + return c; + } + h.p = function(b) { + return b; + }.p = a; + return h; + }().p); + } + expect: { + console.log(function() { + var a = "PASS"; + function h(c) { + return c; + } + h.p = a; + return h; + }().p); + } + expect_stdout: "PASS" +} + +issue_3552: { + options = { + dead_code: true, + pure_getters: "strict", + } + input: { + var a = "PASS"; + (function() { + (1..p += 42) && (a = "FAIL"); + })(); + console.log(a); + } + expect: { + var a = "PASS"; + (function() { + (1..p += 42) && (a = "FAIL"); + })(); + console.log(a); + } + expect_stdout: "PASS" +} + +unreachable_assign: { + options = { + dead_code: true, + strings: true, + } + input: { + console.log(A = "P" + (A = "A" + (B = "S" + (A = B = "S"))), A, B); + } + expect: { + console.log(A = "P" + "A" + (B = "S" + "S"), A, B); + } + expect_stdout: "PASS PASS SS" +} + +catch_return_assign: { + options = { + dead_code: true, + } + input: { + console.log(function() { + try { + throw "FAIL"; + } catch (e) { + return e = "PASS"; + } + }()); + } + expect: { + console.log(function() { + try { + throw "FAIL"; + } catch (e) { + return "PASS"; + } + }()); + } + expect_stdout: "PASS" +} + +catch_return_assign_may_throw: { + options = { + dead_code: true, + } + input: { + function f() { + try { + throw "FAIL"; + } catch (e) { + return e = console.log("PASS"); + } + } + f(); + } + expect: { + function f() { + try { + throw "FAIL"; + } catch (e) { + return console.log("PASS"); + } + } + f(); + } + expect_stdout: "PASS" +} + +finally_return_assign: { + options = { + dead_code: true, + } + input: { + console.log(function(a) { + try { + throw "FAIL"; + } finally { + return a = "PASS"; + } + }()); + } + expect: { + console.log(function(a) { + try { + throw "FAIL"; + } finally { + return "PASS"; + } + }()); + } + expect_stdout: "PASS" +} + +last_assign_statement: { + options = { + dead_code: true, + } + input: { + function f(a) { + a = a("PASS"); + } + f(console.log); + } + expect: { + function f(a) { + a("PASS"); + } + f(console.log); + } + expect_stdout: "PASS" +} + +last_assign_if_else: { + options = { + dead_code: true, + } + input: { + function f(a) { + if (a) + a = console.log("foo"); + else { + console.log("bar"); + a = console.log("baz"); + } + } + f(42); + f(null); + } + expect: { + function f(a) { + if (a) + console.log("foo"); + else { + console.log("bar"); + console.log("baz"); + } + } + f(42); + f(null); + } + expect_stdout: [ + "foo", + "bar", + "baz", + ] +} + +last_assign_catch: { + options = { + dead_code: true, + } + input: { + function f() { + try { + throw "FAIL"; + } catch (e) { + e = console.log("PASS"); + } + } + f(); + } + expect: { + function f() { + try { + throw "FAIL"; + } catch (e) { + console.log("PASS"); + } + } + f(); + } + expect_stdout: "PASS" +} + +last_assign_finally: { + options = { + dead_code: true, + } + input: { + function f(a) { + try { + throw a.log; + } catch (e) { + a = e; + } finally { + a = a("PASS"); + } + } + f(console); + } + expect: { + function f(a) { + try { + throw a.log; + } catch (e) { + a = e; + } finally { + a("PASS"); + } + } + f(console); + } + expect_stdout: "PASS" +} + +consecutive_assignments: { + options = { + dead_code: true, + } + input: { + while (a = void 0, a = "PASS", console.log(a)); + var a; + } + expect: { + while (void 0, a = "PASS", console.log(a)); + var a; + } + expect_stdout: "PASS" +} + +issue_3578: { + options = { + dead_code: true, + } + input: { + var a = "FAIL", b, c; + try { + b = c.p = b = 0; + } catch (e) { + b += 42; + b && (a = "PASS"); + } + console.log(a); + } + expect: { + var a = "FAIL", b, c; + try { + b = c.p = b = 0; + } catch (e) { + b += 42; + b && (a = "PASS"); + } + console.log(a); + } + expect_stdout: "PASS" +} + +issue_3830_1: { + options = { + dead_code: true, + } + input: { + var o = { + set p(v) { + o = o.p = o = v; + } + }; + o.p = "PASS"; + console.log(o); + } + expect: { + var o = { + set p(v) { + o = o.p = o = v; + } + }; + o.p = "PASS"; + console.log(o); + } + expect_stdout: "PASS" +} + +issue_3830_2: { + options = { + dead_code: true, + } + input: { + var a = "FAIL"; + var o = { + set FAIL(v) { + a = o[a] = a = v; + } + }; + o[a] = "PASS"; + console.log(a); + } + expect: { + var a = "FAIL"; + var o = { + set FAIL(v) { + a = o[a] = a = v; + } + }; + o[a] = "PASS"; + console.log(a); + } + expect_stdout: "PASS" +} + +issue_3830_3: { + options = { + dead_code: true, + } + input: { + function f() { + return a; + } + var a = "FAIL"; + var o = { + set FAIL(v) { + a = o[f()] = a = v; + } + }; + o[f()] = "PASS"; + console.log(a); + } + expect: { + function f() { + return a; + } + var a = "FAIL"; + var o = { + set FAIL(v) { + a = o[f()] = a = v; + } + }; + o[f()] = "PASS"; + console.log(a); + } + expect_stdout: "PASS" +} + +issue_3830_4: { + options = { + dead_code: true, + } + input: { + function f() { + return o; + } + var a = "FAIL"; + var o = { + set FAIL(v) { + a = f()[a] = a = v; + } + }; + f()[a] = "PASS"; + console.log(a); + } + expect: { + function f() { + return o; + } + var a = "FAIL"; + var o = { + set FAIL(v) { + a = f()[a] = a = v; + } + }; + f()[a] = "PASS"; + console.log(a); + } + expect_stdout: "PASS" +} + +issue_3830_5: { + options = { + dead_code: true, + } + input: { + function f() { + return o; + } + function g() { + return a; + } + var a = "FAIL"; + var o = { + set FAIL(v) { + a = f()[g()] = a = v; + } + }; + f()[g()] = "PASS"; + console.log(a); + } + expect: { + function f() { + return o; + } + function g() { + return a; + } + var a = "FAIL"; + var o = { + set FAIL(v) { + a = f()[g()] = a = v; + } + }; + f()[g()] = "PASS"; + console.log(a); + } + expect_stdout: "PASS" +} + +issue_3830_6: { + options = { + dead_code: true, + } + input: { + function f() { + return o; + } + function g() { + return a; + } + function h(v) { + a = f()[g()] = a = v; + } + var a = "FAIL"; + var o = { + set FAIL(v) { + h(v); + } + }; + o.FAIL = "PASS"; + console.log(a); + } + expect: { + function f() { + return o; + } + function g() { + return a; + } + function h(v) { + a = f()[g()] = a = v; + } + var a = "FAIL"; + var o = { + set FAIL(v) { + h(v); + } + }; + o.FAIL = "PASS"; + console.log(a); + } + expect_stdout: "PASS" +} + +redundant_assignments: { + options = { + dead_code: true, + } + input: { + var a = a = "PASS", b = "FAIL"; + b = b = "PASS"; + console.log(a, b); + } + expect: { + var a = "PASS", b = "FAIL"; + b = "PASS"; + console.log(a, b); + } + expect_stdout: "PASS PASS" +} + +self_assignments_1: { + options = { + dead_code: true, + } + input: { + var a = "PASS"; + a = a; + console.log(a); + } + expect: { + var a = "PASS"; + a; + console.log(a); + } + expect_stdout: "PASS" +} + +self_assignments_2: { + options = { + dead_code: true, + pure_getters: "strict", + reduce_vars: true, + side_effects: true, + toplevel: true, + } + input: { + var a = "q", o = { + p: "PASS", + }; + o.p = o.p; + o[a] = o[a]; + console.log(o.p, o[a]); + } + expect: { + var a = "q", o = { + p: "PASS", + }; + console.log(o.p, o[a]); + } + expect_stdout: "PASS undefined" +} + +self_assignments_3: { + options = { + dead_code: true, + pure_getters: "strict", + reduce_vars: true, + side_effects: true, + toplevel: true, + } + input: { + var a = "q", o = { + p: "FAIL", + get q() { + return "PASS"; + }, + set q(v) { + this.p = v; + }, + }; + o.p = o.p; + o[a] = o[a]; + console.log(o.p, o[a]); + } + expect: { + var a = "q", o = { + p: "FAIL", + get q() { + return "PASS"; + }, + set q(v) { + this.p = v; + }, + }; + o.p = o.p; + o[a] = o[a]; + console.log(o.p, o[a]); + } + expect_stdout: "PASS PASS" +} + +self_assignments_4: { + options = { + dead_code: true, + pure_getters: "strict", + reduce_vars: true, + side_effects: true, + toplevel: true, + } + input: { + var i = 0, l = [ "PASS" ]; + l[0] = l[0]; + l[i] = l[i]; + console.log(l[0], i); + } + expect: { + var i = 0, l = [ "PASS" ]; + console.log(l[0], i); + } + expect_stdout: "PASS 0" +} + +self_assignments_5: { + options = { + dead_code: true, + evaluate: true, + passes: 3, + pure_getters: "strict", + reduce_vars: true, + side_effects: true, + toplevel: true, + } + input: { + var i = 0, l = [ "FAIL", "PASS" ]; + l[0] = l[0]; + l[i] = l[i]; + l[i++] = l[i++]; + console.log(l[0], i); + } + expect: { + var i = 0, l = [ "FAIL", "PASS" ]; + l[0] = l[1]; + console.log(l[0], 2); + } + expect_stdout: "PASS 2" +} + +self_assignments_6: { + options = { + dead_code: true, + pure_getters: "strict", + reduce_vars: true, + side_effects: true, + toplevel: true, + } + input: { + var o = { + p: "PASS", + }; + console.log(o.p = o.p); + } + expect: { + var o = { + p: "PASS", + }; + console.log(o.p); + } + expect_stdout: "PASS" +} + +issue_3967: { + options = { + dead_code: true, + } + input: { + var a = "FAIL"; + try { + a = 0 in (a = "PASS"); + } catch (e) {} + console.log(a); + } + expect: { + var a = "FAIL"; + try { + a = 0 in (a = "PASS"); + } catch (e) {} + console.log(a); + } + expect_stdout: "PASS" +} + +issue_4051: { + options = { + dead_code: true, + } + input: { + try { + delete (A = A); + } catch (e) { + console.log("PASS"); + } + } + expect: { + try { + delete (A = A); + } catch (e) { + console.log("PASS"); + } + } + expect_stdout: "PASS" +} + +issue_4366: { + options = { + dead_code: true, + } + input: { + function f() { + return "PASS"; + ({ + p: 42, + get p() {}, + }); + } + console.log(f()); + } + expect: { + function f() { + return "PASS"; + } + console.log(f()); + } + expect_stdout: "PASS" + node_version: ">=4" +} + +issue_4570: { + options = { + dead_code: true, + inline: true, + } + input: { + var a = function(b) { + return a += b; + }() ? 0 : a; + console.log(a); + } + expect: { + var a = (a += void 0) ? 0 : a; + console.log(a); + } + expect_stdout: "NaN" +} + +issue_5030: { + options = { + dead_code: true, + } + input: { + (function(a, b) { + a = function f() { + if (a) + if (b--) + setImmediate(f); + else + console.log("FAIL"); + else + console.log("PASS"); + }(); + })(42, 1); + } + expect: { + (function(a, b) { + a = function f() { + if (a) + if (b--) + setImmediate(f); + else + console.log("FAIL"); + else + console.log("PASS"); + }(); + })(42, 1); + } + expect_stdout: "PASS" + node_version: ">=0.12" +} + +issue_5106_1: { + options = { + dead_code: true, + } + input: { + console.log(typeof function(a) { + return a = arguments; + }("FAIL")[0]); + } + expect: { + console.log(typeof function(a) { + return a = arguments; + }("FAIL")[0]); + } + expect_stdout: "object" +} + +issue_5106_2: { + options = { + dead_code: true, + } + input: { + "use strict"; + console.log(function(a) { + return a = arguments; + }("PASS")[0]); + } + expect: { + "use strict"; + console.log(function(a) { + return arguments; + }("PASS")[0]); + } + expect_stdout: "PASS" +} + +issue_5506: { + options = { + dead_code: true, + } + input: { + try { + (function(a) { + var b = 1; + (function f() { + try { + b-- && f(); + } catch (c) {} + console.log(a); + a = 42 in (a = "bar"); + })(); + })("foo"); + } catch (e) {} + } + expect: { + try { + (function(a) { + var b = 1; + (function f() { + try { + b-- && f(); + } catch (c) {} + console.log(a); + a = 42 in (a = "bar"); + })(); + })("foo"); + } catch (e) {} + } + expect_stdout: [ + "foo", + "bar", + ] +} + +issue_5641: { + options = { + collapse_vars: true, + conditionals: true, + dead_code: true, + } + input: { + function f(a) { + if (a || b) { + var b = "PASS", c = b && console.log(b); + } else + var d = a || b; + } + f(42); + } + expect: { + function f(a) { + var b, c, d; + (a || b) && (b = "PASS") && console.log(b); + } + f(42); } + expect_stdout: "PASS" } diff --git a/test/compress/debugger.js b/test/compress/debugger.js index 7c2707340e0..4dd47ecdf81 100644 --- a/test/compress/debugger.js +++ b/test/compress/debugger.js @@ -1,7 +1,7 @@ keep_debugger: { options = { - drop_debugger: false - }; + drop_debugger: false, + } input: { debugger; } @@ -12,8 +12,8 @@ keep_debugger: { drop_debugger: { options = { - drop_debugger: true - }; + drop_debugger: true, + } input: { debugger; if (foo) debugger; diff --git a/test/compress/default-values.js b/test/compress/default-values.js new file mode 100644 index 00000000000..4a248d99c95 --- /dev/null +++ b/test/compress/default-values.js @@ -0,0 +1,3109 @@ +arrow_1: { + input: { + console.log(((a = "PASS") => a)()); + } + expect_exact: 'console.log(((a="PASS")=>a)());' + expect_stdout: "PASS" + node_version: ">=6" +} + +arrow_2: { + input: { + console.log((([ a = "FAIL" ]) => a)([ "PASS" ])); + } + expect_exact: 'console.log((([a="FAIL"])=>a)(["PASS"]));' + expect_stdout: "PASS" + node_version: ">=6" +} + +arrow_3: { + input: { + (([ a = console ] = null) => a.log("PASS"))(""); + } + expect_exact: '(([a=console]=null)=>a.log("PASS"))("");' + expect_stdout: "PASS" + node_version: ">=6" +} + +assign: { + input: { + [ a = "PASS" ] = []; + console.log(a); + } + expect_exact: '[a="PASS"]=[];console.log(a);' + expect_stdout: "PASS" + node_version: ">=6" +} + +declaration_var: { + input: { + var [ a = "PASS" ] = [ , ]; + console.log(a); + } + expect_exact: 'var[a="PASS"]=[,];console.log(a);' + expect_stdout: "PASS" + node_version: ">=6" +} + +declaration_const: { + input: { + const [ a = "FAIL" ] = [ "PASS" ]; + console.log(a); + } + expect_exact: 'const[a="FAIL"]=["PASS"];console.log(a);' + expect_stdout: "PASS" + node_version: ">=6" +} + +declaration_let: { + input: { + let [ a = "PASS" ] = [ void 42 ]; + console.log(a); + } + expect_exact: 'let[a="PASS"]=[void 42];console.log(a);' + expect_stdout: "PASS" + node_version: ">=6" +} + +object_shorthand_assign: { + input: { + ({ a = "PASS" } = 42); + console.log(a); + } + expect_exact: '({a="PASS"}=42);console.log(a);' + expect_stdout: "PASS" + node_version: ">=6" +} + +object_shorthand_declaration: { + input: { + var { a = "PASS" } = 42; + console.log(a); + } + expect_exact: 'var{a="PASS"}=42;console.log(a);' + expect_stdout: "PASS" + node_version: ">=6" +} + +object_shorthand_function: { + input: { + (function({ a = "PASS" }) { + console.log(a); + })(42); + } + expect_exact: '(function({a="PASS"}){console.log(a)})(42);' + expect_stdout: "PASS" + node_version: ">=6" +} + +retain_arguments_1: { + options = { + arguments: true, + } + input: { + console.log(function(a = "FAIL") { + return arguments[0]; + }() || "PASS"); + } + expect: { + console.log(function(a = "FAIL") { + return arguments[0]; + }() || "PASS"); + } + expect_stdout: "PASS" + node_version: ">=6" +} + +retain_arguments_2: { + options = { + arguments: true, + } + input: { + console.log(function(a, b = null) { + a = "FAIL"; + return arguments[0]; + }("PASS", 42)); + } + expect: { + console.log(function(a, b = null) { + a = "FAIL"; + return arguments[0]; + }("PASS", 42)); + } + expect_stdout: "PASS" + node_version: ">=6" +} + +process_boolean_returns: { + options = { + booleans: true, + evaluate: true, + reduce_vars: true, + } + input: { + console.log(function(a = console.log("FAIL 1")) { + return a() ? "PASS" : "FAIL 2"; + }(function() { + return 42; + })); + } + expect: { + console.log(function(a = console.log("FAIL 1")) { + return 42 ? "PASS" : "FAIL 2"; + }(function() { + return 1; + })); + } + expect_stdout: "PASS" + node_version: ">=6" +} + +collapse_arg_sequence: { + options = { + collapse_vars: true, + unused: true, + } + input: { + (function(a = (console.log("bar"), console.log)) { + a("foo"); + })(); + } + expect: { + (function(a = console.log("bar")) { + (0, console.log)("foo"); + })(); + } + expect_stdout: [ + "bar", + "foo", + ] + node_version: ">=6" +} + +collapse_in_arg: { + options = { + collapse_vars: true, + keep_fargs: false, + unused: true, + } + input: { + (function(a, b = a) { + b("PASS"); + })(console.log); + } + expect: { + (function(a) { + a("PASS"); + })(console.log); + } + expect_stdout: "PASS" + node_version: ">=6" +} + +collapse_value_1: { + options = { + collapse_vars: true, + keep_fargs: false, + unused: true, + } + input: { + console.log(function(a = "PASS") { + return a; + }()); + } + expect: { + console.log(function() { + return "PASS"; + }()); + } + expect_stdout: "PASS" + node_version: ">=6" +} + +collapse_value_2: { + options = { + collapse_vars: true, + keep_fargs: false, + unused: true, + } + input: { + (function(a = console) { + return a; + })().log("PASS"); + } + expect: { + (function() { + return console; + })().log("PASS"); + } + expect_stdout: "PASS" + node_version: ">=6" +} + +flatten_if: { + options = { + conditionals: true, + } + input: { + if (console.log("PASS")) { + var [ + a = function b() { + for (c in b); + }, + ] = 0; + } + } + expect: { + var a; + console.log("PASS") && ([ + a = function b() { + for (c in b); + }, + ] = 0); + } + expect_stdout: "PASS" + node_version: ">=6" +} + +maintain_if: { + options = { + conditionals: true, + } + input: { + if (a) + for (;;); + else + var [ a = "PASS" ] = []; + console.log(a); + } + expect: { + if (a) + for (;;); + else + var [ a = "PASS" ] = []; + console.log(a); + } + expect_stdout: "PASS" + node_version: ">=6" +} + +reduce_funarg: { + options = { + evaluate: true, + keep_fargs: false, + reduce_vars: true, + unused: true, + } + input: { + console.log(...function(a = "foo", b = "bar", c = "baz") { + return [ a, b, c ]; + }(void 0, null)); + } + expect: { + console.log(...function() { + return [ "foo", null, "baz" ]; + }()); + } + expect_stdout: "foo null baz" + node_version: ">=6" +} + +reduce_array: { + options = { + evaluate: true, + reduce_vars: true, + toplevel: true, + unsafe: true, + unused: true, + } + input: { + var [ a = "foo", b = "bar", c = "baz" ] = [ void 0, null ]; + console.log(a, b, c); + } + expect: { + var [ c = "baz" ] = []; + console.log("foo", null, c); + } + expect_stdout: "foo null baz" + node_version: ">=6" +} + +reduce_object: { + options = { + evaluate: true, + reduce_vars: true, + toplevel: true, + unsafe: true, + unused: true, + } + input: { + var { a = "foo", b = "bar", c = "baz" } = { a: void 0, b: null }; + console.log(a, b, c); + } + expect: { + var { c = "baz" } = {}; + console.log("foo", null, c); + } + expect_stdout: "foo null baz" + node_version: ">=6" +} + +evaluate_iife: { + options = { + evaluate: true, + } + input: { + console.log(function(a = "PASS") { + return a; + }()); + } + expect: { + console.log("PASS"); + } + expect_stdout: "PASS" + node_version: ">=6" +} + +unsafe_evaluate_iife_1: { + options = { + evaluate: true, + unsafe: true, + } + input: { + console.log(function([ a ] = []) { + return "PASS"; + }()); + } + expect: { + console.log(function([ a ] = []) { + return "PASS"; + }()); + } + expect_stdout: "PASS" + node_version: ">=6" +} + +unsafe_evaluate_iife_2: { + options = { + evaluate: true, + reduce_vars: true, + unsafe: true, + } + input: { + console.log(function([ a ] = []) { + return a[0]; + }([ [ "PASS" ] ])); + } + expect: { + console.log(function([ a ] = []) { + return a[0]; + }([ [ "PASS" ] ])); + } + expect_stdout: "PASS" + node_version: ">=6" +} + +inline_direct: { + options = { + default_values: true, + inline: true, + unused: true, + } + input: { + console.log(function(a = "FAIL") { + return a; + }("PASS")); + } + expect: { + console.log("PASS"); + } + expect_stdout: "PASS" + node_version: ">=6" +} + +inline_constant: { + options = { + inline: true, + } + input: { + console.log(function(a = console.log("foo")) { + return "bar"; + }(void console.log("baz"))); + } + expect: { + console.log((void console.log("baz"), console.log("foo"), "bar")); + } + expect_stdout: [ + "baz", + "foo", + "bar", + ] + node_version: ">=6" +} + +inline_destructured: { + options = { + inline: true, + } + input: { + console.log(function([ a ] = []) { + return "PASS"; + }()); + } + expect: { + console.log(([ [] = [] ] = [], "PASS")); + } + expect_stdout: "PASS" + node_version: ">=6" +} + +inline_function: { + options = { + default_values: true, + inline: true, + sequences: true, + side_effects: true, + toplevel: true, + unused: true, + } + input: { + (function(a = console.log("foo"), b = console.log("bar")) { + console.log("baz"); + }(void console.log("moo"), 42)); + } + expect: { + console.log("moo"), + console.log("foo"), + console.log("baz"); + } + expect_stdout: [ + "moo", + "foo", + "baz", + ] + node_version: ">=6" +} + +inline_loop_1: { + options = { + inline: true, + reduce_vars: true, + toplevel: true, + unused: true, + } + input: { + while (function f(a = "PASS") { + console.log(a); + }()); + } + expect: { + while (a = "PASS", void console.log(a)); + var a; + } + expect_stdout: "PASS" + node_version: ">=6" +} + +inline_loop_2: { + options = { + inline: true, + sequences: true, + toplevel: true, + unused: true, + } + input: { + while (function(a = [ "PASS" ]) { + var a = function f(b) { + console.log(a[b]); + }(0); + }()); + } + expect: { + while (a = [ "PASS" ], + b = void 0, + b = 0, + void (a = void console.log(a[b]))); + var a, b; + } + expect_stdout: "PASS" + node_version: ">=6" +} + +inline_side_effects_1: { + options = { + inline: true, + toplevel: true, + } + input: { + var a = 42; + (function(b = --a) {})(console); + console.log(a); + } + expect: { + var a = 42; + [ b = --a ] = [ console ], + void 0; + var b; + console.log(a); + } + expect_stdout: "42" + node_version: ">=6" +} + +inline_side_effects_2: { + options = { + side_effects: true, + } + input: { + var a = 42; + (function(b = --a) {})(console); + console.log(a); + } + expect: { + var a = 42; + [ [][0] = --a ] = [ console ]; + console.log(a); + } + expect_stdout: "42" + node_version: ">=6" +} + +drop_empty_iife: { + options = { + side_effects: true, + } + input: { + console.log(function(a = console.log("foo")) {}(void console.log("baz"))); + } + expect: { + console.log((console.log("baz"), void console.log("foo"))); + } + expect_stdout: [ + "baz", + "foo", + "undefined", + ] + node_version: ">=6" +} + +retain_empty_iife: { + options = { + side_effects: true, + } + input: { + var a; + try { + (function(a = a) {})(); + } catch (e) { + console.log("PASS"); + } + } + expect: { + var a; + try { + (function(a = a) {})(); + } catch (e) { + console.log("PASS"); + } + } + expect_stdout: "PASS" + node_version: ">=6" +} + +drop_new_function: { + options = { + side_effects: true, + } + input: { + new function(a = console.log("PASS")) {}(); + } + expect: { + void console.log("PASS"); + } + expect_stdout: "PASS" + node_version: ">=6" +} + +retain_fargs: { + options = { + unused: true, + } + input: { + (function([ a = console.log("PASS") ]) {})([]); + } + expect: { + (function([ a = console.log("PASS") ]) {})([]); + } + expect_stdout: "PASS" + node_version: ">=6" +} + +drop_fargs: { + options = { + keep_fargs: false, + unused: true, + } + input: { + console.log(function(a = 42, b = console.log("foo"), c = true) { + return "bar"; + }(console.log("baz"), "moo", false)); + } + expect: { + console.log(function(b = console.log("foo")) { + return "bar"; + }((console.log("baz"), "moo"))); + } + expect_stdout: [ + "baz", + "bar", + ] + expect_warnings: [ + "WARN: Dropping unused default argument c [test/compress/default-values.js:1,61]", + "WARN: Side effects in default value of unused variable b [test/compress/default-values.js:1,37]", + "WARN: Dropping unused default argument assignment a [test/compress/default-values.js:1,29]", + ] + node_version: ">=6" +} + +hoist_vars: { + options = { + hoist_vars: true, + join_vars: true, + unused: true, + } + input: { + var a = "PASS"; + var [ b = 42 ] = []; + console.log(a, b); + } + expect: { + var a = "PASS", [ b = 42 ] = []; + console.log(a, b); + } + expect_stdout: "PASS 42" + node_version: ">=6" +} + +unused_var_1: { + options = { + toplevel: true, + unused: true, + } + input: { + var [ a = 42 ] = [ console.log("PASS") ]; + } + expect: { + console.log("PASS"); + } + expect_stdout: "PASS" + node_version: ">=6" +} + +unused_var_2: { + options = { + pure_getters: "strict", + toplevel: true, + unused: true, + } + input: { + var { + p: [ a ] = "" + console.log("FAIL"), + } = { + p: [ console.log("PASS") ], + }; + } + expect: { + console.log("PASS"); + } + expect_stdout: "PASS" + node_version: ">=6" +} + +unused_value_assign_1: { + options = { + default_values: true, + unused: true, + } + input: { + [] = [ console.log("PASS") ]; + } + expect: { + [ console.log("PASS") ]; + } + expect_stdout: "PASS" + node_version: ">=6" +} + +unused_value_assign_2: { + options = { + default_values: true, + unused: true, + } + input: { + [ a = console.log("FAIL") ] = [ "PASS" ]; + console.log(a); + } + expect: { + [ a ] = [ "PASS" ]; + console.log(a); + } + expect_stdout: "PASS" + node_version: ">=6" +} + +unused_value_var_1: { + options = { + default_values: true, + unused: true, + } + input: { + var [] = [ console.log("PASS") ]; + } + expect: { + console.log("PASS"); + } + expect_stdout: "PASS" + node_version: ">=6" +} + +unused_value_var_2: { + options = { + default_values: true, + unused: true, + } + input: { + var [ a = console.log("FAIL") ] = [ "PASS" ]; + console.log(a); + } + expect: { + var a = [ "PASS" ][0]; + console.log(a); + } + expect_stdout: "PASS" + node_version: ">=6" +} + +mangle_var_1: { + mangle = { + toplevel: false, + } + input: { + var N = 1, [ { + pname: p = "x", + i: n = N, + }, { + [p + n]: v, + } ] = [ {}, { + x1: "PASS", + } ]; + console.log(v); + } + expect: { + var N = 1, [ { + pname: p = "x", + i: n = N, + }, { + [p + n]: v, + } ] = [ {}, { + x1: "PASS", + } ]; + console.log(v); + } + expect_stdout: "PASS" + node_version: ">=6" +} + +mangle_var_1_toplevel: { + mangle = { + toplevel: true, + } + input: { + var N = 1, [ { + pname: p = "x", + i: n = N, + }, { + [p + n]: v, + } ] = [ {}, { + x1: "PASS", + } ]; + console.log(v); + } + expect: { + var o = 1, [ { + pname: a = "x", + i: e = o, + }, { + [a + e]: l, + } ] = [ {}, { + x1: "PASS", + } ]; + console.log(l); + } + expect_stdout: "PASS" + node_version: ">=6" +} + +mangle_var_2: { + mangle = { + toplevel: false, + } + input: { + var N = 1, [ { + pname: p = "x", + i: n = N, + } = {}, { + [p + n]: v, + } ] = [ , { + x1: "PASS", + } ]; + console.log(v); + } + expect: { + var N = 1, [ { + pname: p = "x", + i: n = N, + } = {}, { + [p + n]: v, + } ] = [ , { + x1: "PASS", + } ]; + console.log(v); + } + expect_stdout: "PASS" + node_version: ">=6" +} + +mangle_var_2_toplevel: { + mangle = { + toplevel: true, + } + input: { + var N = 1, [ { + pname: p = "x", + i: n = N, + } = {}, { + [p + n]: v, + } ] = [ , { + x1: "PASS", + } ]; + console.log(v); + } + expect: { + var o = 1, [ { + pname: a = "x", + i: e = o, + } = {}, { + [a + e]: l, + } ] = [ , { + x1: "PASS", + } ]; + console.log(l); + } + expect_stdout: "PASS" + node_version: ">=6" +} + +mangle_function_1: { + mangle = { + toplevel: false, + } + input: { + var N = 1; + (function(o, { + pname: p, + } = o, { + [p + N]: v, + } = o) { + let N; + console.log(v); + })({ + pname: "x", + x1: "PASS", + }); + } + expect: { + var N = 1; + (function(n, { + pname: e, + } = n, { + [e + N]: o, + } = n) { + let a; + console.log(o); + })({ + pname: "x", + x1: "PASS", + }); + } + expect_stdout: "PASS" + node_version: ">=6" +} + +mangle_function_1_toplevel: { + mangle = { + toplevel: true, + } + input: { + var N = 1; + (function(o, { + pname: p, + } = o, { + [p + N]: v, + } = o) { + let N; + console.log(v); + })({ + pname: "x", + x1: "PASS", + }); + } + expect: { + var l = 1; + (function(n, { + pname: e, + } = n, { + [e + l]: o, + } = n) { + let a; + console.log(o); + })({ + pname: "x", + x1: "PASS", + }); + } + expect_stdout: "PASS" + node_version: ">=6" +} + +mangle_function_2: { + mangle = { + toplevel: false, + } + input: { + var N = 1; + (function({ + pname: p = "x", + i: n = N, + }, { + [p + n]: v, + }) { + let N; + console.log(v); + })({}, { + x1: "PASS", + }); + } + expect: { + var N = 1; + (function({ + pname: n = "x", + i: o = N, + }, { + [n + o]: e, + }) { + let l; + console.log(e); + })({}, { + x1: "PASS", + }); + } + expect_stdout: "PASS" + node_version: ">=6" +} + +mangle_function_2_toplevel: { + mangle = { + toplevel: true, + } + input: { + var N = 1; + (function({ + pname: p = "x", + i: n = N, + }, { + [p + n]: v, + }) { + let N; + console.log(v); + })({}, { + x1: "PASS", + }); + } + expect: { + var a = 1; + (function({ + pname: n = "x", + i: o = a, + }, { + [n + o]: e, + }) { + let l; + console.log(e); + })({}, { + x1: "PASS", + }); + } + expect_stdout: "PASS" + node_version: ">=6" +} + +mangle_arrow_1: { + mangle = { + toplevel: false, + } + input: { + var N = 1; + ((o, { + pname: p, + } = o, { + [p + N]: v, + } = o) => { + let N; + console.log(v); + })({ + pname: "x", + x1: "PASS", + }); + } + expect: { + var N = 1; + ((e, { + pname: a, + } = e, { + [a + N]: l, + } = e) => { + let n; + console.log(l); + })({ + pname: "x", + x1: "PASS", + }); + } + expect_stdout: "PASS" + node_version: ">=6.9.3" +} + +mangle_arrow_1_toplevel: { + mangle = { + toplevel: true, + } + input: { + var N = 1; + ((o, { + pname: p, + } = o, { + [p + N]: v, + } = o) => { + let N; + console.log(v); + })({ + pname: "x", + x1: "PASS", + }); + } + expect: { + var o = 1; + ((e, { + pname: a, + } = e, { + [a + o]: l, + } = e) => { + let n; + console.log(l); + })({ + pname: "x", + x1: "PASS", + }); + } + expect_stdout: "PASS" + node_version: ">=6.9.3" +} + +mangle_arrow_2: { + mangle = { + toplevel: false, + } + input: { + var N = 1; + (({ + pname: p = "x", + i: n = N, + }, { + [p + n]: v, + }) => { + let N; + console.log(v); + })({}, { + x1: "PASS", + }); + } + expect: { + var N = 1; + (({ + pname: e = "x", + i: l = N, + }, { + [e + l]: o, + }) => { + let a; + console.log(o); + })({}, { + x1: "PASS", + }); + } + expect_stdout: "PASS" + node_version: ">=6.9.3" +} + +mangle_arrow_2_toplevel: { + mangle = { + toplevel: true, + } + input: { + var N = 1; + (({ + pname: p = "x", + i: n = N, + }, { + [p + n]: v, + }) => { + let N; + console.log(v); + })({}, { + x1: "PASS", + }); + } + expect: { + var n = 1; + (({ + pname: e = "x", + i: l = n, + }, { + [e + l]: o, + }) => { + let a; + console.log(o); + })({}, { + x1: "PASS", + }); + } + expect_stdout: "PASS" + node_version: ">=6.9.3" +} + +collapse_preceding_simple_arg: { + options = { + collapse_vars: true, + unused: true, + } + input: { + var a = "foo"; + console.log(function(b, c = "bar") { + return b + c; + }(a, a)); + } + expect: { + var a = "foo"; + console.log(function(b, c = "bar") { + return a + c; + }(0, a)); + } + expect_stdout: "foofoo" + node_version: ">=6" +} + +drop_preceding_simple_arg: { + options = { + collapse_vars: true, + keep_fargs: false, + unused: true, + } + input: { + var a = "foo"; + console.log(function(b, c = "bar") { + return b + c; + }(a, a)); + } + expect: { + var a = "foo"; + console.log(function(c = "bar") { + return a + c; + }(a)); + } + expect_stdout: "foofoo" + node_version: ">=6" +} + +issue_4444: { + options = { + collapse_vars: true, + } + input: { + var a = "PASS"; + console.log(function(b) { + b = a; + (function(c = b.p) {})(); + return a; + }()); + } + expect: { + var a = "PASS"; + console.log(function(b) { + b = a; + (function(c = b.p) {})(); + return a; + }()); + } + expect_stdout: "PASS" + node_version: ">=6" +} + +issue_4446_1: { + options = { + collapse_vars: true, + } + input: { + a = 42; + [ b = 42 ] = [ "PASS" ]; + c = 42; + console.log(b, a); + } + expect: { + [ b = 42 ] = [ "PASS" ]; + c = a = 42; + console.log(b, a); + } + expect_stdout: "PASS 42" + node_version: ">=6" +} + +issue_4446_2: { + options = { + collapse_vars: true, + } + input: { + a = 42; + var [ b = 42 ] = [ "PASS" ]; + c = 42; + console.log(b, a); + } + expect: { + var [ b = 42 ] = [ "PASS" ]; + c = a = 42; + console.log(b, a); + } + expect_stdout: "PASS 42" + node_version: ">=6" +} + +issue_4458: { + options = { + evaluate: true, + reduce_vars: true, + toplevel: true, + unused: true, + } + input: { + var a = "PASS"; + function f(b = a = "FAIL") { + console.log(a, b); + } + f(42); + } + expect: { + var a = "PASS"; + (function(b = a = "FAIL") { + console.log(a, b); + })(42); + } + expect_stdout: "PASS 42" + node_version: ">=6" +} + +issue_4460: { + options = { + collapse_vars: true, + } + input: { + var log = console.log, a = "FAIL"; + var [ b = a ] = (a = "PASS", []); + log(a, b); + } + expect: { + var log = console.log, a = "FAIL"; + var [ b = a ] = (a = "PASS", []); + log(a, b); + } + expect_stdout: "PASS PASS" + node_version: ">=6" +} + +issue_4461_1: { + options = { + collapse_vars: true, + unused: true, + } + input: { + var a = 0; + (function(b = a && console.log("PASS"), c) { + return c; + })(void 0, a++); + } + expect: { + var a = 0; + (function(b = a && console.log("PASS"), c) { + return c; + })(void 0, a++); + } + expect_stdout: "PASS" + node_version: ">=6" +} + +issue_4461_2: { + options = { + collapse_vars: true, + unused: true, + } + input: { + var a = 0; + (function([ b = a && console.log("PASS") ], c) { + return c; + })([], a++); + } + expect: { + var a = 0; + (function([ b = a && console.log("PASS") ], c) { + return c; + })([], a++); + } + expect_stdout: "PASS" + node_version: ">=6" +} + +issue_4468: { + options = { + evaluate: true, + keep_fargs: false, + pure_getters: "strict", + reduce_vars: true, + side_effects: true, + unused: true, + } + input: { + (function(a) { + var { + [console.log("PASS")]: b = a && (a.p = 0), + } = 0; + a; + })(1234); + } + expect: { + (function() { + var { + [console.log("PASS")]: b, + } = 0; + })(); + } + expect_stdout: "PASS" + node_version: ">=6" +} + +issue_4483: { + options = { + conditionals: true, + evaluate: true, + reduce_vars: true, + toplevel: true, + } + input: { + if (console) + var [ a = "FAIL" ] = [], b = a = "PASS"; + console.log(b); + } + expect: { + var a, b; + console && ([ a = "FAIL" ] = [], b = "PASS"); + console.log(b); + } + expect_stdout: "PASS" + node_version: ">=6" +} + +issue_4485_1: { + options = { + pure_getters: "strict", + side_effects: true, + } + input: { + (function(a = null) { + var arguments; + try { + arguments.length; + } catch (e) { + console.log("PASS"); + } + })(); + } + expect: { + (function(a = null) { + var arguments; + try { + arguments.length; + } catch (e) { + console.log("PASS"); + } + })(); + } + expect_stdout: true + node_version: ">=6" +} + +issue_4485_2: { + options = { + pure_getters: "strict", + side_effects: true, + } + input: { + (function(a = null) { + var arguments = null; + try { + arguments.length; + } catch (e) { + console.log("PASS"); + } + })(); + } + expect: { + (function(a = null) { + var arguments = null; + try { + arguments.length; + } catch (e) { + console.log("PASS"); + } + })(); + } + expect_stdout: "PASS" + node_version: ">=6" +} + +issue_4485_3: { + options = { + unused: true, + } + input: { + (function(a = null) { + var arguments; + try { + arguments.length; + } catch (e) { + console.log("PASS"); + } + })(); + } + expect: { + (function(a = null) { + var arguments; + try { + arguments.length; + } catch (e) { + console.log("PASS"); + } + })(); + } + expect_stdout: true + node_version: ">=6" +} + +issue_4496: { + options = { + default_values: true, + unused: true, + } + input: { + (function f(a = 0) { + console.log(function(b) { + a && b(); + return a; + }(f)); + })(42); + } + expect: { + (function f(a = 0) { + console.log(function(b) { + a && b(); + return a; + }(f)); + })(42); + } + expect_stdout: [ + "0", + "42", + ] + node_version: ">=6" +} + +issue_4502_1: { + options = { + inline: true, + unused: true, + } + input: { + (function() { + var a = "PASS"; + (function(b = a++) { + var a; + })(void 0, console.log(a)); + })(); + } + expect: { + (function() { + var a = "PASS"; + void 0, + console.log(a), + a++, + void 0; + })(); + } + expect_stdout: "PASS" + node_version: ">=6" +} + +issue_4502_2: { + options = { + inline: true, + unused: true, + } + input: { + (function() { + var a = "PASS"; + (function(b = a++) {})(void 0, console.log(a)); + })(); + } + expect: { + (function() { + var a = "PASS"; + void 0, + console.log(a), + a++, + void 0; + })(); + } + expect_stdout: "PASS" + node_version: ">=6" +} + +issue_4502_3: { + options = { + side_effects: true, + } + input: { + (function() { + var a = "PASS"; + (function(b = a++) {})(void 0, console.log(a)); + })(); + } + expect: { + (function() { + var a = "PASS"; + console.log(a), + a++; + })(); + } + expect_stdout: "PASS" + node_version: ">=6" +} + +issue_4502_4: { + options = { + side_effects: true, + } + input: { + (function(a, b = console.log("FAIL")) {})(..."" + console.log(42)); + } + expect: { + [ , [][0] = console.log("FAIL") ] = [ ..."" + console.log(42) ]; + } + expect_stdout: "42" + node_version: ">=6" +} + +issue_4510_1: { + options = { + default_values: true, + unused: true, + } + input: { + var a = []; + var [ , b = console.log("PASS") ] = [ ...a, null ]; + } + expect: { + var a = []; + var [ , b = console.log("PASS") ] = [ ...a, null ]; + } + expect_stdout: "PASS" + node_version: ">=6" +} + +issue_4510_2: { + options = { + default_values: true, + unused: true, + } + input: { + var o = { + p: void 0, + }; + var { + p: a = console.log("PASS"), + } = { + p: null, + ...o, + }; + } + expect: { + var o = { + p: void 0, + }; + var { + p: a = console.log("PASS"), + } = { + p: null, + ...o, + }; + } + expect_stdout: "PASS" + node_version: ">=8.3.0" +} + +issue_4523: { + options = { + merge_vars: true, + } + input: { + console.log(function() { + var a, b; + [ a = b = false ] = [ "FAIL" ]; + return b || "PASS"; + }()); + } + expect: { + console.log(function() { + var a, b; + [ a = b = false ] = [ "FAIL" ]; + return b || "PASS"; + }()); + } + expect_stdout: "PASS" + node_version: ">=6" +} + +issue_4540: { + options = { + reduce_vars: true, + side_effects: true, + } + input: { + console.log(function() { + function f([ a = 0 ]) {} + f([]); + }()); + } + expect: { + console.log(function() { + function f([ a = 0 ]) {} + f([]); + }()); + } + expect_stdout: "undefined" + node_version: ">=6" +} + +issue_4548_1: { + options = { + merge_vars: true, + toplevel: true, + } + input: { + A = "foo"; + var a = A; + [ b = c = "bar" ] = [ console, console.log(a) ]; + console.log(c); + var c; + } + expect: { + A = "foo"; + var a = A; + [ b = c = "bar" ] = [ console, console.log(a) ]; + console.log(c); + var c; + } + expect_stdout: [ + "foo", + "undefined", + ] + node_version: ">=6" +} + +issue_4548_2: { + options = { + merge_vars: true, + toplevel: true, + } + input: { + A = "foo"; + var a = A; + var [ b = c = "bar" ] = [ console, console.log(a) ]; + console.log(c); + var c; + } + expect: { + A = "foo"; + var a = A; + var [ b = c = "bar" ] = [ console, console.log(a) ]; + console.log(c); + var c; + } + expect_stdout: [ + "foo", + "undefined", + ] + node_version: ">=6" +} + +issue_4588_1_unused: { + options = { + unused: true, + } + input: { + console.log(function(a = 42) {}.length); + } + expect: { + console.log(function(a = 0) {}.length); + } + expect_stdout: "0" + node_version: ">=6" +} + +issue_4588_2_unused: { + options = { + unused: true, + } + input: { + console.log(function(a, b = void 0, c, d = "foo") {}.length); + } + expect: { + console.log(function(a, b = 0, c, d) {}.length); + } + expect_stdout: "1" + expect_warnings: [ + "WARN: Dropping unused default argument assignment d [test/compress/default-values.js:1,47]", + "WARN: Dropping unused default argument value b [test/compress/default-values.js:1,32]", + ] + node_version: ">=6" +} + +issue_4588_1_evaluate: { + options = { + evaluate: true, + unsafe: true, + } + input: { + console.log(function(a = 42) {}.length); + } + expect: { + console.log(0); + } + expect_stdout: "0" + node_version: ">=6" +} + +issue_4588_2_evaluate: { + options = { + evaluate: true, + unsafe: true, + } + input: { + console.log(function(a, b = void 0, c, d = "foo") {}.length); + } + expect: { + console.log(1); + } + expect_stdout: "1" + node_version: ">=6" +} + +issue_4817: { + options = { + ie: true, + inline: true, + unused: true, + } + input: { + (function f(a = console.log(typeof f)) { + return 42; + })(); + } + expect: { + (function f(a = console.log(typeof f)) { + return 42; + })(); + } + expect_stdout: "function" + node_version: ">=6" +} + +issue_4854: { + options = { + collapse_vars: true, + inline: true, + side_effects: true, + unused: true, + } + input: { + console.log(function(a) { + (function(b = a = "foo") { + [] = "foo"; + })(); + a; + }()); + } + expect: { + console.log(void 0); + } + expect_stdout: "undefined" + node_version: ">=6" +} + +issue_4916: { + options = { + collapse_vars: true, + pure_getters: "strict", + reduce_vars: true, + } + input: { + var log = console.log; + (function(b = "foo") { + b.value = "FAIL"; + b; + log(b.value); + })(); + } + expect: { + var log = console.log; + (function(b = "foo") { + b.value = "FAIL"; + b; + log(b.value); + })(); + } + expect_stdout: "undefined" + node_version: ">=6" +} + +issue_4994: { + options = { + loops: true, + unused: true, + } + input: { + var a = "FAIL"; + (function(b = function() { + for (a in { PASS: 42 }); + }()) { + var a; + })(); + console.log(a); + } + expect: { + var a = "FAIL"; + (function(b = function() { + for (a in { PASS: 42 }); + }()) {})(); + console.log(a); + } + expect_stdout: "PASS" + node_version: ">=6" +} + +issue_5057_1: { + options = { + collapse_vars: true, + inline: true, + sequences: true, + unused: true, + } + input: { + var a = 42; + (function() { + var b = function(c = (console.log("foo"), b = a)) { + a && console.log("bar"); + }(); + })(); + } + expect: { + var a = 42; + console.log("foo"), + void (a && console.log("bar")); + } + expect_stdout: [ + "foo", + "bar", + ] + node_version: ">=6" +} + +issue_5057_2: { + options = { + inline: true, + unused: true, + } + input: { + (function f(a) { + (function(b = console.log("FAIL")) {})(a); + })(42); + console.log(typeof b); + } + expect: { + (function(a) { + [ b = console.log("FAIL") ] = [ a ], + void 0; + var b; + })(42); + console.log(typeof b); + } + expect_stdout: "undefined" + node_version: ">=6" +} + +issue_5057_3: { + options = { + inline: 3, + unused: true, + } + input: { + (function(a) { + (function f(b) { + (function(a = console.log("FAIL 1")) {})(b); + console.log(a); + })("FAIL 2"); + })("PASS"); + } + expect: { + (function(a) { + (function(b) { + (function(a = console.log("FAIL 1")) {})(b); + console.log(a); + })("FAIL 2"); + })("PASS"); + } + expect_stdout: "PASS" + node_version: ">=6" +} + +issue_5057_4: { + options = { + if_return: true, + inline: true, + unused: true, + } + input: { + (function(a) { + (function f(b) { + (function(a = console.log("FAIL 1")) {})(b); + console.log(a); + })("FAIL 2"); + })("PASS"); + } + expect: { + (function(a) { + var b = "FAIL 2"; + (function(a = console.log("FAIL 1")) {})(b); + console.log(a); + })("PASS"); + } + expect_stdout: "PASS" + node_version: ">=6" +} + +issue_5065: { + options = { + pure_getters: "strict", + toplevel: true, + unused: true, + } + input: { + var [ a = console.log("PASS") ] = [ (A = 42).p ]; + } + expect: { + var [ a = console.log("PASS") ] = [ (A = 42).p ]; + } + expect_stdout: "PASS" + node_version: ">=6" +} + +issue_5138_1: { + options = { + evaluate: true, + } + input: { + console.log(function(a, b = a = "FAIL") { + return a; + }() && "PASS"); + } + expect: { + console.log(function(a, b = a = "FAIL") { + return a; + }() && "PASS"); + } + expect_stdout: "PASS" + node_version: ">=6" +} + +issue_5138_2: { + options = { + evaluate: true, + } + input: { + console.log(function(a, b = a = "FAIL 1") { + return a; + }(null, "FAIL 2") || "PASS"); + } + expect: { + console.log((null, "PASS")); + } + expect_stdout: "PASS" + node_version: ">=6" +} + +issue_5192: { + options = { + dead_code: true, + ie: true, + } + input: { + (function a(a, [] = a = "PASS") { + console.log(a); + })("FAIL"); + } + expect: { + (function a(a, [] = a = "PASS") { + console.log(a); + })("FAIL"); + } + expect_stdout: "PASS" + node_version: ">=6" +} + +issue_5246_1: { + options = { + keep_fargs: false, + pure_getters: true, + unused: true, + } + input: { + console.log(function({} = 42) { + return "PASS"; + }("foo")); + } + expect: { + console.log(function() { + return "PASS"; + }()); + } + expect_stdout: "PASS" + expect_warnings: [ + "INFO: Dropping unused default argument {} [test/compress/default-values.js:1,29]", + ] + node_version: ">=6" +} + +issue_5246_2: { + options = { + keep_fargs: false, + unused: true, + } + input: { + (function f(a = "FAIL", [] = 42) { + console.log(a); + })("PASS", []); + } + expect: { + (function(a = "FAIL") { + console.log(a); + })("PASS"); + } + expect_stdout: "PASS" + node_version: ">=6" +} + +issue_5246_3: { + options = { + default_values: true, + keep_fargs: false, + unused: true, + } + input: { + console.log(function f([ , {} ] = null){}([ , {} ])); + } + expect: { + console.log(function([ {} ]){}([ {} ])); + } + expect_stdout: "undefined" + node_version: ">=6" +} + +issue_5256: { + options = { + inline: true, + } + input: { + (function(arguments = console.log) { + console; + })(); + console.log(typeof arguments); + } + expect: { + // Syntax error on Node.js v6 + (function(arguments = console.log) { + console; + })(); + console.log(typeof arguments); + } + expect_stdout: "undefined" + node_version: ">=8" +} + +issue_5314_1: { + options = { + side_effects: true, + } + input: { + A = this; + new function() { + (function(a = console.log(this === A ? "PASS" : "FAIL")) {})(); + }(); + } + expect: { + A = this; + (function() { + (function(a = console.log(this === A ? "PASS" : "FAIL")) {})(); + })(); + } + expect_stdout: "PASS" + node_version: ">=6" +} + +issue_5314_2: { + options = { + side_effects: true, + } + input: { + A = this; + new function() { + ((a = console.log(this === A ? "FAIL" : "PASS")) => {})(); + }(); + } + expect: { + A = this; + new function() { + console.log(this === A ? "FAIL" : "PASS"); + }(); + } + expect_stdout: "PASS" + node_version: ">=6" +} + +issue_5336: { + options = { + default_values: true, + unused: true, + } + input: { + var a; + do { + (function f(b = console.log("PASS")) { + a = f; + })(42); + } while (a()); + } + expect: { + var a; + do { + (function f(b = console.log("PASS")) { + a = f; + })(42); + } while (a()); + } + expect_stdout: "PASS" + node_version: ">=6" +} + +issue_5340_1: { + options = { + keep_fargs: true, + pure_getters: "strict", + unused: true, + } + input: { + var a; + (function(b = 42) {})(({ p: a } = true).q); + console.log(a); + } + expect: { + var a; + (function(b = 0) {})(({ p: a } = true).q); + console.log(a); + } + expect_stdout: "undefined" + node_version: ">=6" +} + +issue_5340_2: { + options = { + keep_fargs: true, + pure_getters: "strict", + side_effects: true, + unused: true, + } + input: { + var a; + (function(b = 42) {})(({ p: a } = true).q); + console.log(a); + } + expect: { + var a; + [ [][0] = 0 ] = [ ({ p: a } = true).q ]; + console.log(a); + } + expect_stdout: "undefined" + node_version: ">=6" +} + +issue_5340_3: { + options = { + keep_fargs: false, + pure_getters: "strict", + unused: true, + } + input: { + var a; + (function(b = 42) {})(({ p: a } = true).q); + console.log(a); + } + expect: { + var a; + (function() {})(a = true["p"]); + console.log(a); + } + expect_stdout: "undefined" + node_version: ">=6" +} + +issue_5407: { + options = { + evaluate: true, + reduce_vars: true, + } + input: { + (function(a) { + for (var i = 0; i < 2; i++) + (function(b = 4) { + console.log(b); + a = 2; + })(a); + })(); + } + expect: { + (function(a) { + for (var i = 0; i < 2; i++) + (function(b = 4) { + console.log(b); + a = 2; + })(a); + })(); + } + expect_stdout: [ + "4", + "2", + ] + node_version: ">=6" +} + +issue_5444_1: { + options = { + merge_vars: true, + toplevel: true, + } + input: { + var a = 42; + var b = function({} = setImmediate(function() { + console.log(a++); + })) { + return this; + }(); + console.log(typeof b); + } + expect: { + var a = 42; + var b = function({} = setImmediate(function() { + console.log(a++); + })) { + return this; + }(); + console.log(typeof b); + } + expect_stdout: [ + "object", + "42", + ] + node_version: ">=6" +} + +issue_5444_2: { + options = { + merge_vars: true, + } + input: { + function f(a, b = a++) { + return b; + } + console.log(f("FAIL") || "PASS"); + } + expect: { + function f(a, b = a++) { + return b; + } + console.log(f("FAIL") || "PASS"); + } + expect_stdout: "PASS" + node_version: ">=6" +} + +issue_5444_3: { + options = { + merge_vars: true, + } + input: { + function f(a, b = function(c = a *= this) { + return c; + }()) { + return b; + } + console.log(f("FAIL") || "PASS"); + } + expect: { + function f(a, b = function(c = a *= this) { + return c; + }()) { + return b; + } + console.log(f("FAIL") || "PASS"); + } + expect_stdout: "PASS" + node_version: ">=6" +} + +issue_5448_1: { + options = { + reduce_vars: true, + unused: true, + } + input: { + (function(a = typeof console.log) { + do { + var b = [ ...a ]; + } while (console.log("PASS")); + })(); + } + expect: { + (function(a = console.log) { + do {} while (console.log("PASS")); + })(); + } + expect_stdout: "PASS" + node_version: ">=6" +} + +issue_5448_2: { + options = { + keep_fargs: true, + reduce_vars: true, + unused: true, + } + input: { + (function(a = typeof console) { + do { + var b = [ ...a ]; + } while (console.log("PASS")); + })(); + } + expect: { + (function(a = 0) { + do {} while (console.log("PASS")); + })(); + } + expect_stdout: "PASS" + node_version: ">=6" +} + +issue_5448_3: { + options = { + evaluate: true, + reduce_vars: true, + toplevel: true, + unsafe: true, + unused: true, + } + input: { + var [ a = typeof console ] = [ void console.log("PASS") ]; + var b = [ ...a ]; + } + expect: { + console.log("PASS"); + } + expect_stdout: "PASS" + node_version: ">=6" +} + +issue_5448_4: { + options = { + evaluate: true, + pure_getters: "strict", + reduce_vars: true, + toplevel: true, + unsafe: true, + unused: true, + } + input: { + var { p: a = typeof console } = { p: void console.log("PASS") }; + var b = [ ...a ]; + } + expect: { + console.log("PASS"); + } + expect_stdout: "PASS" + node_version: ">=6" +} + +issue_5463: { + options = { + collapse_vars: true, + conditionals: true, + evaluate: true, + reduce_vars: true, + toplevel: true, + unsafe: true, + } + input: { + if (console.log("PASS")) + var a = void 0, + b = void 0, + b = ([ a = FAIL ] = b && b); + } + expect: { + var a, b, b; + console.log("PASS") && ( + b = a = void 0, + b = [a = FAIL] = a + ); + } + expect_stdout: "PASS" + node_version: ">=6" +} + +issue_5465: { + options = { + inline: true, + merge_vars: true, + reduce_vars: true, + toplevel: true, + unused: true, + } + input: { + function f(a, b) { + (function(c = b = "FAIL 2") { + this && console.log(b || "PASS"); + })(42 - a && a); + } + f("FAIL 1"); + } + expect: { + a = "FAIL 1", + void function(c = b = "FAIL 2") { + this && console.log(b || "PASS"); + }(42 - a && a); + var a, b; + } + expect_stdout: "PASS" + node_version: ">=6" +} + +issue_5485: { + options = { + comparisons: true, + } + input: { + (function f(f, a = console.log(void 0 === f ? "PASS" : "FAIL")) {})(); + } + expect: { + (function f(f, a = console.log(void 0 === f ? "PASS" : "FAIL")) {})(); + } + expect_stdout: "PASS" + node_version: ">=6" +} + +issue_5533_1_keep_fargs: { + options = { + evaluate: true, + inline: true, + join_vars: true, + keep_fargs: true, + loops: true, + side_effects: true, + unused: true, + } + input: { + "use strict"; + try { + (function() { + var a; + for (; 1;) + a = function() { + (function f(b = 42) { + b; + throw "PASS"; + })(); + }(); + })(); + } catch (e) { + console.log(e); + } + } + expect: { + "use strict"; + try { + (function() { + for (;;) + throw "PASS"; + })(); + } catch (e) { + console.log(e); + } + } + expect_stdout: "PASS" + node_version: ">=6" +} + +issue_5533_1_drop_fargs: { + options = { + evaluate: true, + inline: true, + join_vars: true, + keep_fargs: false, + loops: true, + side_effects: true, + unused: true, + } + input: { + "use strict"; + try { + (function() { + var a; + for (; 1;) + a = function() { + (function f(b = 42) { + b; + throw "PASS"; + })(); + }(); + })(); + } catch (e) { + console.log(e); + } + } + expect: { + "use strict"; + try { + (function() { + for (;;) + throw "PASS"; + })(); + } catch (e) { + console.log(e); + } + } + expect_stdout: "PASS" + node_version: ">=6" +} + +issue_5533_2_keep_fargs: { + options = { + evaluate: true, + inline: true, + join_vars: true, + keep_fargs: true, + loops: true, + side_effects: true, + unused: true, + } + input: { + "use strict"; + try { + (function() { + var a; + for (; 1;) + a = function() { + (function f([ b ] = []) { + b; + throw "PASS"; + })(); + }(); + })(); + } catch (e) { + console.log(e); + } + } + expect: { + "use strict"; + try { + (function() { + for (;;) { + var [ [] = [] ] = []; + throw "PASS"; + } + })(); + } catch (e) { + console.log(e); + } + } + expect_stdout: "PASS" + node_version: ">=6" +} + +issue_5533_2_drop_fargs: { + options = { + evaluate: true, + inline: true, + join_vars: true, + keep_fargs: false, + loops: true, + side_effects: true, + unused: true, + } + input: { + "use strict"; + try { + (function() { + var a; + for (; 1;) + a = function() { + (function f([ b ] = []) { + b; + throw "PASS"; + })(); + }(); + })(); + } catch (e) { + console.log(e); + } + } + expect: { + "use strict"; + try { + (function() { + for (;;) { + var [ [ , ] = [] ] = []; + throw "PASS"; + } + })(); + } catch (e) { + console.log(e); + } + } + expect_stdout: "PASS" + node_version: ">=6" +} + +issue_5533_3_keep_fargs: { + options = { + evaluate: true, + inline: true, + join_vars: true, + keep_fargs: true, + loops: true, + side_effects: true, + unused: true, + } + input: { + "use strict"; + try { + (function() { + var a; + for (; 1;) + a = function() { + (function f(b = 42, c = null) { + c; + throw "PASS"; + })(); + }(); + })(); + } catch (e) { + console.log(e); + } + } + expect: { + "use strict"; + try { + (function() { + for (;;) + throw "PASS"; + })(); + } catch (e) { + console.log(e); + } + } + expect_stdout: "PASS" + node_version: ">=6" +} + +issue_5533_3_drop_fargs: { + options = { + evaluate: true, + inline: true, + join_vars: true, + keep_fargs: false, + loops: true, + side_effects: true, + unused: true, + } + input: { + "use strict"; + try { + (function() { + var a; + for (; 1;) + a = function() { + (function f(b = 42, c = null) { + c; + throw "PASS"; + })(); + }(); + })(); + } catch (e) { + console.log(e); + } + } + expect: { + "use strict"; + try { + (function() { + for (;;) + throw "PASS"; + })(); + } catch (e) { + console.log(e); + } + } + expect_stdout: "PASS" + node_version: ">=6" +} + +issue_5533_4_keep_fargs: { + options = { + evaluate: true, + inline: true, + join_vars: true, + keep_fargs: true, + loops: true, + side_effects: true, + unused: true, + } + input: { + "use strict"; + try { + (function() { + var a; + for (; 1;) + a = function() { + (function f(b = 42, [ c ] = []) { + c; + throw "PASS"; + })(); + }(); + })(); + } catch (e) { + console.log(e); + } + } + expect: { + "use strict"; + try { + (function() { + for (;;) { + var [ [] = [] ] = []; + throw "PASS"; + } + })(); + } catch (e) { + console.log(e); + } + } + expect_stdout: "PASS" + node_version: ">=6" +} + +issue_5533_4_drop_fargs: { + options = { + evaluate: true, + inline: true, + join_vars: true, + keep_fargs: false, + loops: true, + side_effects: true, + unused: true, + } + input: { + "use strict"; + try { + (function() { + var a; + for (; 1;) + a = function() { + (function f(b = 42, [ c ] = []) { + c; + throw "PASS"; + })(); + }(); + })(); + } catch (e) { + console.log(e); + } + } + expect: { + "use strict"; + try { + (function() { + for (;;) { + var [ [] = [] ] = []; + throw "PASS"; + } + })(); + } catch (e) { + console.log(e); + } + } + expect_stdout: "PASS" + node_version: ">=6" +} + +issue_5536: { + options = { + inline: true, + keep_fargs: true, + unused: true, + } + input: { + (function*() { + (([], a = 42) => {})([]); + console.log(typeof a); + })().next(); + } + expect: { + (function*() { + [ , [][0] = 0 ] = [ [] ], + void 0; + console.log(typeof a); + })().next(); + } + expect_stdout: "undefined" + node_version: ">=6" +} + +issue_5566_1: { + options = { + unused: true, + } + input: { + (function(a, f = function() { + return a; + }) { + var a = "foo"; + console.log(a, f()); + })("bar"); + } + expect: { + (function(a, f = function() { + return a; + }) { + var a = "foo"; + console.log(a, f()); + })("bar"); + } + expect_stdout: "foo bar" + node_version: ">=6" +} + +issue_5566_2: { + options = { + inline: true, + reduce_vars: true, + } + input: { + (function(a, f = function() { + return a; + }) { + function a() {} + console.log(typeof a, typeof f()); + })(42); + } + expect: { + (function(a, f = function() { + return a; + }) { + function a() {} + console.log(typeof a, typeof f()); + })(42); + } + expect_stdout: "function number" + node_version: ">=6" +} + +issue_5566_3: { + options = { + reduce_vars: true, + unused: true, + } + input: { + (function(a, f = function() { + return a; + }) { + function a() {} + console.log(typeof a, typeof f()); + })(42); + } + expect: { + (function(a, f = function() { + return a; + }) { + function a() {} + console.log(typeof a, typeof f()); + })(42); + } + expect_stdout: "function number" + node_version: ">=6" +} + +issue_5566_4: { + options = { + collapse_vars: true, + unused: true, + } + input: { + (function(a, b = function() { + return a; + }) { + var a = 0; + b()("PASS"); + })(console.log); + } + expect: { + (function(a, b = function() { + return a; + }) { + var a = 0; + b()("PASS"); + })(console.log); + } + expect_stdout: "PASS" + node_version: ">=6" +} + +issue_5566_5: { + options = { + hoist_vars: true, + } + input: { + (function(a, f = function() { + return a; + }) { + var a = "foo"; + var b; + console.log(a, f()); + })("bar"); + } + expect: { + (function(a, f = function() { + return a; + }) { + var a, b; + a = "foo"; + console.log(a, f()); + })("bar"); + } + expect_stdout: "foo bar" + node_version: ">=6" +} + +issue_5651: { + options = { + ie: true, + unused: true, + } + input: { + console.log(function arguments(a = "FAIL") { + try {} catch (arguments) { + var arguments; + } + return arguments[0]; + }()); + } + expect: { + console.log(function arguments(a = "FAIL") { + try {} catch (arguments) { + var arguments; + } + return arguments[0]; + }()); + } + expect_stdout: true + node_version: ">=6" +} + +issue_5774: { + options = { + collapse_vars: true, + conditionals: true, + evaluate: true, + join_vars: true, + reduce_vars: true, + sequences: true, + unsafe: true, + } + input: { + (function() { + while (console.log("PASS")) { + if (console) { + a = void 0; + var b = void 0; + var c = void 0; + ([ a = 0 ] = [ b, b ]); + var a; + } + } + })(); + } + expect: { + (function() { + while (console.log("PASS")) { + var a, b, c, a; + console && ( + c = b = a = void 0, + [ a = 0 ] = [ a, a ] + ); + } + })(); + } + expect_stdout: "PASS" + node_version: ">=6" +} diff --git a/test/compress/destructured.js b/test/compress/destructured.js new file mode 100644 index 00000000000..e051541a529 --- /dev/null +++ b/test/compress/destructured.js @@ -0,0 +1,3888 @@ +redefine_arguments_1: { + options = { + toplevel: false, + unused: true, + } + input: { + function f([ arguments ]) {} + } + expect: { + function f([]) {} + } + expect_stdout: true + node_version: ">=8" +} + +redefine_arguments_1_toplevel: { + options = { + toplevel: true, + unused: true, + } + input: { + function f([ arguments ]) {} + } + expect: {} + expect_stdout: true + node_version: ">=8" +} + +redefine_arguments_2: { + options = { + side_effects: true, + } + input: { + (function([], arguments) {}); + } + expect: {} + expect_stdout: true + node_version: ">=8" +} + +redefine_arguments_3: { + options = { + keep_fargs: false, + unused: true, + } + input: { + (function([], arguments) {})([]); + } + expect: { + (function() {})(); + } + expect_stdout: true + node_version: ">=8" +} + +redefine_arguments_4: { + options = { + toplevel: true, + unused: true, + } + input: { + function f() { + (function({}, arguments) {}); + } + } + expect: {} + expect_stdout: true + node_version: ">=8" +} + +uses_arguments_1_merge_vars: { + options = { + merge_vars: true, + } + input: { + console.log(typeof function({}) { + return arguments; + }(42)); + } + expect: { + console.log(typeof function({}) { + return arguments; + }(42)); + } + expect_stdout: "object" + node_version: ">=6" +} + +uses_arguments_1_unused: { + options = { + unused: true, + } + input: { + console.log(typeof function({}) { + return arguments; + }(42)); + } + expect: { + console.log(typeof function({}) { + return arguments; + }(42)); + } + expect_stdout: "object" + node_version: ">=6" +} + +uses_arguments_2: { + options = { + collapse_vars: true, + reduce_vars: true, + } + input: { + console.log(typeof function({ a }) { + a[1] = 2; + return arguments; + }({ a: 42 })); + } + expect: { + console.log(typeof function({ a }) { + a[1] = 2; + return arguments; + }({ a: 42 })); + } + expect_stdout: "object" + node_version: ">=6" +} + +funarg_merge_vars_1: { + options = { + merge_vars: true, + } + input: { + function f(a, { + [a]: b + }) { + console.log(b); + } + f(0, [ "PASS" ]); + } + expect: { + function f(a, { + [a]: b + }) { + console.log(b); + } + f(0, [ "PASS" ]); + } + expect_stdout: "PASS" + node_version: ">=6" +} + +funarg_merge_vars_2: { + options = { + merge_vars: true, + } + input: { + var a = 0; + (function f({ + [a]: b, + }) { + var a = typeof b; + console.log(a); + })([ 42 ]); + } + expect: { + var a = 0; + (function f({ + [a]: b, + }) { + var a = typeof b; + console.log(a); + })([ 42 ]); + } + expect_stdout: "number" + node_version: ">=6" +} + +funarg_side_effects_1: { + options = { + side_effects: true, + } + input: { + try { + (function({}) {})(); + } catch (e) { + console.log("PASS"); + } + } + expect: { + try { + [ {} ] = []; + } catch (e) { + console.log("PASS"); + } + } + expect_stdout: "PASS" + node_version: ">=6" +} + +funarg_side_effects_2: { + options = { + side_effects: true, + } + input: { + try { + (function({ + [(a, 0)]: a, + }) {})(1); + } catch (e) { + console.log("PASS"); + } + } + expect: { + try { + (function({ + [(a, 0)]: a, + }) {})(1); + } catch (e) { + console.log("PASS"); + } + } + expect_stdout: "PASS" + node_version: ">=6.9.2" +} + +funarg_side_effects_3: { + options = { + side_effects: true, + } + input: { + try { + (function({ + p: { + [(a, 0)]: a, + }, + }) {})({ + p: 1, + }); + } catch (e) { + console.log("PASS"); + } + } + expect: { + try { + (function({ + p: { + [(a, 0)]: a, + }, + }) {})({ + p: 1, + }); + } catch (e) { + console.log("PASS"); + } + } + expect_stdout: "PASS" + node_version: ">=6.9.2" +} + +funarg_unused_1: { + options = { + keep_fargs: false, + unused: true, + } + input: { + (function([]) {})([ console.log("PASS") ]); + } + expect: { + (function() {})(console.log("PASS")); + } + expect_stdout: "PASS" + node_version: ">=6" +} + +funarg_unused_2: { + options = { + unused: true, + } + input: { + function f([ a, b, c ]) { + console.log(b); + } + f([ "FAIL", "PASS" ]); + } + expect: { + function f([ , b ]) { + console.log(b); + } + f([ "FAIL", "PASS" ]); + } + expect_stdout: "PASS" + node_version: ">=6" +} + +funarg_unused_3: { + options = { + objects: true, + evaluate: true, + unused: true, + } + input: { + function f({ + [0]: a, + }) { + return "PASS"; + } + console.log(f([ "FAIL" ])); + } + expect: { + function f({}) { + return "PASS"; + } + console.log(f([ "FAIL" ])); + } + expect_stdout: "PASS" + node_version: ">=6" +} + +funarg_unused_4: { + options = { + keep_fargs: false, + pure_getters: "strict", + unused: true, + } + input: { + console.log(function([ a ], { b }, c) { + return "PASS"; + }([ 1 ], { b: 2 }, 3)); + } + expect: { + console.log(function() { + return "PASS"; + }()); + } + expect_stdout: "PASS" + node_version: ">=6" +} + +funarg_unused_5: { + options = { + unused: true, + } + input: { + try { + (function({ + [c = 0]: c + }) {})(1); + } catch (e) { + console.log("PASS"); + } + } + expect: { + try { + (function({ + [c = 0]: c + }) {})(1); + } catch (e) { + console.log("PASS"); + } + } + expect_stdout: "PASS" + node_version: ">=6" +} + +funarg_unused_6_inline: { + options = { + inline: true, + pure_getters: "strict", + unused: true, + } + input: { + (function(a) { + var {} = (a = console, 42); + })(); + console.log(typeof a); + } + expect: { + void console; + console.log(typeof a); + } + expect_stdout: "undefined" + node_version: ">=6" +} + +funarg_unused_6_keep_fargs: { + options = { + keep_fargs: false, + unused: true, + } + input: { + (function(a) { + var {} = (a = console, 42); + })(); + console.log(typeof a); + } + expect: { + (function() { + console; + var {} = 42; + })(); + console.log(typeof a); + } + expect_stdout: "undefined" + node_version: ">=6" +} + +funarg_collapse_vars_1: { + options = { + collapse_vars: true, + unused: true, + } + input: { + console.log(function(a, {}) { + return typeof a; + var b; + }(console, {})); + } + expect: { + console.log(function(a, {}) { + return typeof (0, console); + }(0, {})); + } + expect_stdout: "object" + node_version: ">=6" +} + +funarg_collapse_vars_2: { + options = { + collapse_vars: true, + keep_fargs: false, + unused: true, + } + input: { + console.log(function([ a ], { b }, c) { + return a + b + c; + }([ "P" ], { b: "A" }, "SS")); + } + expect: { + console.log(function([ a ], { b }) { + return a + b + "SS"; + }([ "P" ], { b: "A" })); + } + expect_stdout: "PASS" + node_version: ">=6" +} + +funarg_collapse_vars_3: { + options = { + collapse_vars: true, + } + input: { + var a = "FAIL"; + try { + a = "PASS"; + (function({}) {})(); + throw "PASS"; + } catch (e) { + console.log(a); + } + } + expect: { + var a = "FAIL"; + try { + a = "PASS"; + (function({}) {})(); + throw "PASS"; + } catch (e) { + console.log(a); + } + } + expect_stdout: "PASS" + node_version: ">=6" +} + +funarg_collapse_vars_4: { + options = { + collapse_vars: true, + unused: true, + } + input: { + var a = "PASS"; + (function(b, { log: c }) { + c(b); + })(a, console); + } + expect: { + var a = "PASS"; + (function(b, { log: c }) { + c(a); + })(0, console); + } + expect_stdout: "PASS" + node_version: ">=6" +} + +funarg_collapse_vars_5: { + options = { + collapse_vars: true, + unused: true, + } + input: { + A = "FAIL"; + B = "PASS"; + try { + console.log(function({}, a) { + return a; + }(null, A = B)); + } catch (e) {} + console.log(A); + } + expect: { + A = "FAIL"; + B = "PASS"; + try { + console.log(function({}, a) { + return a; + }(null, A = B)); + } catch (e) {} + console.log(A); + } + expect_stdout: "PASS" + node_version: ">=6" +} + +funarg_collapse_vars_6: { + options = { + collapse_vars: true, + unused: true, + } + input: { + A = "FAIL"; + B = "PASS"; + function f() { + console.log(function({}, a) { + return a; + }(null, A = B)); + } + try { + f(); + } catch (e) { + console.log(A); + } + } + expect: { + A = "FAIL"; + B = "PASS"; + function f() { + console.log(function({}, a) { + return a; + }(null, A = B)); + } + try { + f(); + } catch (e) { + console.log(A); + } + } + expect_stdout: "PASS" + node_version: ">=6" +} + +funarg_reduce_vars_1: { + options = { + reduce_vars: true, + unused: true, + } + input: { + try { + (function({ + [a]: b, + }, a) { + console.log("FAIL"); + })({}); + } catch (e) { + console.log("PASS"); + } + } + expect: { + try { + (function({ + [a]: b, + }, a) { + console.log("FAIL"); + })({}); + } catch (e) { + console.log("PASS"); + } + } + expect_stdout: "PASS" + node_version: ">=6" +} + +funarg_reduce_vars_2: { + options = { + evaluate: true, + keep_fargs: false, + pure_getters: "strict", + reduce_vars: true, + unsafe: true, + unused: true, + } + input: { + console.log(function([ a ], { b }, c) { + return a + b + c; + }([ "P" ], { b: "A" }, "SS")); + } + expect: { + console.log("PASS"); + } + expect_stdout: "PASS" + node_version: ">=6" +} + +funarg_reduce_vars_3: { + options = { + evaluate: true, + reduce_vars: true, + toplevel: true, + } + input: { + var a = 0; + (function({ + [a++]: b + }) {})(0); + console.log(a); + } + expect: { + var a = 0; + (function({ + [a++]: b + }) {})(0); + console.log(a); + } + expect_stdout: "1" + node_version: ">=6" +} + +funarg_reduce_vars_4: { + options = { + evaluate: true, + reduce_vars: true, + } + input: { + try { + (function f({ + [a = 1]: a, + }) {})(2); + } catch (e) { + console.log("PASS"); + } + } + expect: { + try { + (function f({ + [a = 1]: a, + }) {})(2); + } catch (e) { + console.log("PASS"); + } + } + expect_stdout: "PASS" + node_version: ">=6" +} + +funarg_computed_key_scope_1: { + rename = true + input: { + var b = 0; + function f({ + [b]: a + }) { + var b = 42; + console.log(a, b); + } + f([ "PASS" ]); + } + expect: { + var b = 0; + function f({ + [b]: a + }) { + var c = 42; + console.log(a, c); + } + f([ "PASS" ]); + } + expect_stdout: "PASS 42" + node_version: ">=6" +} + +funarg_computed_key_scope_2: { + options = { + reduce_funcs: true, + reduce_vars: true, + unused: true, + } + input: { + (function({ + [function() { + console.log(typeof f); + }()]: a + }) { + function f() {} + })(0); + } + expect: { + (function({ + [function() { + console.log(typeof f); + }()]: a + }) {})(0); + } + expect_stdout: "undefined" + node_version: ">=6" +} + +funarg_computed_key_scope_3: { + options = { + reduce_funcs: true, + reduce_vars: true, + unused: true, + } + input: { + (function({ + [function() { + (function({ + [function() { + console.log(typeof f, typeof g, typeof h); + }()]: a + }) { + function f() {} + })(1); + function g() {} + }()]: b + }) { + function h() {} + })(2); + } + expect: { + (function({ + [function() { + (function({ + [function() { + console.log(typeof f, typeof function() {}, typeof h); + }()]: a + }) {})(1); + }()]: b + }) {})(2); + } + expect_stdout: "undefined function undefined" + node_version: ">=6" +} + +funarg_inline: { + options = { + inline: true, + reduce_vars: true, + toplevel: true, + unused: true, + } + input: { + try { + function f({}) { + return 42; + } + var a = f(); + } catch (e) { + console.log("PASS"); + } + } + expect: { + try { + [ {} ] = []; + } catch (e) { + console.log("PASS"); + } + } + expect_stdout: "PASS" + node_version: ">=6" +} + +process_returns: { + options = { + booleans: true, + } + input: { + console.log(function({ length }) { + return length ? "FAIL" : "PASS"; + }(function() { + return 42; + })); + } + expect: { + console.log(function({ length }) { + return length ? "FAIL" : "PASS"; + }(function() {})); + } + expect_stdout: "PASS" + node_version: ">=6" +} + +simple_const: { + options = { + evaluate: true, + reduce_vars: true, + toplevel: true, + unsafe: true, + unused: true, + varify: true, + } + input: { + const [ a ] = [ "PASS" ]; + console.log(a); + } + expect: { + console.log("PASS"); + } + expect_stdout: "PASS" + node_version: ">=6" +} + +simple_let: { + options = { + evaluate: true, + reduce_vars: true, + toplevel: true, + unsafe: true, + unused: true, + varify: true, + } + input: { + let [ a ] = [ "PASS" ]; + console.log(a); + } + expect: { + console.log("PASS"); + } + expect_stdout: "PASS" + node_version: ">=6" +} + +simple_var: { + options = { + evaluate: true, + reduce_vars: true, + toplevel: true, + unsafe: true, + unused: true, + } + input: { + var [ a ] = [ "PASS" ]; + console.log(a); + } + expect: { + console.log("PASS"); + } + expect_stdout: "PASS" + node_version: ">=6" +} + +drop_catch: { + options = { + dead_code: true, + } + input: { + try {} catch ({ + [console.log("FAIL")]: e, + }) {} finally { + console.log("PASS"); + } + } + expect: { + console.log("PASS"); + } + expect_stdout: "PASS" + node_version: ">=6" +} + +drop_catch_var: { + options = { + unused: true, + } + input: { + try { + throw new Error("PASS"); + } catch ({ name, message }) { + console.log(message); + } + } + expect: { + try { + throw new Error("PASS"); + } catch ({ message }) { + console.log(message); + } + } + expect_stdout: "PASS" + node_version: ">=6" +} + +collapse_vars_1: { + options = { + collapse_vars: true, + } + input: { + var a = "PASS"; + var { + [a.p]: a, + } = !console.log(a); + } + expect: { + var a = "PASS"; + var { + [a.p]: a, + } = !console.log(a); + } + expect_stdout: "PASS" + node_version: ">=6" +} + +collapse_vars_2: { + options = { + collapse_vars: true, + } + input: { + console.log(function() { + [] = []; + return "PASS"; + }()); + } + expect: { + console.log(function() { + [] = []; + return "PASS"; + }()); + } + expect_stdout: "PASS" + node_version: ">=6" +} + +collapse_vars_3: { + options = { + collapse_vars: true, + } + input: { + console.log(function(a) { + [ a ] = (a = "FAIL", [ "PASS" ]); + return a; + }()); + } + expect: { + console.log(function(a) { + [ a ] = (a = "FAIL", [ "PASS" ]); + return a; + }()); + } + expect_stdout: "PASS" + node_version: ">=6" +} + +collapse_vars_4: { + options = { + collapse_vars: true, + } + input: { + var a; + try { + a = 42; + [ 42..p ] = null; + } catch (e) { + console.log(a); + } + } + expect: { + var a; + try { + a = 42; + [ 42..p ] = null; + } catch (e) { + console.log(a); + } + } + expect_stdout: "42" + node_version: ">=6" +} + +collapse_vars_5: { + options = { + collapse_vars: true, + } + input: { + var a; + try { + [] = (a = 42, null); + a = 42; + } catch (e) { + console.log(a); + } + } + expect: { + var a; + try { + [] = (a = 42, null); + a = 42; + } catch (e) { + console.log(a); + } + } + expect_stdout: "42" + node_version: ">=6" +} + +collapse_vars_6: { + options = { + collapse_vars: true, + } + input: { + var a; + try { + var [] = (a = 42, null); + a = 42; + } catch (e) { + console.log(a); + } + } + expect: { + var a; + try { + var [] = (a = 42, null); + a = 42; + } catch (e) { + console.log(a); + } + } + expect_stdout: "42" + node_version: ">=6" +} + +collapse_vars_7: { + options = { + collapse_vars: true, + } + input: { + var a = "FAIL"; + try { + (function() { + [] = (a = "PASS", null); + return "PASS"; + })(); + } catch (e) { + console.log(a); + } + } + expect: { + var a = "FAIL"; + try { + (function() { + [] = (a = "PASS", null); + return "PASS"; + })(); + } catch (e) { + console.log(a); + } + } + expect_stdout: "PASS" + node_version: ">=6" +} + +collapse_vars_8: { + options = { + collapse_vars: true, + } + input: { + var a = "FAIL"; + try { + (function() { + var {} = (a = "PASS", null); + return "PASS"; + })(); + } catch (e) { + console.log(a); + } + } + expect: { + var a = "FAIL"; + try { + (function() { + var {} = (a = "PASS", null); + return "PASS"; + })(); + } catch (e) { + console.log(a); + } + } + expect_stdout: "PASS" + node_version: ">=6" +} + +collapse_vars_9: { + options = { + collapse_vars: true, + } + input: { + console.log(function(a) { + try { + var b = function([ c ]) { + if (c) + return "FAIL 1"; + }(); + a = "FAIL 2"; + return b; + } catch (e) { + return a; + } + }("PASS")); + } + expect: { + console.log(function(a) { + try { + var b = function([ c ]) { + if (c) + return "FAIL 1"; + }(); + a = "FAIL 2"; + return b; + } catch (e) { + return a; + } + }("PASS")); + } + expect_stdout: "PASS" + node_version: ">=6" +} + +conditionals: { + options = { + conditionals: true, + } + input: { + if (console.log("PASS")) { + var [] = 0; + } + } + expect: { + console.log("PASS") && ([] = 0); + } + expect_stdout: "PASS" + node_version: ">=6" +} + +dead_code: { + options = { + conditionals: true, + dead_code: true, + evaluate: true, + } + input: { + if (0) { + let [] = 42; + var { a, b: [ c ] } = null; + } + console.log("PASS"); + } + expect: { + 0; + var a, c; + console.log("PASS"); + } + expect_stdout: "PASS" + node_version: ">=6" +} + +drop_unused_1: { + options = { + toplevel: true, + unused: true, + } + input: { + switch (0) { + case console.log(a, a): + try { + throw 42; + } catch (a) { + var [ a ] = []; + } + } + } + expect: { + switch (0) { + case console.log(a, a): + try { + throw 42; + } catch (a) { + var a = [][0]; + } + } + } + expect_stdout: "undefined undefined" + node_version: ">=6" +} + +drop_unused_2: { + options = { + merge_vars: true, + pure_getters: "strict", + reduce_vars: true, + toplevel: true, + unused: true, + } + input: { + function f(a) { + var b = [ console.log("PASS"), a ], { + p: a, + } = 0; + } + f(); + } + expect: { + (function(a) { + console.log("PASS"); + })(); + } + expect_stdout: "PASS" + node_version: ">=6" +} + +drop_hole: { + options = { + unused: true, + } + input: { + var [ a ] = [ , ]; + console.log(a); + } + expect: { + var a = [][0]; + console.log(a); + } + expect_stdout: "undefined" + node_version: ">=6" +} + +keep_key_1: { + options = { + evaluate: true, + side_effects: true, + unused: true, + } + input: { + ({} = { + [(console.log("PASS"), 42)]: null, + }); + } + expect: { + ({} = { + [(console.log("PASS"), 42)]: 0, + }); + } + expect_stdout: "PASS" + node_version: ">=6" +} + +keep_key_2: { + options = { + evaluate: true, + toplevel: true, + unused: true, + } + input: { + var { 42: a } = { [(console.log("PASS"), 42)](){} }; + } + expect: { + var {} = { [(console.log("PASS"), 42)]: 0 }; + } + expect_stdout: "PASS" + node_version: ">=6" +} + +keep_key_2_pure_getters: { + options = { + evaluate: true, + pure_getters: "strict", + toplevel: true, + unused: true, + } + input: { + var { 42: a } = { [(console.log("PASS"), 42)](){} }; + } + expect: { + console.log("PASS"); + } + expect_stdout: "PASS" + node_version: ">=6" +} + +keep_reference: { + options = { + reduce_vars: true, + toplevel: true, + unused: true, + } + input: { + var a = [ {}, 42 ]; + var [ b, c ] = a; + console.log(a[0] === b ? "PASS" : "FAIL"); + } + expect: { + var a = [ {}, 42 ]; + var b = a[0]; + console.log(a[0] === b ? "PASS" : "FAIL"); + } + expect_stdout: "PASS" + node_version: ">=6" +} + +maintain_position_assign: { + options = { + unused: true, + } + input: { + console.log(([ , ] = [ , "PASS" ])[1]); + } + expect: { + console.log([ , "PASS" ][1]); + } + expect_stdout: "PASS" + node_version: ">=6" +} + +maintain_position_var: { + options = { + toplevel: true, + unused: true, + } + input: { + A = "FAIL"; + var [ a, b ] = [ A ]; + console.log(b || "PASS"); + } + expect: { + A = "FAIL"; + var [ , b ] = [ A ]; + console.log(b || "PASS"); + } + expect_stdout: "PASS" + node_version: ">=6" +} + +side_effects_array: { + options = { + unused: true, + } + input: { + try { + var [ a ] = 42; + } catch (e) { + console.log("PASS"); + } + } + expect: { + try { + var [ a ] = 42; + } catch (e) { + console.log("PASS"); + } + } + expect_stdout: "PASS" + node_version: ">=6" +} + +side_effects_object: { + options = { + toplevel: true, + unused: true, + } + input: { + var a = null, b = console, { c } = 42; + try { + c[a = "PASS"]; + } catch (e) { + console.log(a); + } + } + expect: { + var a = null, c = (console, 42["c"]); + try { + c[a = "PASS"]; + } catch (e) { + console.log(a); + } + } + expect_stdout: "PASS" + node_version: ">=6" +} + +join_vars: { + options = { + conditionals: true, + join_vars: true, + } + input: { + const [ a ] = [ "PASS" ]; + a, + console.log(a); + } + expect: { + const [ a ] = [ "PASS" ]; + a, + console.log(a); + } + expect_stdout: "PASS" + node_version: ">=6" +} + +keep_fargs: { + options = { + keep_fargs: false, + unused: true, + } + input: { + console.log(function f(a) { + var {} = a; + }(0)); + } + expect: { + console.log(function(a) { + var {} = a; + }(0)); + } + expect_stdout: "undefined" + node_version: ">=6" +} + +reduce_vars_1: { + options = { + reduce_vars: true, + toplevel: true, + unused: true, + } + input: { + var a; + console.log("PASS") && ([ a ] = 0); + } + expect: { + var a; + console.log("PASS") && ([ a ] = 0); + } + expect_stdout: "PASS" + node_version: ">=6" +} + +reduce_vars_2: { + options = { + evaluate: true, + reduce_vars: true, + toplevel: true, + unused: true, + } + input: { + var a = "FAIL", b = 42; + ({ + [console.log(a, b)]: b.p + } = a = "PASS"); + } + expect: { + ({ + [console.log("PASS", 42)]: 42..p + } = "PASS"); + } + expect_stdout: "PASS 42" + node_version: ">=6" +} + +computed_key_evaluate: { + options = { + evaluate: true, + reduce_vars: true, + toplevel: true, + } + input: { + var a = 0, { + [++a]: b, + } = [ "FAIL 1", a ? "FAIL 2" : "PASS" ]; + console.log(b); + } + expect: { + var a = 0, { + [1]: b, + } = [ "FAIL 1", 0 ? "FAIL 2" : "PASS" ]; + console.log(b); + } + expect_stdout: "PASS" + node_version: ">=6" +} + +computed_key_unused: { + options = { + toplevel: true, + unused: true, + } + input: { + var { + [console.log("bar")]: a, + [console.log("baz")]: { b }, + [console.log("moo")]: [ + c, + { + [console.log("moz")]: d, + e, + }, + ], + } = { + [console.log("foo")]: [ null, 42 ], + }; + } + expect: { + var { + [console.log("bar")]: a, + [console.log("baz")]: {}, + [console.log("moo")]: [ + , + { + [console.log("moz")]: d, + }, + ], + } = { + [console.log("foo")]: [ null, 42 ], + }; + } + expect_stdout: [ + "foo", + "bar", + "baz", + "moo", + "moz", + ] + node_version: ">=6" +} + +for_in_1: { + options = { + loops: true, + toplevel: true, + unused: true, + } + input: { + for (var { a } in console.log("PASS")); + } + expect: { + for (var { a } in console.log("PASS")); + } + expect_stdout: "PASS" + node_version: ">=6" +} + +for_in_2: { + options = { + join_vars: true, + } + input: { + var a; + for (var { b } in console.log("PASS")); + } + expect: { + var a, b; + for ({ b } in console.log("PASS")); + } + expect_stdout: "PASS" + node_version: ">=6" +} + +for_in_3: { + options = { + merge_vars: true, + reduce_vars: true, + } + input: { + for (var { length: a } in [ 42 ]) + console.log(a); + } + expect: { + for (var { length: a } in [ 42 ]) + console.log(a); + } + expect_stdout: "1" + node_version: ">=6" +} + +fn_name_evaluate: { + options = { + evaluate: true, + objects: true, + reduce_vars: true, + typeofs: true, + } + input: { + console.log(function f({ + [typeof f]: a, + }) { + var f; + return a; + }({ + function: "PASS", + undefined: "FAIL", + })); + } + expect: { + console.log(function f({ + function: a, + }) { + var f; + return a; + }({ + function: "PASS", + undefined: "FAIL", + })); + } + expect_stdout: "PASS" + node_version: ">=6" +} + +fn_name_unused: { + options = { + unused: true, + } + input: { + console.log(function f({ + [typeof f]: a, + }) { + var f; + return a; + }({ + function: "PASS", + undefined: "FAIL", + })); + } + expect: { + console.log(function f({ + [typeof f]: a, + }) { + var f; + return a; + }({ + function: "PASS", + undefined: "FAIL", + })); + } + expect_stdout: "PASS" + node_version: ">=6" +} + +hoist_vars: { + options = { + hoist_vars: true, + join_vars: true, + unused: true, + } + input: { + var a = "PASS"; + var [ b ] = [ 42 ]; + console.log(a, b); + } + expect: { + var a = "PASS", b = [ 42 ][0]; + console.log(a, b); + } + expect_stdout: "PASS 42" + node_version: ">=6" +} + +singleton_1: { + options = { + pure_getters: true, + side_effects: true, + unused: true, + } + input: { + var [ a ] = "P", b, o = {}; + [ { 1: o.p } ] = [ "FAIL" ]; + ({ foo: [ o.q ] } = { foo: "S" }); + [ b = "S" ] = []; + console.log(a + o.p + o.q + b); + } + expect: { + var b, a = "P"[0], o = {}; + o.p = [ "FAIL"["1"] ][0]; + o.q = { foo: "S"[0] }["foo"]; + [ b = "S" ] = []; + console.log(a + o.p + o.q + b); + } + expect_stdout: "PASS" + node_version: ">=6" +} + +singleton_2: { + options = { + evaluate: true, + passes: 2, + pure_getters: true, + side_effects: true, + unsafe: true, + unused: true, + } + input: { + var [ a ] = "P", b, o = {}; + [ { 1: o.p } ] = [ "FAIL" ]; + ({ foo: [ o.q ] } = { foo: "S" }); + [ b = "S" ] = []; + console.log(a + o.p + o.q + b); + } + expect: { + var b, a = "P", o = {}; + o.p = "A"; + o.q = "S"; + [ b = "S" ] = []; + console.log(a + o.p + o.q + b); + } + expect_stdout: "PASS" + node_version: ">=6" +} + +singleton_side_effects: { + options = { + side_effects: true, + unused: true, + } + input: { + [ 42[console.log("foo")] ] = [ console.log("bar") ]; + } + expect: { + [ 42[console.log("foo")] ] = [ console.log("bar") ]; + } + expect_stdout: [ + "bar", + "foo", + ] + node_version: ">=6" +} + +mangle_properties: { + mangle = { + properties: { + domprops: true, + }, + } + input: { + function f({ p: a }) { + return a; + } + console.log(f({ p: "PASS" })); + } + expect_exact: 'function f({n}){return n}console.log(f({n:"PASS"}));' + expect_stdout: "PASS" + node_version: ">=6" +} + +issue_4280: { + options = { + evaluate: true, + reduce_vars: true, + toplevel: true, + unsafe: true, + unused: true, + } + input: { + var { + 1: a, + } = 2; + console.log(a); + } + expect: { + var {} = 2; + console.log(void 0); + } + expect_stdout: "undefined" + node_version: ">=6" +} + +issue_4282: { + options = { + evaluate: true, + reduce_vars: true, + unused: true, + } + input: { + (function(a) { + ({ + [a = "bar"]: 0[console.log(a)], + } = 0); + })("foo"); + } + expect: { + (function(a) { + ({ + [a = "bar"]: 0[console.log(a)], + } = 0); + })("foo"); + } + expect_stdout: true + node_version: ">=6" +} + +issue_4284_1: { + options = { + dead_code: true, + } + input: { + var a, { + 0: b, + } = a = "foo"; + console.log(a, b); + } + expect: { + var a, { + 0: b, + } = a = "foo"; + console.log(a, b); + } + expect_stdout: "foo f" + node_version: ">=6" +} + +issue_4284_2: { + options = { + collapse_vars: true, + } + input: { + var a, { + [console.log(a)]: b, + } = (a = "PASS", 0); + var c = a; + } + expect: { + var a, { + [console.log(a)]: b, + } = (a = "PASS", 0); + var c = a; + } + expect_stdout: "PASS" + node_version: ">=6" +} + +issue_4284_3: { + options = { + collapse_vars: true, + } + input: { + var a, b; + ({ + [console.log(a)]: b, + } = (a = "PASS", 0)); + var c = a; + } + expect: { + var a, b; + ({ + [console.log(a)]: b, + } = (a = "PASS", 0)); + var c = a; + } + expect_stdout: "PASS" + node_version: ">=6" +} + +issue_4286_1: { + options = { + collapse_vars: true, + toplevel: true, + } + input: { + var a = "PASS", b; + (0 && a)[{ a } = b = a]; + console.log(b); + } + expect: { + var a = "PASS", b; + (0 && a)[{ a } = b = a]; + console.log(b); + } + expect_stdout: "PASS" + node_version: ">=6" +} + +issue_4286_2: { + options = { + collapse_vars: true, + toplevel: true, + } + input: { + a = [ "PASS" ]; + var b, { a } = b = a; + console.log(b[0]); + } + expect: { + var b, { a } = b = a = [ "PASS" ]; + console.log(b[0]); + } + expect_stdout: "PASS" + node_version: ">=6" +} + +issue_4288: { + options = { + merge_vars: true, + } + input: { + function f({ + [new function() { + console.log(typeof b); + }()]: a, + }) { + var b = a; + b++; + } + f(0); + } + expect: { + function f({ + [new function() { + console.log(typeof b); + }()]: a, + }) { + var a = a; + a++; + } + f(0); + } + expect_stdout: "undefined" + node_version: ">=6" +} + +issue_4294: { + options = { + merge_vars: true, + } + input: { + A = "PASS"; + (function() { + var a = function({ + [a]: {}, + }) {}({ + [a]: 0, + }); + var b = A; + console.log(b); + })(); + } + expect: { + A = "PASS"; + (function() { + var a = function({ + [a]: {}, + }) {}({ + [a]: 0, + }); + var b = A; + console.log(b); + })(); + } + expect_stdout: "PASS" + node_version: ">=6" +} + +issue_4297: { + options = { + reduce_vars: true, + unused: true, + } + input: { + console.log(typeof function(a) { + return { a } = a; + }(function() {})); + } + expect: { + console.log(typeof function(a) { + return { a } = a; + }(function() {})); + } + expect_stdout: "function" + node_version: ">=6" +} + +issue_4298: { + options = { + merge_vars: true, + } + input: { + (function() { + var a = { + object: "PASS", + }; + function f({ + [typeof a]: b, + }) { + var a = b; + return a; + } + var c = f(a); + console.log(c); + })(); + } + expect: { + (function() { + var a = { + object: "PASS", + }; + function f({ + [typeof a]: b, + }) { + var a = b; + return a; + } + var c = f(a); + console.log(c); + })(); + } + expect_stdout: "PASS" + node_version: ">=6" +} + +issue_4301: { + options = { + merge_vars: true, + } + input: { + try { + console.log(function() { + var a, b = console; + return { + [a = b]: a.p, + } = "foo"; + }()); + } catch (e) { + console.log("bar"); + } + } + expect: { + try { + console.log(function() { + var a, b = console; + return { + [a = b]: a.p, + } = "foo"; + }()); + } catch (e) { + console.log("bar"); + } + } + expect_stdout: true + node_version: ">=6" +} + +issue_4308: { + options = { + collapse_vars: true, + unused: true, + } + input: { + var a = "PASS"; + console.log(function({ + [a = "FAIL"]: b + }, c) { + return c; + }(0, a)); + } + expect: { + var a = "PASS"; + console.log(function({ + [a = "FAIL"]: b + }, c) { + return c; + }(0, a)); + } + expect_stdout: "PASS" + node_version: ">=6" +} + +issue_4312: { + options = { + collapse_vars: true, + inline: true, + merge_vars: true, + reduce_vars: true, + toplevel: true, + unused: true, + } + input: { + var a; + (function f(b, c) { + return function({ + [a = b]: d, + }) {}(c && c); + })("PASS", "FAIL"); + console.log(a); + } + expect: { + b = "PASS", + c = "FAIL", + [ + { + [c = b]: d, + }, + ] = [ c && c ], + void 0; + var b, c, d; + console.log(c); + } + expect_stdout: "PASS" + node_version: ">=6" +} + +issue_4315: { + options = { + conditionals: true, + dead_code: true, + evaluate: true, + inline: true, + passes: 2, + reduce_funcs: true, + reduce_vars: true, + side_effects: true, + toplevel: true, + unused: true, + } + input: { + function f() { + console; + } + var a = function() { + if ([ 0[f && f] ] = []) + return this; + }(), b; + do { + console.log("PASS"); + } while (0 && (b = 0), b && a); + } + expect: { + [ 0[function() { + console + }] ] = []; + do { + console.log("PASS"); + } while (void 0); + } + expect_stdout: "PASS" + node_version: ">=6" +} + +issue_4319: { + options = { + inline: true, + reduce_vars: true, + toplevel: true, + } + input: { + function f(a) { + while (!a); + } + console.log(function({}) { + return f(console); + }(0)); + } + expect: { + function f(a) { + while (!a); + } + console.log(([ {} ] = [ 0 ], f(console))); + } + expect_stdout: "undefined" + node_version: ">=6" +} + +issue_4321: { + options = { + inline: 3, + keep_fargs: false, + } + input: { + try { + console.log(function({}) { + return function() { + while (!console); + }(); + }()); + } catch (e) { + console.log("PASS"); + } + } + expect: { + try { + console.log(([ {} ] = [], function() { + while (!console); + }())); + } catch (e) { + console.log("PASS"); + } + } + expect_stdout: "PASS" + node_version: ">=6" +} + +issue_4323: { + options = { + ie: true, + inline: true, + merge_vars: true, + reduce_vars: true, + toplevel: true, + unused: true, + } + input: { + var a = 0; + (function b({ + [function a() { + console.log(typeof a); + }()]: d, + }) {})(0); + (function c(e) { + e.p; + })(1, console.log); + } + expect: { + var a = 0; + [ + { + [function a() { + console.log(typeof a); + }()]: d, + }, + ] = [ 0 ], + void 0; + var d; + e = 1, + console.log, + void e.p; + var e; + } + expect_stdout: "function" + node_version: ">=6" +} + +issue_4355: { + options = { + loops: true, + unused: true, + } + input: { + var a; + (function({ + [function() { + for (a in "foo"); + }()]: b, + }) { + var a; + })(0); + console.log(a); + } + expect: { + var a; + (function({ + [function() { + for (a in "foo"); + }()]: b, + }) {})(0); + console.log(a); + } + expect_stdout: "2" + node_version: ">=6" +} + +issue_4372_1: { + options = { + dead_code: true, + } + input: { + var a = "FAIL"; + a += { + [console.log(a)]: a, + } = a = "PASS"; + } + expect: { + var a = "FAIL"; + a += { + [console.log(a)]: a, + } = a = "PASS"; + } + expect_stdout: "PASS" + node_version: ">=6" +} + +issue_4372_2: { + options = { + dead_code: true, + } + input: { + var a; + [ a ] = a = [ "PASS", "FAIL" ]; + console.log(a); + } + expect: { + var a; + [ a ] = a = [ "PASS", "FAIL" ]; + console.log(a); + } + expect_stdout: "PASS" + node_version: ">=6" +} + +issue_4383: { + options = { + evaluate: true, + reduce_vars: true, + unsafe: true, + } + input: { + console.log(function(a) { + [ a[0] ] = []; + return a.length; + }([])); + } + expect: { + console.log(function(a) { + [ a[0] ] = []; + return a.length; + }([])); + } + expect_stdout: "1" + node_version: ">=6" +} + +issue_4386: { + options = { + arguments: true, + } + input: { + function f({}) { + return arguments[0]; + } + console.log(f("PASS")); + } + expect: { + function f({}) { + return arguments[0]; + } + console.log(f("PASS")); + } + expect_stdout: "PASS" + node_version: ">=6" +} + +issue_4395: { + options = { + arguments: true, + } + input: { + console.log(function(a, {}) { + a = "FAIL"; + return arguments[0]; + }("PASS", 42)); + } + expect: { + console.log(function(a, {}) { + a = "FAIL"; + return arguments[0]; + }("PASS", 42)); + } + expect_stdout: "PASS" + node_version: ">=6" +} + +issue_4399: { + options = { + arguments: true, + } + input: { + console.log(function({ + [arguments[1]]: a, + }, b) { + return a; + }([ "PASS" ], 0)); + } + expect: { + console.log(function({ + [arguments[1]]: a, + }, b) { + return a; + }([ "PASS" ], 0)); + } + expect_stdout: "PASS" + node_version: ">=6" +} + +issue_4420: { + options = { + unused: true, + } + input: { + console.log(function() { + var a = 1; + try { + throw [ "FAIL", "PASS" ]; + } catch ({ + [a]: b, + }) { + let a = 0; + return b; + } + }()); + } + expect: { + console.log(function() { + var a = 1; + try { + throw [ "FAIL", "PASS" ]; + } catch ({ + [a]: b, + }) { + return b; + } + }()); + } + expect_stdout: "PASS" + node_version: ">=8" +} + +issue_4425: { + rename = true + input: { + var a; + console.log(function() { + try { + try { + throw 42; + } catch ({ + [a]: a, + }) {} + return "FAIL"; + } catch (e) { + return "PASS"; + } + }()); + } + expect: { + var a; + console.log(function() { + try { + try { + throw 42; + } catch ({ + [b]: b, + }) {} + return "FAIL"; + } catch (c) { + return "PASS"; + } + }()); + } + expect_stdout: "PASS" + node_version: ">=8" +} + +issue_4436_Infinity: { + options = { + unused: true, + } + input: { + console.log(function({ + [delete Infinity]: a, + }) { + var Infinity; + return a; + }({ + true: "FAIL", + false: "PASS", + })); + } + expect: { + console.log(function({ + [delete Infinity]: a, + }) { + return a; + }({ + true: "FAIL", + false: "PASS", + })); + } + expect_stdout: true + node_version: ">=6" +} + +issue_4436_NaN: { + options = { + unused: true, + } + input: { + console.log(function({ + [delete NaN]: a, + }) { + var NaN; + return a; + }({ + true: "FAIL", + false: "PASS", + })); + } + expect: { + console.log(function({ + [delete NaN]: a, + }) { + return a; + }({ + true: "FAIL", + false: "PASS", + })); + } + expect_stdout: true + node_version: ">=6" +} + +issue_4436_undefined: { + options = { + unused: true, + } + input: { + console.log(function({ + [delete undefined]: a, + }) { + var undefined; + return a; + }({ + true: "FAIL", + false: "PASS", + })); + } + expect: { + console.log(function({ + [delete undefined]: a, + }) { + return a; + }({ + true: "FAIL", + false: "PASS", + })); + } + expect_stdout: true + node_version: ">=6" +} + +issue_4446: { + options = { + collapse_vars: true, + } + input: { + a = "PASS"; + var a = [ a[0] ] = [ a ]; + console.log(a[0]); + } + expect: { + a = "PASS"; + var a = [ a[0] ] = [ a ]; + console.log(a[0]); + } + expect_stdout: "PASS" + node_version: ">=6" +} + +issue_4456: { + options = { + pure_getters: "strict", + unused: true, + } + input: { + var o = { + set p(v) { + console.log(v); + }, + }; + [ function() { + try { + return o; + } catch ({}) {} + }().p ] = [ "PASS" ]; + } + expect: { + var o = { + set p(v) { + console.log(v); + }, + }; + [ function() { + try { + return o; + } catch ({}) {} + }().p ] = [ "PASS" ]; + } + expect_stdout: "PASS" + node_version: ">=6" +} + +issue_4485_1: { + options = { + pure_getters: "strict", + side_effects: true, + } + input: { + (function([]) { + var arguments; + try { + arguments.length; + } catch (e) { + console.log("PASS"); + } + })([]); + } + expect: { + (function([]) { + var arguments; + try { + arguments.length; + } catch (e) { + console.log("PASS"); + } + })([]); + } + expect_stdout: true + node_version: ">=6" +} + +issue_4485_2: { + options = { + pure_getters: "strict", + side_effects: true, + } + input: { + (function([]) { + var arguments = null; + try { + arguments.length; + } catch (e) { + console.log("PASS"); + } + })([]); + } + expect: { + (function([]) { + var arguments = null; + try { + arguments.length; + } catch (e) { + console.log("PASS"); + } + })([]); + } + expect_stdout: "PASS" + node_version: ">=6" +} + +issue_4485_3: { + options = { + keep_fargs: false, + unused: true, + } + input: { + (function([]) { + var arguments; + try { + arguments.length; + } catch (e) { + console.log("PASS"); + } + })([]); + } + expect: { + (function([]) { + var arguments; + try { + arguments.length; + } catch (e) { + console.log("PASS"); + } + })([]); + } + expect_stdout: true + node_version: ">=6" +} + +issue_4500: { + options = { + evaluate: true, + keep_fnames: true, + reduce_vars: true, + toplevel: true, + unused: true, + } + input: { + var a = function f(b) { + return [ b ] = [], b; + }("FAIL"); + console.log(a || "PASS"); + } + expect: { + var a = function f(b) { + return [ b ] = [], b; + }("FAIL"); + console.log(a || "PASS"); + } + expect_stdout: "PASS" + node_version: ">=6" +} + +issue_4504: { + options = { + inline: true, + merge_vars: true, + } + input: { + A = "FAIL"; + (function f(a) { + ({ + [console.log(a)]: 0[(b => console + b)(A)] + } = 0); + })("PASS"); + } + expect: { + A = "FAIL"; + (function f(a) { + ({ + [console.log(a)]: 0[b = A, console + b] + } = 0); + var b; + })("PASS"); + } + expect_stdout: "PASS" + node_version: ">=6" +} + +issue_4508: { + options = { + inline: true, + toplevel: true, + unused: true, + } + input: { + for (var i = 0; i < 2; i++) + (function f([ a ]) { + var a = console.log(a) && b, b = null; + })([ "PASS" ]); + } + expect: { + for (var i = 0; i < 2; i++) + [ [ a ] ] = [ [ "PASS" ] ], + b = void 0, + a = console.log(a) && b, + b = null, + void 0; + var a, b; + } + expect_stdout: [ + "PASS", + "PASS", + ] + node_version: ">=6" +} + +issue_4512: { + options = { + side_effects: true, + } + input: { + console.log(function([ a, b = a ]) {}([])); + } + expect: { + console.log(function([ a, b = a ]) {}([])); + } + expect_stdout: "undefined" + node_version: ">=6" +} + +issue_4519_1: { + options = { + arguments: true, + keep_fargs: false, + } + input: { + try { + (function() { + var [ arguments ] = []; + arguments[0]; + })(); + } catch (e) { + console.log("PASS"); + } + } + expect: { + try { + (function() { + var [ arguments ] = []; + arguments[0]; + })(); + } catch (e) { + console.log("PASS"); + } + } + expect_stdout: "PASS" + node_version: ">=6" +} + +issue_4519_2: { + options = { + pure_getters: "strict", + side_effects: true, + } + input: { + try { + (function() { + var [ arguments ] = []; + arguments[0]; + })(); + } catch (e) { + console.log("PASS"); + } + } + expect: { + try { + (function() { + var [ arguments ] = []; + arguments[0]; + })(); + } catch (e) { + console.log("PASS"); + } + } + expect_stdout: "PASS" + node_version: ">=6" +} + +issue_4554: { + options = { + collapse_vars: true, + unused: true, + } + input: { + A = "PASS"; + var a = "FAIL"; + try { + (function({}, b) { + return b; + })(void 0, a = A); + } catch (e) { + console.log(a); + } + } + expect: { + A = "PASS"; + var a = "FAIL"; + try { + (function({}, b) { + return b; + })(void 0, a = A); + } catch (e) { + console.log(a); + } + } + expect_stdout: "PASS" + node_version: ">=6" +} + +issue_4584: { + options = { + evaluate: true, + reduce_vars: true, + } + input: { + try { + (function f({ + [console.log(a = "FAIL")]: a, + }) {})(0); + } catch (e) { + console.log("PASS"); + } + } + expect: { + try { + (function f({ + [console.log(a = "FAIL")]: a, + }) {})(0); + } catch (e) { + console.log("PASS"); + } + } + expect_stdout: "PASS" + node_version: ">=6" +} + +issue_4608_1: { + options = { + arguments: true, + keep_fargs: false, + } + input: { + (function() { + [ arguments ] = [ "foo" ]; + console.log(arguments[0]); + })(); + } + expect: { + (function() { + [ arguments ] = [ "foo" ]; + console.log(arguments[0]); + })(); + } + expect_stdout: "f" + node_version: ">=6" +} + +issue_4608_2: { + options = { + arguments: true, + reduce_vars: true, + } + input: { + (function(a) { + [ arguments ] = [ "foo" ]; + console.log(arguments[0]); + })(); + } + expect: { + (function(a) { + [ arguments ] = [ "foo" ]; + console.log(arguments[0]); + })(); + } + expect_stdout: "f" + node_version: ">=6" +} + +issue_4994: { + options = { + loops: true, + unused: true, + } + input: { + var a = "FAIL"; + (function([ + { + [function() { + for (a in { PASS: null }); + }()]: b, + }, + ]) { + var a; + })([ 42 ]); + console.log(a); + } + expect: { + var a = "FAIL"; + (function([ + { + [function() { + for (a in { PASS: null }); + }()]: b, + }, + ]) {})([ 42 ]); + console.log(a); + } + expect_stdout: "PASS" + node_version: ">=6" +} + +issue_5017: { + options = { + collapse_vars: true, + reduce_vars: true, + toplevel: true, + } + input: { + var a = function() {}; + var b = c = a; + var c = [ c ] = [ c ]; + console.log(c[0] === a ? "PASS" : "FAIL"); + } + expect: { + var a = function() {}; + var b = a; + var c = [ c ] = [ c = a ]; + console.log(c[0] === a ? "PASS" : "FAIL"); + } + expect_stdout: "PASS" + node_version: ">=6" +} + +issue_5071_1: { + options = { + unused: true, + } + input: { + var a; + console.log(([ , a ] = [ "PA", , ]).join("SS")); + } + expect: { + var a; + console.log(([ , a ] = [ "PA", , ]).join("SS")); + } + expect_stdout: "PASS" + node_version: ">=6" +} + +issue_5071_2: { + options = { + pure_getters: "strict", + unused: true, + } + input: { + var a; + ([ a ] = []).p = console.log("PASS"); + } + expect: { + var a; + ([ a ] = []).p = console.log("PASS"); + } + expect_stdout: "PASS" + node_version: ">=6" +} + +issue_5074_getter: { + options = { + evaluate: true, + side_effects: true, + unused: true, + } + input: { + ({} = { get [(console.log("PASS"), 42)]() {} }); + } + expect: { + ({} = { [(console.log("PASS"), 42)]: 0 }); + } + expect_stdout: "PASS" + node_version: ">=6" +} + +issue_5074_getter_pure_getters: { + options = { + evaluate: true, + pure_getters: "strict", + side_effects: true, + unused: true, + } + input: { + ({} = { get [(console.log("PASS"), 42)]() {} }); + } + expect: { + console.log("PASS"); + } + expect_stdout: "PASS" + node_version: ">=6" +} + +issue_5074_setter: { + options = { + evaluate: true, + side_effects: true, + unused: true, + } + input: { + ({} = { set [(console.log("PASS"), 42)](v) {} }); + } + expect: { + ({} = { [(console.log("PASS"), 42)]: 0 }); + } + expect_stdout: "PASS" + node_version: ">=6" +} + +issue_5074_setter_pure_getters: { + options = { + evaluate: true, + pure_getters: "strict", + side_effects: true, + unused: true, + } + input: { + ({} = { set [(console.log("PASS"), 42)](v) {} }); + } + expect: { + console.log("PASS"); + } + expect_stdout: "PASS" + node_version: ">=6" +} + +issue_5074_method: { + options = { + evaluate: true, + side_effects: true, + unused: true, + } + input: { + ({} = { [(console.log("PASS"), 42)]() {} }); + } + expect: { + ({} = { [(console.log("PASS"), 42)]: 0 }); + } + expect_stdout: "PASS" + node_version: ">=6" +} + +issue_5074_method_pure_getters: { + options = { + evaluate: true, + pure_getters: "strict", + side_effects: true, + unused: true, + } + input: { + ({} = { [(console.log("PASS"), 42)]() {} }); + } + expect: { + console.log("PASS"); + } + expect_stdout: "PASS" + node_version: ">=6" +} + +issue_5085_1: { + options = { + evaluate: true, + passes: 2, + reduce_vars: true, + toplevel: true, + unsafe: true, + unused: true, + } + input: { + var a = "PASS"; + var [ b ] = [ 42, a ], c = b ? 0 : a = "FAIL"; + console.log(a); + } + expect: { + var a = "PASS"; + 42; + console.log(a); + } + expect_stdout: "PASS" + node_version: ">=6" +} + +issue_5085_2: { + options = { + evaluate: true, + passes: 2, + reduce_vars: true, + side_effects: true, + unsafe: true, + unused: true, + } + input: { + var a = "PASS"; + (function(b) { + [ b ] = [ 42, a ]; + var c = b ? 0 : a = "FAIL"; + })(); + console.log(a); + } + expect: { + var a = "PASS"; + (function(b) { + 0; + })(); + console.log(a); + } + expect_stdout: "PASS" + node_version: ">=6" +} + +issue_5087_1: { + options = { + collapse_vars: true, + inline: true, + properties: true, + sequences: true, + side_effects: true, + unused: true, + } + input: { + var a = "PASS"; + (function() { + (function() { + (function([ b ]) { + b && console.log(b); + })([ a ]); + })(); + })(); + } + expect: { + var a = "PASS"; + (function() { + var b; + (b = a) && console.log(b); + })(); + } + expect_stdout: "PASS" + node_version: ">=6" +} + +issue_5087_2: { + options = { + collapse_vars: true, + inline: true, + passes: 2, + properties: true, + reduce_vars: true, + sequences: true, + side_effects: true, + unused: true, + } + input: { + var a = "PASS"; + (function() { + (function() { + (function([ b ]) { + b && console.log(b); + })([ a ]); + })(); + })(); + } + expect: { + var a = "PASS"; + a && console.log(a); + } + expect_stdout: "PASS" + node_version: ">=6" +} + +issue_5114_1: { + options = { + inline: true, + unused: true, + } + input: { + var a = "PASS"; + (function({}, a) {})(42); + console.log(a); + } + expect: { + var a = "PASS"; + [ {} ] = [ 42 ], + void 0; + console.log(a); + } + expect_stdout: "PASS" + node_version: ">=6" +} + +issue_5114_2: { + options = { + inline: true, + pure_getters: "strict", + reduce_vars: true, + side_effects: true, + unused: true, + } + input: { + var a = "PASS"; + (function f([], a) { + f.length; + })([]); + console.log(a); + } + expect: { + var a = "PASS"; + 0; + console.log(a); + } + expect_stdout: "PASS" + node_version: ">=6" +} + +issue_5114_3: { + options = { + inline: true, + pure_getters: "strict", + reduce_vars: true, + side_effects: true, + unused: true, + } + input: { + var a = "PASS"; + (function f(a, {}) { + f.length; + })(null, 42); + console.log(a); + } + expect: { + var a = "PASS"; + 0; + console.log(a); + } + expect_stdout: "PASS" + node_version: ">=6" +} + +issue_5153_array_assign: { + options = { + dead_code: true, + } + input: { + var a = function*() { + yield b; + }(), b; + [ b ] = b = a; + console.log(a === b ? "PASS" : "FAIL"); + } + expect: { + var a = function*() { + yield b; + }(), b; + [ b ] = b = a; + console.log(a === b ? "PASS" : "FAIL"); + } + expect_stdout: "PASS" + node_version: ">=6" +} + +issue_5153_array_var: { + options = { + dead_code: true, + } + input: { + var a = function*() { + yield b; + }(), [ b ] = b = a; + console.log(a === b ? "PASS" : "FAIL"); + } + expect: { + var a = function*() { + yield b; + }(), [ b ] = b = a; + console.log(a === b ? "PASS" : "FAIL"); + } + expect_stdout: "PASS" + node_version: ">=6" +} + +issue_5153_object_assign: { + options = { + dead_code: true, + } + input: { + var a = { + get p() { + return b; + }, + }, b; + ({ + p: b + } = b = a); + console.log(a === b ? "PASS" : "FAIL"); + } + expect: { + var a = { + get p() { + return b; + }, + }, b; + ({ + p: b + } = b = a); + console.log(a === b ? "PASS" : "FAIL"); + } + expect_stdout: "PASS" + node_version: ">=6" +} + +issue_5153_object_var: { + options = { + dead_code: true, + } + input: { + var a = { + get p() { + return b; + }, + }, { + p: b + } = b = a; + console.log(a === b ? "PASS" : "FAIL"); + } + expect: { + var a = { + get p() { + return b; + }, + }, { + p: b + } = b = a; + console.log(a === b ? "PASS" : "FAIL"); + } + expect_stdout: "PASS" + node_version: ">=6" +} + +issue_5168: { + options = { + collapse_vars: true, + } + input: { + (function a({ + [console.log(typeof function() { + ++a; + return a; + }())]: b, + }) { + var a; + })({}); + } + expect: { + (function a({ + [console.log(typeof function() { + ++a; + return a; + }())]: b, + }) { + var a; + })({}); + } + expect_stdout: "function" + node_version: ">=6" +} + +issue_5189_1: { + options = { + pure_getters: "strict", + reduce_vars: true, + side_effects: true, + toplevel: true, + unused: true, + } + input: { + var a = 42; + [ a.p ] = a = "PASS"; + console.log(a); + } + expect: { + var a; + [ a.p ] = a = "PASS"; + console.log(a); + } + expect_stdout: "PASS" + node_version: ">=6" +} + +issue_5189_2: { + options = { + pure_getters: "strict", + reduce_vars: true, + side_effects: true, + toplevel: true, + unused: true, + } + input: { + var a = 42; + ({ p: a.q } = a = "PASS"); + console.log(a); + } + expect: { + var a; + ({ p: a.q } = a = "PASS"); + console.log(a); + } + expect_stdout: "PASS" + node_version: ">=6" +} + +issue_5222: { + options = { + hoist_props: true, + inline: true, + reduce_vars: true, + side_effects: true, + toplevel: true, + unused: true, + } + input: { + function f() { + do { + (function() { + var a = { + p: [ a ] = [], + }; + })(); + } while (console.log("PASS")); + } + f(); + } + expect: { + do { + a = void 0, + a = { + p: [ a ] = [], + }; + } while (console.log("PASS")); + var a; + } + expect_stdout: "PASS" + node_version: ">=6" +} + +issue_5288_1: { + options = { + conditionals: true, + inline: true, + keep_fargs: false, + reduce_vars: true, + toplevel: true, + unused: true, + varify: true, + } + input: { + while (function([]) {}([ function f() { + if (console) + return console.log("PASS"); + else { + let a = 0; + } + }() ])); + } + expect: { + while (function() { + if (console) + console.log("PASS"); + }(), void 0); + } + expect_stdout: "PASS" + node_version: ">=6" +} + +issue_5288_2: { + options = { + conditionals: true, + inline: true, + keep_fargs: false, + passes: 2, + reduce_vars: true, + toplevel: true, + unused: true, + varify: true, + } + input: { + while (function([]) {}([ function f() { + if (console) + return console.log("PASS"); + else { + let a = 0; + } + }() ])); + } + expect: { + while (console && console.log("PASS"), void 0); + } + expect_stdout: "PASS" + node_version: ">=6" +} + +issue_5314_1: { + options = { + side_effects: true, + } + input: { + A = this; + new function() { + (function({ + [console.log(this === A ? "PASS" : "FAIL")]: a, + }) {})(42); + }(); + } + expect: { + A = this; + (function() { + (function({ + [console.log(this === A ? "PASS" : "FAIL")]: a, + }) {})(42); + })(); + } + expect_stdout: "PASS" + node_version: ">=6" +} + +issue_5314_2: { + options = { + side_effects: true, + } + input: { + A = this; + new function() { + (({ + [console.log(this === A ? "FAIL" : "PASS")]: a, + }) => {})(42); + }(); + } + expect: { + A = this; + new function() { + [ { + [console.log(this === A ? "FAIL" : "PASS")]: [][0], + } ] = [ 42 ]; + }(); + } + expect_stdout: "PASS" + node_version: ">=6" +} + +issue_5370: { + options = { + dead_code: true, + ie: true, + unused: true, + } + input: { + console.log(function arguments({}) { + return arguments; + try {} catch (e) { + var arguments; + } + }(42)); + } + expect: { + console.log(function arguments({}) { + return arguments; + var arguments; + }(42)); + } + expect_stdout: true + node_version: ">=6" +} + +issue_5405_1: { + options = { + evaluate: true, + reduce_vars: true, + toplevel: true, + unsafe: true, + } + input: { + var [ a ] = [ {} ]; + console.log(a === a ? "PASS" : "FAIL"); + } + expect: { + var [ a ] = [ {} ]; + console.log(true ? "PASS" : "FAIL"); + } + expect_stdout: "PASS" + node_version: ">=6" +} + +issue_5405_2: { + options = { + evaluate: true, + reduce_vars: true, + toplevel: true, + unsafe: true, + } + input: { + var { p: a } = { p: [] }; + console.log(a === a ? "PASS" : "FAIL"); + } + expect: { + var { p: a } = { p: [] }; + console.log(true ? "PASS" : "FAIL"); + } + expect_stdout: "PASS" + node_version: ">=6" +} + +issue_5423: { + options = { + merge_vars: true, + toplevel: true, + } + input: { + var a, b; + function f({ + [function() { + if (++a) + return 42; + }()]: c + }) {} + f(b = f); + console.log(typeof b); + } + expect: { + var a, b; + function f({ + [function() { + if (++a) + return 42; + }()]: c + }) {} + f(b = f); + console.log(typeof b); + } + expect_stdout: "function" + node_version: ">=6" +} + +issue_5454: { + options = { + hoist_props: true, + reduce_vars: true, + side_effects: true, + toplevel: true, + unused: true, + } + input: { + function f(a) { + var a = 42, a = { + p: [ a ] = [], + }; + return "PASS"; + } + console.log(f()); + } + expect: { + console.log(function(a) { + a = 42, a = { + p: [ a ] = [], + }; + return "PASS"; + }()); + } + expect_stdout: "PASS" + node_version: ">=6" +} + +issue_5485: { + options = { + comparisons: true, + } + input: { + (function f({ + p: f, + [console.log(void 0 === f ? "PASS" : "FAIL")]: a, + }) {})(42); + } + expect: { + (function f({ + p: f, + [console.log(void 0 === f ? "PASS" : "FAIL")]: a, + }) {})(42); + } + expect_stdout: "PASS" + node_version: ">=6" +} + +issue_5533_keep_fargs: { + options = { + evaluate: true, + inline: true, + join_vars: true, + keep_fargs: true, + loops: true, + side_effects: true, + unused: true, + } + input: { + "use strict"; + try { + (function() { + var a; + for (; 1;) + a = function() { + (function f([ b ]) { + b; + throw "PASS"; + })([]); + }(); + })(); + } catch (e) { + console.log(e); + } + } + expect: { + "use strict"; + try { + (function() { + for (;;) + throw "PASS"; + })(); + } catch (e) { + console.log(e); + } + } + expect_stdout: "PASS" + node_version: ">=6" +} + +issue_5533_drop_fargs: { + options = { + evaluate: true, + inline: true, + join_vars: true, + keep_fargs: false, + loops: true, + side_effects: true, + unused: true, + } + input: { + "use strict"; + try { + (function() { + var a; + for (; 1;) + a = function() { + (function f([ b ]) { + b; + throw "PASS"; + })([]); + }(); + })(); + } catch (e) { + console.log(e); + } + } + expect: { + "use strict"; + try { + (function() { + for (;;) + throw "PASS"; + })(); + } catch (e) { + console.log(e); + } + } + expect_stdout: "PASS" + node_version: ">=6" +} + +issue_5573: { + options = { + collapse_vars: true, + } + input: { + var log = console.log; + var a = "FAIL"; + (function([ { [log(a)]: b } ]) { + A = 42; + })((a = "PASS", [ {} ])); + log(a, A); + } + expect: { + var log = console.log; + var a = "FAIL"; + (function([ { [log(a)]: b } ]) { + A = 42; + })((a = "PASS", [ {} ])); + log(a, A); + } + expect_stdout: [ + "PASS", + "PASS 42", + ] + node_version: ">=6" +} + +issue_5651: { + options = { + ie: true, + unused: true, + } + input: { + console.log(function arguments({}) { + try {} catch (arguments) { + var arguments; + } + return arguments[0]; + }("PASS")); + } + expect: { + console.log(function arguments({}) { + try {} catch (arguments) { + var arguments; + } + return arguments[0]; + }("PASS")); + } + expect_stdout: true + node_version: ">=6" +} diff --git a/test/compress/directives.js b/test/compress/directives.js new file mode 100644 index 00000000000..0d1ad31e033 --- /dev/null +++ b/test/compress/directives.js @@ -0,0 +1,175 @@ +simple_statement_is_not_a_directive: { + input: { + "use strict" + .split(" ") + .forEach(function(s) { + console.log(s); + }); + console.log(!this); // is strict mode? + (function() { + "directive" + "" + "use strict" + "hello world" + .split(" ") + .forEach(function(s) { + console.log(s); + }); + console.log(!this); // is strict mode? + })(); + } + expect: { + "use strict".split(" ").forEach(function(s) { + console.log(s); + }); + console.log(!this); + (function() { + "directive"; + ""; + "use strict"; + "hello world".split(" ").forEach(function(s) { + console.log(s); + }); + console.log(!this); + })(); + } + expect_stdout: [ + "use", + "strict", + "false", + "hello", + "world", + "true", + ] +} + +drop_lone_use_strict: { + options = { + directives: true, + unused: true, + } + input: { + function f1() { + "use strict"; + } + function f2() { + "use strict"; + function f3() { + "use strict"; + } + } + (function f4() { + "use strict"; + })(); + } + expect: { + function f1() { + } + function f2() { + } + (function() {})(); + } +} + +issue_3166: { + options = { + directives: true, + } + input: { + "foo"; + "use strict"; + function f() { + "use strict"; + "bar"; + "use asm"; + } + } + expect: { + "use strict"; + function f() { + "use asm"; + } + } +} + +valid_after_invalid_1: { + input: { + console.log(typeof function() { + "use\x20strict"; + "use strict"; + return this; + }()); + } + expect: { + console.log(typeof function() { + "use\x20strict"; + "use strict"; + return this; + }()); + } + expect_stdout: "undefined" +} + +valid_after_invalid_2: { + options = { + directives: true, + } + input: { + console.log(typeof function() { + "use\x20strict"; + "use strict"; + return this; + }()); + } + expect: { + console.log(typeof function() { + "use strict"; + return this; + }()); + } + expect_stdout: "undefined" +} + +issue_5368_1: { + expression = true + options = { + directives: true, + expression: true, + } + input: { + "foo" + } + expect_exact: '"foo"' + expect_stdout: "foo" +} + +issue_5368_2: { + expression = true + options = { + directives: true, + expression: true, + } + input: { + (function() { + "foo"; + })() + } + expect_exact: "function(){}()" + expect_stdout: "undefined" +} + +issue_5368_3: { + options = { + directives: true, + expression: true, + } + input: { + "foo"; + (function() { + "bar"; + })(); + } + expect: { + (function() {})(); + } +} diff --git a/test/compress/drop-console.js b/test/compress/drop-console.js new file mode 100644 index 00000000000..9a91edcb453 --- /dev/null +++ b/test/compress/drop-console.js @@ -0,0 +1,26 @@ +drop_console_1: { + options = {} + input: { + console.log('foo'); + console.log.apply(console, arguments); + } + expect: { + console.log('foo'); + console.log.apply(console, arguments); + } +} + +drop_console_2: { + options = { + drop_console: true, + } + input: { + console.log('foo'); + console.log.apply(console, arguments); + } + expect: { + // with regular compression these will be stripped out as well + void 0; + void 0; + } +} diff --git a/test/compress/drop-unused.js b/test/compress/drop-unused.js index de4b222086e..fffa69419f3 100644 --- a/test/compress/drop-unused.js +++ b/test/compress/drop-unused.js @@ -1,33 +1,43 @@ unused_funarg_1: { - options = { unused: true }; + options = { + keep_fargs: false, + unused: true, + } input: { - function f(a, b, c, d, e) { + console.log(function f(a, b, c, d, e) { return a + b; - } + }(14, 28)); } expect: { - function f(a, b) { + console.log(function(a, b) { return a + b; - } + }(14, 28)); } + expect_stdout: "42" } unused_funarg_2: { - options = { unused: true }; + options = { + keep_fargs: false, + unused: true, + } input: { - function f(a, b, c, d, e) { + console.log(function f(a, b, c, d, e) { return a + c; - } + }(14, 21, 28)); } expect: { - function f(a, b, c) { + console.log(function(a, c) { return a + c; - } + }(14, 28)); } + expect_stdout: "42" } unused_nested_function: { - options = { unused: true }; + options = { + unused: true, + } input: { function f(x, y) { function g() { @@ -44,7 +54,9 @@ unused_nested_function: { } unused_circular_references_1: { - options = { unused: true }; + options = { + unused: true, + } input: { function f(x, y) { // circular reference @@ -65,7 +77,9 @@ unused_circular_references_1: { } unused_circular_references_2: { - options = { unused: true }; + options = { + unused: true, + } input: { function f(x, y) { var foo = 1, bar = baz, baz = foo + bar, qwe = moo(); @@ -81,7 +95,9 @@ unused_circular_references_2: { } unused_circular_references_3: { - options = { unused: true }; + options = { + unused: true, + } input: { function f(x, y) { var g = function() { return h() }; @@ -97,7 +113,9 @@ unused_circular_references_3: { } unused_keep_setter_arg: { - options = { unused: true }; + options = { + unused: true, + } input: { var x = { _foo: null, @@ -121,12 +139,14 @@ unused_keep_setter_arg: { } unused_var_in_catch: { - options = { unused: true }; + options = { + unused: true, + } input: { function foo() { try { foo(); - } catch(ex) { + } catch (ex) { var x = 10; } } @@ -135,18 +155,20 @@ unused_var_in_catch: { function foo() { try { foo(); - } catch(ex) {} + } catch (ex) {} } } } used_var_in_catch: { - options = { unused: true }; + options = { + unused: true, + } input: { function foo() { try { foo(); - } catch(ex) { + } catch (ex) { var x = 10; } return x; @@ -156,7 +178,7 @@ used_var_in_catch: { function foo() { try { foo(); - } catch(ex) { + } catch (ex) { var x = 10; } return x; @@ -165,7 +187,11 @@ used_var_in_catch: { } keep_fnames: { - options = { unused: true, keep_fnames: true }; + options = { + keep_fnames: true, + unsafe: true, + unused: true, + } input: { function foo() { return function bar(baz) {}; @@ -173,7 +199,3618 @@ keep_fnames: { } expect: { function foo() { - return function bar() {}; + return function bar(baz) {}; + } + } +} + +drop_assign: { + options = { + unused: true, + } + input: { + function f1() { + var a; + a = 1; + } + function f2() { + var a = 1; + a = 2; + } + function f3(a) { + a = 1; + } + function f4() { + var a; + return a = 1; + } + function f5() { + var a; + return function() { + a = 1; + }; + } + } + expect: { + function f1() { + 1; + } + function f2() { + 2; + } + function f3(a) { + 1; + } + function f4() { + return 1; + } + function f5() { + return function() { + 1; + }; + } + } +} + +keep_assign: { + options = { + unused: "keep_assign", + } + input: { + function f1() { + var a; + a = 1; + } + function f2() { + var a = 1; + a = 2; + } + function f3(a) { + a = 1; + } + function f4() { + var a; + return a = 1; + } + function f5() { + var a; + return function() { + a = 1; + }; + } + } + expect: { + function f1() { + var a; + a = 1; + } + function f2() { + var a = 1; + a = 2; + } + function f3(a) { + a = 1; + } + function f4() { + var a; + return a = 1; + } + function f5() { + var a; + return function() { + a = 1; + }; + } + } +} + +drop_toplevel_funcs: { + options = { + toplevel: "funcs", + unused: true, + } + input: { + var a, b = 1, c = g; + function f(d) { + return function() { + c = 2; + }; + } + a = 2; + function g() {} + function h() {} + console.log(b = 3); + } + expect: { + var a, b = 1, c = g; + a = 2; + function g() {} + console.log(b = 3); + } +} + +drop_toplevel_vars: { + options = { + toplevel: "vars", + unused: true, + } + input: { + var a, b = 1, c = g; + function f(d) { + return function() { + c = 2; + }; + } + a = 2; + function g() {} + function h() {} + console.log(b = 3); + } + expect: { + function f(d) { + return function() { + 2; + }; + } + 2; + function g() {} + function h() {} + console.log(3); + } +} + +drop_toplevel_all: { + options = { + toplevel: true, + unused: true, + } + input: { + var a, b = 1, c = g; + function f(d) { + return function() { + c = 2; + }; + } + a = 2; + function g() {} + function h() {} + console.log(b = 3); + } + expect: { + 2; + console.log(3); + } +} + +drop_toplevel_retain: { + options = { + top_retain: "f,a,o", + unused: true, + } + input: { + var a, b = 1, c = g; + function f(d) { + return function() { + c = 2; + }; + } + a = 2; + function g() {} + function h() {} + console.log(b = 3); + } + expect: { + var a; + function f(d) { + return function() { + 2; + }; + } + a = 2; + console.log(3); + } + expect_stdout: "3" + expect_warnings: [ + "INFO: Retaining variable a", + "INFO: Retaining variable f", + "INFO: Dropping unused variable b [test/compress/drop-unused.js:1,15]", + "INFO: Dropping unused variable c [test/compress/drop-unused.js:1,22]", + "INFO: Dropping unused function g [test/compress/drop-unused.js:8,17]", + "WARN: Dropping unused function h [test/compress/drop-unused.js:9,17]", + ] +} + +drop_toplevel_retain_array: { + options = { + top_retain: [ + "f", + "a", + "o" + ], + unused: true, + } + input: { + var a, b = 1, c = g; + function f(d) { + return function() { + c = 2; + }; + } + a = 2; + function g() {} + function h() {} + console.log(b = 3); + } + expect: { + var a; + function f(d) { + return function() { + 2; + }; + } + a = 2; + console.log(3); + } + expect_stdout: "3" + expect_warnings: [ + "INFO: Retaining variable a", + "INFO: Retaining variable f", + "INFO: Dropping unused variable b [test/compress/drop-unused.js:1,15]", + "INFO: Dropping unused variable c [test/compress/drop-unused.js:1,22]", + "INFO: Dropping unused function g [test/compress/drop-unused.js:8,17]", + "WARN: Dropping unused function h [test/compress/drop-unused.js:9,17]", + ] +} + +drop_toplevel_retain_regex: { + options = { + top_retain: /^[fao]$/, + unused: true, + } + input: { + var a, b = 1, c = g; + function f(d) { + return function() { + c = 2; + }; + } + a = 2; + function g() {} + function h() {} + console.log(b = 3); + } + expect: { + var a; + function f(d) { + return function() { + 2; + }; + } + a = 2; + console.log(3); + } + expect_stdout: "3" + expect_warnings: [ + "INFO: Retaining variable a", + "INFO: Retaining variable f", + "INFO: Dropping unused variable b [test/compress/drop-unused.js:1,15]", + "INFO: Dropping unused variable c [test/compress/drop-unused.js:1,22]", + "INFO: Dropping unused function g [test/compress/drop-unused.js:8,17]", + "WARN: Dropping unused function h [test/compress/drop-unused.js:9,17]", + ] +} + +drop_toplevel_all_retain: { + options = { + top_retain: "f,a,o", + toplevel: true, + unused: true, + } + input: { + var a, b = 1, c = g; + function f(d) { + return function() { + c = 2; + }; + } + a = 2; + function g() {} + function h() {} + console.log(b = 3); + } + expect: { + var a; + function f(d) { + return function() { + 2; + }; + } + a = 2; + console.log(3); + } + expect_stdout: "3" + expect_warnings: [ + "INFO: Retaining variable a", + "INFO: Retaining variable f", + "INFO: Dropping unused variable b [test/compress/drop-unused.js:1,15]", + "INFO: Dropping unused variable c [test/compress/drop-unused.js:1,22]", + "INFO: Dropping unused function g [test/compress/drop-unused.js:8,17]", + "WARN: Dropping unused function h [test/compress/drop-unused.js:9,17]", + ] +} + +drop_toplevel_funcs_retain: { + options = { + top_retain: "f,a,o", + toplevel: "funcs", + unused: true, + } + input: { + var a, b = 1, c = g; + function f(d) { + return function() { + c = 2; + }; + } + a = 2; + function g() {} + function h() {} + console.log(b = 3); + } + expect: { + var a, b = 1, c = g; + function f(d) { + return function() { + c = 2; + }; + } + a = 2; + function g() {} + console.log(b = 3); + } + expect_stdout: "3" + expect_warnings: [ + "INFO: Retaining variable a", + "INFO: Retaining variable f", + "WARN: Dropping unused function h [test/compress/drop-unused.js:9,17]", + ] +} + +drop_toplevel_vars_retain: { + options = { + top_retain: "f,a,o", + toplevel: "vars", + unused: true, + } + input: { + var a, b = 1, c = g; + function f(d) { + return function() { + c = 2; + }; + } + a = 2; + function g() {} + function h() {} + console.log(b = 3); + } + expect: { + var a; + function f(d) { + return function() { + 2; + }; + } + a = 2; + function g() {} + function h() {} + console.log(3); + } + expect_stdout: "3" + expect_warnings: [ + "INFO: Retaining variable a", + "INFO: Retaining variable f", + "INFO: Dropping unused variable b [test/compress/drop-unused.js:1,15]", + "INFO: Dropping unused variable c [test/compress/drop-unused.js:1,22]", + ] +} + +drop_toplevel_keep_assign: { + options = { + toplevel: true, + unused: "keep_assign", + } + input: { + var a, b = 1, c = g; + function f(d) { + return function() { + c = 2; + }; + } + a = 2; + function g() {} + function h() {} + console.log(b = 3); + } + expect: { + var a, b = 1; + a = 2; + console.log(b = 3); + } +} + +drop_fargs: { + options = { + keep_fargs: false, + unused: true, + } + input: { + console.log(function f(a) { + var b = a; + }()); + } + expect: { + console.log(function() {}()); + } + expect_stdout: "undefined" +} + +drop_fnames: { + options = { + keep_fnames: false, + unused: true, + } + input: { + function f() { + return function g() { + var a = g; + }; + } + } + expect: { + function f() { + return function() {}; + } + } +} + +global_var: { + options = { + side_effects: true, + unused: true, + } + input: { + var a; + function foo(b) { + a; + b; + c; + typeof c === "undefined"; + c + b + a; + b && b.ar(); + return b; + } + } + expect: { + var a; + function foo(b) { + c; + c; + b && b.ar(); + return b; + } + } +} + +iife: { + options = { + side_effects: true, + unused: true, + } + input: { + function f() { + var a; + ~function() {}(b); + } + } + expect: { + function f() { + b; + } + } +} + +drop_instanceof: { + options = { + booleans: true, + toplevel: true, + unused: true, + } + input: { + function f() {} + console.log({} instanceof f, Math instanceof f); + } + expect: { + console.log(!1, (Math, !1)); + } + expect_stdout: "false false" +} + +keep_instanceof_1: { + options = { + toplevel: true, + unused: true, + } + input: { + function f() {} + var f; + console.log({} instanceof f, Math instanceof f); + } + expect: { + function f() {} + var f; + console.log({} instanceof f, Math instanceof f); + } + expect_stdout: "false false" +} + +keep_instanceof_2: { + options = { + toplevel: true, + unused: true, + } + input: { + function f() {} + var f = Object; + console.log({} instanceof f, Math instanceof f); + } + expect: { + function f() {} + var f = Object; + console.log({} instanceof f, Math instanceof f); + } + expect_stdout: "true true" +} + +keep_instanceof_3: { + options = { + toplevel: true, + unused: true, + } + input: { + f = Object; + function f() {} + console.log({} instanceof f, Math instanceof f); + } + expect: { + f = Object; + function f() {} + console.log({} instanceof f, Math instanceof f); + } + expect_stdout: "true true" +} + +issue_1539: { + options = { + collapse_vars: true, + sequences: true, + side_effects: true, + unused: true, + } + input: { + function f() { + var a, b; + a = b = 42; + return a; + } + } + expect: { + function f() { + return 42; + } + } +} + +vardef_value: { + options = { + keep_fnames: false, + reduce_funcs: true, + reduce_vars: true, + unused: true, + } + input: { + function f() { + function g(){ + return x(); + } + var a = g(); + return a(42); + } + } + expect: { + function f() { + var a = function(){ + return x(); + }(); + return a(42); + } + } +} + +assign_binding: { + options = { + collapse_vars: true, + side_effects: true, + unused: true, + } + input: { + function f() { + var a; + a = f.g, a(); + } + } + expect: { + function f() { + (0, f.g)(); + } + } +} + +assign_chain: { + options = { + unused: true, + } + input: { + function f() { + var a, b; + x = a = y = b = 42; + } + } + expect: { + function f() { + x = y = 42; + } + } +} + +issue_1583: { + options = { + keep_fargs: true, + passes: 2, + reduce_funcs: true, + reduce_vars: true, + unused: true, + } + input: { + function m(t) { + (function(e) { + t = e(); + })(function() { + return (function(a) { + return a; + })(function(a) {}); + }); + } + } + expect: { + function m(t) { + (function(e) { + (function() { + return (function(a) { + return function(a) {}; + })(); + })(); + })(); } } -} \ No newline at end of file +} + +issue_1656: { + options = { + toplevel: true, + unused: true, + } + beautify = { + beautify: true, + } + input: { + for (var a=0;;); + } + expect_exact: "for (;;);" +} + +issue_1709: { + options = { + unused: true, + } + input: { + console.log( + function x() { + var x = 1; + return x; + }(), + function z() { + function z() {} + return z; + }() + ); + } + expect: { + console.log( + function() { + var x = 1; + return x; + }(), + function() { + function z() {} + return z; + }() + ); + } + expect_stdout: true +} + +issue_1715_1: { + options = { + unused: true, + } + input: { + var a = 1; + function f() { + a++; + try { + x(); + } catch (a) { + var a; + } + } + f(); + console.log(a); + } + expect: { + var a = 1; + function f() { + a++; + try { + x(); + } catch (a) { + var a; + } + } + f(); + console.log(a); + } + expect_stdout: "1" +} + +issue_1715_2: { + options = { + unused: true, + } + input: { + var a = 1; + function f() { + a++; + try { + x(); + } catch (a) { + var a = 2; + } + } + f(); + console.log(a); + } + expect: { + var a = 1; + function f() { + a++; + try { + x(); + } catch (a) { + var a; + } + } + f(); + console.log(a); + } + expect_stdout: "1" +} + +issue_1715_3: { + options = { + unused: true, + } + input: { + var a = 1; + function f() { + a++; + try { + console; + } catch (a) { + var a = 2 + x(); + } + } + f(); + console.log(a); + } + expect: { + var a = 1; + function f() { + a++; + try { + console; + } catch (a) { + var a; + x(); + } + } + f(); + console.log(a); + } + expect_stdout: "1" +} + +issue_1715_4: { + options = { + unused: true, + } + input: { + var a = 1; + !function a() { + a++; + try { + x(); + } catch (a) { + var a; + } + }(); + console.log(a); + } + expect: { + var a = 1; + !function() { + a++; + try { + x(); + } catch (a) { + var a; + } + }(); + console.log(a); + } + expect_stdout: "1" +} + +delete_assign_1: { + options = { + booleans: true, + evaluate: true, + toplevel: true, + unused: true, + } + input: { + var a; + console.log(delete (a = undefined)); + console.log(delete (a = void 0)); + console.log(delete (a = Infinity)); + console.log(delete (a = 1 / 0)); + console.log(delete (a = NaN)); + console.log(delete (a = 0 / 0)); + } + expect: { + console.log(!0); + console.log(!0); + console.log(!0); + console.log(!0); + console.log(!0); + console.log(!0); + } + expect_stdout: [ + "true", + "true", + "true", + "true", + "true", + "true", + ] +} + +delete_assign_2: { + options = { + booleans: true, + evaluate: true, + keep_infinity: true, + toplevel: true, + unused: true, + } + input: { + var a; + console.log(delete (a = undefined)); + console.log(delete (a = void 0)); + console.log(delete (a = Infinity)); + console.log(delete (a = 1 / 0)); + console.log(delete (a = NaN)); + console.log(delete (a = 0 / 0)); + } + expect: { + console.log(!0); + console.log(!0); + console.log(!0); + console.log(!0); + console.log(!0); + console.log(!0); + } + expect_stdout: [ + "true", + "true", + "true", + "true", + "true", + "true", + ] +} + +drop_var: { + options = { + toplevel: true, + unused: true, + } + input: { + var a; + console.log(a, b); + var a = 1, b = 2; + console.log(a, b); + var a = 3; + console.log(a, b); + } + expect: { + console.log(a, b); + var a = 1, b = 2; + console.log(a, b); + a = 3; + console.log(a, b); + } + expect_stdout: [ + "undefined undefined", + "1 2", + "3 2", + ] +} + +issue_1830_1: { + options = { + unused: true, + } + input: { + !function() { + L: for (var b = console.log(1); !1;) continue L; + }(); + } + expect: { + !function() { + L: for (console.log(1); !1;) continue L; + }(); + } + expect_stdout: "1" +} + +issue_1830_2: { + options = { + unused: true, + } + input: { + !function() { + L: for (var a = 1, b = console.log(a); --a;) continue L; + }(); + } + expect: { + !function() { + var a = 1; + L: for (console.log(a); --a;) continue L; + }(); + } + expect_stdout: "1" +} + +issue_1838: { + options = { + join_vars: true, + loops: true, + unused: true, + } + beautify = { + beautify: true, + } + input: { + function f() { + var b = a; + while (c); + } + } + expect_exact: [ + "function f() {", + " for (a; c; );", + "}", + ] +} + +var_catch_toplevel: { + options = { + conditionals: true, + negate_iife: true, + passes: 2, + reduce_funcs: true, + reduce_vars: true, + side_effects: true, + toplevel: true, + unused: true, + } + input: { + function f() { + a--; + try { + a++; + x(); + } catch (a) { + if (a) var a; + var a = 10; + } + } + f(); + } + expect: { + !function() { + try { + x(); + } catch (a) { + var a; + } + }(); + } +} + +issue_2105_1: { + options = { + collapse_vars: true, + inline: true, + passes: 3, + reduce_funcs: true, + reduce_vars: true, + side_effects: true, + unused: true, + } + input: { + !function(factory) { + factory(); + }(function() { + return function(fn) { + fn()().prop(); + }(function() { + function bar() { + var quux = function() { + console.log("PASS"); + }, foo = function() { + console.log; + quux(); + }; + return { prop: foo }; + } + return bar; + }); + }); + } + expect: { + ({ + prop: function() { + console.log; + console.log("PASS"); + } + }).prop(); + } + expect_stdout: "PASS" +} + +issue_2105_2: { + options = { + collapse_vars: true, + inline: true, + passes: 3, + properties: true, + pure_getters: "strict", + reduce_funcs: true, + reduce_vars: true, + side_effects: true, + unsafe: true, + unused: true, + } + input: { + !function(factory) { + factory(); + }(function() { + return function(fn) { + fn()().prop(); + }(function() { + function bar() { + var quux = function() { + console.log("PASS"); + }, foo = function() { + console.log; + quux(); + }; + return { prop: foo }; + } + return bar; + }); + }); + } + expect: { + console.log("PASS"); + } + expect_stdout: "PASS" +} + +issue_2105_3: { + options = { + inline: true, + join_vars: true, + passes: 3, + reduce_vars: true, + side_effects: true, + sequences: true, + unused: true, + } + input: { + !function(factory) { + factory(); + }(function() { + return function(fn) { + fn()().prop(); + }(function() { + function bar() { + var quux = function() { + console.log("PASS"); + }, foo = function() { + console.log; + quux(); + }; + return { prop: foo }; + } + return bar; + }); + }); + } + expect: { + ({ + prop: function() { + console.log, + console.log("PASS"); + }, + }).prop(); + } + expect_stdout: "PASS" +} + +issue_2226_1: { + options = { + side_effects: true, + unused: true, + } + input: { + function f1() { + var a = b; + a += c; + } + function f2(a) { + a <<= b; + } + function f3(a) { + --a; + } + function f4() { + var a = b; + return a *= c; + } + function f5(a) { + x(a /= b); + } + } + expect: { + function f1() { + b; + c; + } + function f2(a) { + b; + } + function f3(a) { + 0; + } + function f4() { + var a = b; + return a *= c; + } + function f5(a) { + x(a /= b); + } + } +} + +issue_2226_2: { + options = { + collapse_vars: true, + sequences: true, + side_effects: true, + unused: true, + } + input: { + console.log(function(a, b) { + a += b; + return a; + }(1, 2)); + } + expect: { + console.log(function(a, b) { + return a += 2; + }(1)); + } + expect_stdout: "3" +} + +issue_2226_3: { + options = { + collapse_vars: true, + side_effects: true, + unused: true, + } + input: { + console.log(function(a, b) { + a += b; + return a; + }(1, 2)); + } + expect: { + console.log(function(a, b) { + return a += 2; + }(1)); + } + expect_stdout: "3" +} + +issue_2288: { + options = { + unused: true, + } + beautify = { + beautify: true, + } + input: { + function foo(o) { + for (var j = o.a, i = 0; i < 0; i++); + for (var i = 0; i < 0; i++); + } + } + expect: { + function foo(o) { + o.a; + for (var i = 0; i < 0; i++); + for (i = 0; i < 0; i++); + } + } +} + +issue_2516_1: { + options = { + collapse_vars: true, + reduce_funcs: true, + reduce_vars: true, + unused: true, + } + input: { + function foo() { + function qux(x) { + bar.call(null, x); + } + function bar(x) { + var FOUR = 4; + var trouble = x || never_called(); + var value = (FOUR - 1) * trouble; + console.log(value == 6 ? "PASS" : value); + } + Baz = qux; + } + var Baz; + foo(); + Baz(2); + } + expect: { + function foo() { + Baz = function(x) { + (function(x) { + var trouble = x || never_called(); + var value = (4 - 1) * trouble; + console.log(6 == value ? "PASS" : value); + }).call(null, x); + }; + } + var Baz; + foo(); + Baz(2); + } +} + +issue_2516_2: { + options = { + collapse_vars: true, + passes: 2, + reduce_funcs: true, + reduce_vars: true, + unused: true, + } + input: { + function foo() { + function qux(x) { + bar.call(null, x); + } + function bar(x) { + var FOUR = 4; + var trouble = x || never_called(); + var value = (FOUR - 1) * trouble; + console.log(value == 6 ? "PASS" : value); + } + Baz = qux; + } + var Baz; + foo(); + Baz(2); + } + expect: { + function foo() { + Baz = function(x) { + (function(x) { + var value = (4 - 1) * (x || never_called()); + console.log(6 == value ? "PASS" : value); + }).call(null, x); + }; + } + var Baz; + foo(); + Baz(2); + } +} + +defun_lambda_same_name: { + options = { + toplevel: true, + unused: true, + } + input: { + function f(n) { + return n ? n * f(n - 1) : 1; + } + console.log(function f(n) { + return n ? n * f(n - 1) : 1; + }(5)); + } + expect: { + console.log(function f(n) { + return n ? n * f(n - 1) : 1; + }(5)); + } + expect_stdout: "120" +} + +issue_2660_1: { + options = { + reduce_vars: true, + side_effects: true, + toplevel: true, + unused: true, + } + input: { + var a = 2; + function f(b) { + return b && f() || a--; + } + f(1); + console.log(a); + } + expect: { + var a = 2; + (function f(b) { + return b && f() || a--; + })(1); + console.log(a); + } + expect_stdout: "1" +} + +issue_2660_2: { + options = { + collapse_vars: true, + reduce_vars: true, + sequences: true, + side_effects: true, + toplevel: true, + unused: true, + } + input: { + var a = 1; + function f(b) { + b && f(); + --a, a.toString(); + } + f(); + console.log(a); + } + expect: { + var a = 1; + (function f(b) { + b && f(), + (--a).toString(); + })(), + console.log(a); + } + expect_stdout: "0" +} + +issue_2665: { + options = { + evaluate: true, + inline: true, + keep_fargs: false, + passes: 2, + reduce_funcs: true, + reduce_vars: true, + side_effects: true, + toplevel: true, + typeofs: true, + unused: true, + } + input: { + var a = 1; + function g() { + a-- && g(); + } + typeof h == "function" && h(); + function h() { + typeof g == "function" && g(); + } + console.log(a); + } + expect: { + var a = 1; + (function g() { + a-- && g(); + })(); + console.log(a); + } + expect_stdout: "-1" +} + +double_assign_1: { + options = { + passes: 2, + reduce_vars: true, + side_effects: true, + unused: true, + } + input: { + function f1() { + var a = {}; + var a = []; + return a; + } + function f2() { + var a = {}; + a = []; + return a; + } + function f3() { + a = {}; + var a = []; + return a; + } + function f4(a) { + a = {}; + a = []; + return a; + } + function f5(a) { + var a = {}; + a = []; + return a; + } + function f6(a) { + a = {}; + var a = []; + return a; + } + console.log(f1(), f2(), f3(), f4(), f5(), f6()); + } + expect: { + function f1() { + return []; + } + function f2() { + var a; + a = []; + return a; + } + function f3() { + return []; + } + function f4(a) { + a = []; + return a; + } + function f5(a) { + a = []; + return a; + } + function f6(a) { + a = []; + return a; + } + console.log(f1(), f2(), f3(), f4(), f5(), f6()); + } + expect_stdout: true +} + +double_assign_2: { + options = { + reduce_vars: true, + toplevel: true, + unused: true, + } + input: { + for (var i = 0; i < 2; i++) + a = void 0, a = {}, console.log(a); + var a; + } + expect: { + for (var i = 0; i < 2; i++) + a = {}, console.log(a); + var a; + } +} + +double_assign_3: { + options = { + reduce_vars: true, + toplevel: true, + unused: true, + } + input: { + for (var i = 0; i < 2; i++) + a = void 0, a = { a: a }, console.log(a); + var a; + } + expect: { + for (var i = 0; i < 2; i++) + a = void 0, a = { a: a }, console.log(a); + var a; + } +} + +cascade_drop_assign: { + options = { + reduce_vars: true, + toplevel: true, + unused: true, + } + input: { + var a, b = a = "PASS"; + console.log(b); + } + expect: { + var b = "PASS"; + console.log(b); + } + expect_stdout: "PASS" +} + +chained_3: { + options = { + reduce_vars: true, + unused: true, + } + input: { + console.log(function(a, b) { + var c = a, c = b; + b++; + return c; + }(1, 2)); + } + expect: { + console.log(function(a, b) { + var c = b; + +b; + return c; + }(0, 2)); + } + expect_stdout: "2" +} + +issue_2768: { + options = { + inline: true, + reduce_vars: true, + sequences: true, + side_effects: true, + toplevel: true, + unused: true, + } + input: { + var a = "FAIL", c = 1; + var c = function(b) { + var d = b = a; + var e = --b + (d && (a = "PASS")); + }(); + console.log(a, typeof c); + } + expect: { + var a = "FAIL"; + d = a; + var c = void (d && (a = "PASS")); + var d; + console.log(a, typeof c); + } + expect_stdout: "PASS undefined" +} + +issue_2846: { + options = { + collapse_vars: true, + reduce_vars: true, + toplevel: true, + unused: true, + } + input: { + function f(a, b) { + var a = 0; + b && b(a); + return a++; + } + var c = f(); + console.log(c); + } + expect: { + var c = function(a, b) { + a = 0; + b && b(a); + return +a; + }(); + console.log(c); + } + expect_stdout: "0" +} + +issue_805_1: { + options = { + inline: true, + passes: 3, + pure_getters: "strict", + reduce_vars: true, + sequences: true, + side_effects: true, + unused: true, + } + input: { + (function(a) { + var unused = function() {}; + unused.prototype[a()] = 42; + (unused.prototype.bar = function() { + console.log("bar"); + })(); + return unused; + })(function() { + console.log("foo"); + return "foo"; + }); + } + expect: { + console.log("foo"), + console.log("bar"); + } + expect_stdout: [ + "foo", + "bar", + ] +} + +issue_805_2: { + options = { + inline: true, + passes: 3, + pure_getters: "strict", + reduce_vars: true, + sequences: true, + side_effects: true, + unused: true, + } + input: { + (function(a) { + function unused() {} + unused.prototype[a()] = 42; + (unused.prototype.bar = function() { + console.log("bar"); + })(); + return unused; + })(function() { + console.log("foo"); + return "foo"; + }); + } + expect: { + console.log("foo"), + console.log("bar"); + } + expect_stdout: [ + "foo", + "bar", + ] +} + +issue_2995: { + options = { + pure_getters: "strict", + reduce_vars: true, + unused: true, + } + input: { + function f(a) { + var b; + a.b = b = function() {}; + b.c = "PASS"; + } + var o = {}; + f(o); + console.log(o.b.c); + } + expect: { + function f(a) { + var b; + a.b = b = function() {}; + b.c = "PASS"; + } + var o = {}; + f(o); + console.log(o.b.c); + } + expect_stdout: "PASS" +} + +issue_3146_1: { + options = { + collapse_vars: true, + unused: true, + } + input: { + (function(f) { + f("g()"); + })(function(a) { + eval(a); + function g(b) { + if (!b) b = "PASS"; + console.log(b); + } + }); + } + expect: { + (function(f) { + f("g()"); + })(function(a) { + eval(a); + function g(b) { + if (!b) b = "PASS"; + console.log(b); + } + }); + } + expect_stdout: "PASS" +} + +issue_3146_2: { + options = { + reduce_vars: true, + unused: true, + } + input: { + (function(f) { + f("g()"); + })(function(a) { + eval(a); + function g(b) { + if (!b) b = "PASS"; + console.log(b); + } + }); + } + expect: { + (function(f) { + f("g()"); + })(function(a) { + eval(a); + function g(b) { + if (!b) b = "PASS"; + console.log(b); + } + }); + } + expect_stdout: "PASS" +} + +issue_3146_3: { + options = { + collapse_vars: true, + unused: true, + } + input: { + var g = "PASS"; + (function(f) { + var g = "FAIL"; + f("console.log(g)", g[g]); + })(function(a) { + eval(a); + }); + } + expect: { + var g = "PASS"; + (function(f) { + var g = "FAIL"; + f("console.log(g)", g[g]); + })(function(a) { + eval(a); + }); + } + expect_stdout: "PASS" +} + +issue_3146_4: { + options = { + reduce_vars: true, + unused: true, + } + input: { + var g = "PASS"; + (function(f) { + var g = "FAIL"; + f("console.log(g)", g[g]); + })(function(a) { + eval(a); + }); + } + expect: { + var g = "PASS"; + (function(f) { + var g = "FAIL"; + f("console.log(g)", g[g]); + })(function(a) { + eval(a); + }); + } + expect_stdout: "PASS" +} + +issue_3192_1: { + options = { + unused: true, + } + input: { + (function(a) { + console.log(a = "foo", arguments[0]); + })("bar"); + (function(a) { + "use strict"; + console.log(a = "foo", arguments[0]); + })("bar"); + } + expect: { + (function(a) { + console.log(a = "foo", arguments[0]); + })("bar"); + (function(a) { + "use strict"; + console.log("foo", arguments[0]); + })("bar"); + } + expect_stdout: [ + "foo foo", + "foo bar", + ] +} + +issue_3192_2: { + options = { + keep_fargs: false, + unused: true, + } + input: { + "use strict"; + (function(a) { + console.log(a = "foo", arguments[0]); + })("bar"); + } + expect: { + "use strict"; + (function() { + console.log("foo", arguments[0]); + })("bar"); + } + expect_stdout: "foo bar" +} + +issue_3233: { + options = { + pure_getters: "strict", + side_effects: true, + unused: true, + } + input: { + var a = function b() { + b.c = "PASS"; + }; + a(); + console.log(a.c); + } + expect: { + var a = function b() { + b.c = "PASS"; + }; + a(); + console.log(a.c); + } + expect_stdout: "PASS" +} + +issue_3375: { + options = { + reduce_vars: true, + toplevel: true, + unused: true, + } + input: { + var b = 1; + var a = c = [], c = --b + ("function" == typeof f && f()); + var a = c && c[a]; + console.log(a, b); + } + expect: { + var b = 1; + var a = [], c = --b + ("function" == typeof f && f()); + a = c && c[a]; + console.log(a, b); + } + expect_stdout: "0 0" +} + +issue_3427_1: { + options = { + sequences: true, + side_effects: true, + unused: true, + } + input: { + (function() { + var a; + a = a || {}; + })(); + } + expect: {} +} + +issue_3427_2: { + options = { + unused: true, + } + input: { + (function() { + var s = "PASS"; + console.log(s = s || "FAIL"); + })(); + } + expect: { + (function() { + var s = "PASS"; + console.log(s = s || "FAIL"); + })(); + } + expect_stdout: "PASS" +} + +issue_3495: { + options = { + dead_code: true, + pure_getters: "strict", + side_effects: true, + unused: true, + } + input: { + console.log(function f() { + f = 0; + var a = f.p; + }()); + } + expect: { + console.log(void 0); + } + expect_stdout: "undefined" +} + +issue_3497: { + options = { + pure_getters: "strict", + side_effects: true, + unused: true, + } + input: { + var a; + console.log(function(b) { + (b += a).p = 0; + }()); + } + expect: { + var a; + console.log(function(b) { + (b += a).p = 0; + }()); + } + expect_stdout: "undefined" +} + +issue_3515_1: { + options = { + collapse_vars: true, + evaluate: true, + reduce_vars: true, + unused: true, + } + input: { + var c = 0; + (function() { + this[c++] = 0; + var expr20 = !0; + for (var key20 in expr20); + })(); + console.log(c); + } + expect: { + var c = 0; + (function() { + for (var key20 in !(this[c++] = 0)); + })(); + console.log(c); + } + expect_stdout: "1" +} + +issue_3515_2: { + options = { + side_effects: true, + toplevel: true, + unused: true, + } + input: { + var a = "FAIL"; + function f() { + typeof b === "number"; + delete a; + } + var b = f(a = "PASS"); + console.log(a); + } + expect: { + var a = "FAIL"; + function f() { + delete a; + } + f(a = "PASS"); + console.log(a); + } + expect_stdout: "PASS" +} + +issue_3515_3: { + options = { + collapse_vars: true, + evaluate: true, + unused: true, + } + input: { + var c = "FAIL"; + (function() { + function f() { + c = "PASS"; + } + var a = f(); + var a = function g(b) { + b && (b.p = this); + }(a); + })(); + console.log(c); + } + expect: { + var c = "FAIL"; + (function() { + function f() { + c = "PASS"; + } + (function(b) { + b && (b.p = this); + })(f()); + })(); + console.log(c); + } + expect_stdout: "PASS" +} + +function_assign: { + options = { + pure_getters: "strict", + reduce_vars: true, + side_effects: true, + unused: true, + } + input: { + console.log(function() { + var a = "PASS"; + function g(b) { + return b; + } + g.p = a; + function h(c) { + return c; + } + h.p = a; + return h; + }().p); + } + expect: { + console.log(function() { + var a = "PASS"; + function h(c) { + return c; + } + h.p = a; + return h; + }().p); + } + expect_stdout: "PASS" +} + +issue_3598: { + options = { + collapse_vars: true, + evaluate: true, + reduce_vars: true, + unused: true, + } + input: { + var a = "FAIL"; + try { + (function() { + var b = void 0; + a = "PASS"; + c.p = 0; + var c = b[!1]; + })(); + } catch (e) {} + console.log(a); + } + expect: { + var a = "FAIL"; + try { + (function() { + a = "PASS"; + (void ((void 0).p = 0))[!1]; + })(); + } catch (e) {} + console.log(a); + } + expect_stdout: "PASS" +} + +self_assign: { + options = { + passes: 2, + side_effects: true, + unused: true, + } + input: { + function d(a) { + a = a; + } + function e(a, b) { + a = b; + b = a; + } + function f(a, b, c) { + a = b; + b = c; + c = a; + } + function g(a, b, c) { + a = a * b + c; + } + } + expect: { + function d(a) {} + function e(a, b) {} + function f(a, b, c) {} + function g(a, b, c) {} + } +} + +function_argument_reference: { + options = { + keep_fargs: false, + side_effects: true, + unused: true, + } + input: { + var a = 1, b = 42; + function f(a) { + b <<= a; + } + f(); + console.log(a, b); + } + expect: { + var a = 1, b = 42; + function f(a) { + b <<= a; + } + f(); + console.log(a, b); + } + expect_stdout: "1 42" +} + +function_parameter_ie8: { + options = { + ie: true, + reduce_vars: true, + unused: true, + } + input: { + (function() { + var a; + function f() { + console.log("PASS"); + } + f(a = 1 + a); + })(); + } + expect: { + (function() { + (function() { + console.log("PASS"); + })(); + })(); + } + expect_stdout: "PASS" +} + +issue_3664: { + options = { + pure_getters: "strict", + side_effects: true, + unused: true, + } + input: { + console.log(function() { + var a, b = (a = (a = [ b && console.log("FAIL") ]).p = 0, 0); + return "PASS"; + }()); + } + expect: { + console.log(function() { + a = (a = [ b && console.log("FAIL") ]).p = 0; + var a, b = 0; + return "PASS"; + }()); + } + expect_stdout: "PASS" +} + +issue_3673: { + options = { + pure_getters: "strict", + sequences: true, + side_effects: true, + toplevel: true, + unused: true, + } + input: { + var a; + (a = [ a ]).p = 42; + console.log("PASS"); + } + expect: { + console.log("PASS"); + } + expect_stdout: "PASS" +} + +issue_3746: { + options = { + keep_fargs: false, + side_effects: true, + unused: true, + } + input: { + try { + A; + } catch (e) { + var e; + } + (function f(a) { + e = a; + })(); + console.log("PASS"); + } + expect: { + try { + A; + } catch (e) { + var e; + } + (function(a) { + e = a; + })(); + console.log("PASS"); + } + expect_stdout: "PASS" +} + +drop_duplicated_side_effects: { + options = { + toplevel: true, + unused: true, + } + input: { + var a = 0; + for (var i = 1; i--;) + var a = 0, b = ++a; + console.log(a); + } + expect: { + var a = 0; + for (var i = 1; i--;) + a = 0, ++a; + console.log(a); + } + expect_stdout: "1" +} + +drop_duplicated_var_catch: { + options = { + unused: true, + } + input: { + function f() { + try { + x(); + } catch (a) { + var a, a; + } + } + } + expect: { + function f() { + try { + x(); + } catch (a) { + var a; + } + } + } +} + +issue_3802_1: { + options = { + functions: true, + reduce_vars: true, + toplevel: true, + unused: true, + } + input: { + var a = 0; + a += 0; + var a = function() {}; + console.log(typeof a); + } + expect: { + var a = 0; + a += 0; + a = function() {}; + console.log(typeof a); + } + expect_stdout: "function" +} + +issue_3802_2: { + options = { + functions: true, + reduce_vars: true, + side_effects: true, + toplevel: true, + unused: true, + } + input: { + var a = 0; + a += 0; + var a = function() {}; + console.log(typeof a); + } + expect: { + 0; + var a = function() {}; + console.log(typeof a); + } + expect_stdout: "function" +} + +issue_3899: { + options = { + assignments: true, + evaluate: true, + functions: true, + inline: true, + join_vars: true, + passes: 2, + reduce_vars: true, + side_effects: true, + toplevel: true, + unused: true, + } + input: { + var a = 0; + a = a + 1; + var a = function f(b) { + return function() { + return b; + }; + }(2); + console.log(typeof a); + } + expect: { + console.log(typeof function() { + return 2; + }); + } + expect_stdout: "function" +} + +cross_scope_assign_chain: { + options = { + reduce_vars: true, + side_effects: true, + toplevel: true, + unused: true, + } + input: { + var a, b = 0; + (function() { + a = b; + a++; + while (b++); + })(); + console.log(a ? "PASS" : "FAIL"); + } + expect: { + var a, b = 0; + (function() { + a = b; + a++; + while (b++); + })(); + console.log(a ? "PASS" : "FAIL"); + } + expect_stdout: "PASS" +} + +assign_if_assign_read: { + options = { + reduce_vars: true, + unused: true, + } + input: { + (function(a) { + var b; + do { + b = "FAIL"; + if (Array.isArray(a)) { + b = a[0]; + console.log(b); + } + } while (!console); + })([ "PASS" ]); + } + expect: { + (function(a) { + var b; + do { + "FAIL"; + if (Array.isArray(a)) { + b = a[0]; + console.log(b); + } + } while (!console); + })([ "PASS" ]); + } + expect_stdout: "PASS" +} + +issue_3951: { + options = { + pure_getters: "strict", + reduce_vars: true, + side_effects: true, + toplevel: true, + unused: true, + } + input: { + var a = console.log("PASS"); + console.log(a); + a = "0"; + console.log(a.p = 0); + a && a; + } + expect: { + var a = console.log("PASS"); + console.log(a); + a = "0"; + console.log(a.p = 0); + } + expect_stdout: [ + "PASS", + "undefined", + "0", + ] +} + +issue_3956: { + options = { + collapse_vars: true, + evaluate: true, + inline: true, + passes: 3, + reduce_vars: true, + sequences: true, + side_effects: true, + toplevel: true, + unused: true, + } + input: { + (function(a) { + function f(b) { + console.log(b); + a = 1; + } + var c = f(c += 0); + (function(d) { + console.log(d); + })(console.log(a) ^ 1, c); + })(); + } + expect: { + var d; + console.log(NaN), + d = 1 ^ console.log(1), + console.log(d); + } + expect_stdout: [ + "NaN", + "1", + "1", + ] +} + +issue_3962_1: { + options = { + evaluate: true, + keep_fargs: false, + reduce_vars: true, + toplevel: true, + unused: true, + } + input: { + var a = 0; + function f(b, c) { + do { + var d = console + e, e = 0..toString() === b; + } while (0); + if (c) console.log("PASS"); + } + var a = f(a--, 1); + a; + } + expect: { + var a = 0; + (function(c) { + do { + console; + 0..toString(); + } while (0); + if (c) console.log("PASS"); + })(1); + void 0; + } + expect_stdout: "PASS" +} + +issue_3962_2: { + options = { + keep_fargs: false, + reduce_vars: true, + side_effects: true, + toplevel: true, + unused: true, + } + input: { + var a = 0; + function f(b, c) { + do { + var d = console + e, e = 0..toString() === b; + } while (0); + if (c) console.log("PASS"); + } + var a = f(a--, 1); + a; + } + expect: { + var a = 0; + (function(c) { + do { + console; + 0..toString(); + } while (0); + if (c) console.log("PASS"); + })(1); + } + expect_stdout: "PASS" +} + +issue_3986: { + options = { + reduce_vars: true, + side_effects: true, + toplevel: true, + unused: true, + } + input: { + var a = 0, b = 0; + (function() { + try { + throw 42; + } catch (e) { + a++; + } + b = b && 0; + })(b *= a); + console.log(b); + } + expect: { + var a = 0, b = 0; + (function() { + try { + throw 42; + } catch (e) { + a++; + } + b = b && 0; + })(b *= a); + console.log(b); + } + expect_stdout: "0" +} + +issue_4017_1: { + options = { + pure_getters: "strict", + reduce_vars: true, + unused: true, + } + input: { + var a = 0; + console.log(function f() { + var b = c &= 0; + var c = a++ + (A = a); + var d = c && c[f]; + }()); + } + expect: { + var a = 0; + console.log(function() { + c &= 0; + var c = a++ + (A = a); + }()); + } + expect_stdout: "undefined" +} + +issue_4017_2: { + options = { + passes: 2, + pure_getters: "strict", + reduce_vars: true, + unused: true, + } + input: { + var a = 0; + console.log(function f() { + var b = c &= 0; + var c = a++ + (A = a); + var d = c && c[f]; + }()); + } + expect: { + var a = 0; + console.log(function() { + 0; + a++, + A = a; + }()); + } + expect_stdout: "undefined" +} + +issue_4025: { + options = { + collapse_vars: true, + evaluate: true, + passes: 2, + reduce_vars: true, + toplevel: true, + unused: true, + } + input: { + var a = 0, b = 0, c = 0, d = a++; + try { + var e = console.log(c), f = b; + } finally { + var d = b = 1, d = c + 1; + c = 0; + } + console.log(a, b, d); + } + expect: { + try { + console.log(0); + } finally { + 0; + } + console.log(1, 1, 1); + } + expect_stdout: [ + "0", + "1 1 1", + ] +} + +forin_var_1: { + options = { + unused: true, + } + input: { + var k; + for (k in [ 1, 2 ]) + console.log(k); + for (k in { PASS: 3 }) + console.log(k); + console.log(k); + } + expect: { + for (var k in [ 1, 2 ]) + console.log(k); + for (k in { PASS: 3 }) + console.log(k); + console.log(k); + } + expect_stdout: [ + "0", + "1", + "PASS", + "PASS", + ] +} + +forin_var_2: { + options = { + unused: true, + } + input: { + console.log(function() { + switch (0) { + case function() { + for (a in 0); + }: + var b = 0; + } + for (var c = 0; a;); + var a; + }()); + } + expect: { + console.log(function() { + switch (0) { + case function() { + for (a in 0); + }: + } + for (; a;); + var a; + }()); + } + expect_stdout: "undefined" +} + +issue_4133: { + options = { + evaluate: true, + merge_vars: true, + pure_getters: "strict", + reduce_vars: true, + toplevel: true, + unused: true, + } + input: { + var a = 1; + var b = [ a-- ], c = b && b[c]; + console.log(a); + } + expect: { + var a = 1; + console.log(0); + } + expect_stdout: "0" +} + +issue_4144: { + options = { + keep_fargs: false, + reduce_vars: true, + unused: true, + } + input: { + (function(a, b) { + var b = console, c = ++b; + })(console.log("PASS"), 0); + } + expect: { + (function(b) { + b = console, + ++b; + })(console.log("PASS")); + } + expect_stdout: "PASS" +} + +issue_4146: { + options = { + reduce_vars: true, + toplevel: true, + unused: true, + } + input: { + function f(a, b) { + function g() {} + var a = g; + var c = b; + c.p; + console.log(typeof a); + } + f("FAIL", 42); + } + expect: { + (function(a, b) { + a = function () {}; + var c = b; + c.p; + console.log(typeof a); + })(0, 42); + } + expect_stdout: "function" +} + +var_catch_redefined: { + options = { + toplevel: true, + unused: true, + } + input: { + var a = "FAIL"; + try { + throw "PASS"; + } catch (a) { + function f() { + return a; + } + console.log(a); + } + f(); + } + expect: { + var a = "FAIL"; + try { + throw "PASS"; + } catch (a) { + function f() { + return a; + } + console.log(a); + } + f(); + } + expect_stdout: "PASS" +} + +single_use_catch_redefined: { + options = { + reduce_vars: true, + toplevel: true, + unused: true, + } + input: { + var a = 1; + try { + throw 2; + } catch (a) { + function g() { + return a; + } + } + console.log(g()); + } + expect: { + var a = 1; + try { + throw 2; + } catch (a) { + function g() { + return a; + } + } + console.log(g()); + } + expect_stdout: true +} + +issue_4184: { + options = { + reduce_vars: true, + unused: true, + } + input: { + (function() { + var a = function() {}, b = [ a, 1 && b, a = {} ]; + try { + throw 42; + } catch (a) { + { + console.log(a); + } + } + })(); + } + expect: { + (function() { + var b = [ function() {}, 1 && b, {} ]; + try { + throw 42; + } catch (a) { + console.log(a); + } + })(); + } + expect_stdout: "42" +} + +issue_4235_1: { + options = { + inline: true, + reduce_vars: true, + unused: true, + varify: true, + } + input: { + (function() { + { + const f = 0; + } + (function f() { + var f = console.log(f); + })(); + })(); + } + expect: { + void function() { + var f = console.log(f); + }(); + } + expect_stdout: "undefined" +} + +issue_4235_2: { + options = { + inline: true, + passes: 2, + reduce_vars: true, + side_effects: true, + unused: true, + varify: true, + } + input: { + (function() { + { + const f = 0; + } + (function f() { + var f = console.log(f); + })(); + })(); + } + expect: { + console.log(void 0); + } + expect_stdout: "undefined" +} + +issue_4404: { + options = { + pure_getters: "strict", + unused: true, + } + input: { + function f(a) { + arguments[0] = "PASS"; + console.log(a); + } + f("FAIL"); + } + expect: { + function f(a) { + arguments[0] = "PASS"; + console.log(a); + } + f("FAIL"); + } + expect_stdout: "PASS" +} + +issue_4413: { + options = { + reduce_vars: true, + unused: true, + } + input: { + console.log(function f(arguments) { + var arguments = function() {}; + return arguments.length; + }()); + } + expect: { + console.log(function(arguments) { + return function() {}.length; + }()); + } + expect_stdout: "0" +} + +issue_4464_1: { + options = { + reduce_vars: true, + unused: true, + } + input: { + function f(a) { + var a = function() {}; + return [ arguments, a ]; + } + console.log(typeof f()[1]); + } + expect: { + function f(a) { + a = function() {}; + return [ arguments, a ]; + } + console.log(typeof f()[1]); + } + expect_stdout: "function" +} + +issue_4464_2: { + options = { + reduce_vars: true, + unused: true, + } + input: { + function f(a) { + var a = function() {}; + return [ arguments, a ]; + } + console.log(typeof f(42)[0][0]); + } + expect: { + function f(a) { + a = function() {}; + return [ arguments, a ]; + } + console.log(typeof f(42)[0][0]); + } + expect_stdout: "function" +} + +issue_4464_3: { + options = { + reduce_vars: true, + unused: true, + } + input: { + (function a(a) { + var a = function() {}; + return [ arguments[0], a ]; + })(42).forEach(function(b) { + console.log(typeof b); + }); + } + expect: { + (function(a) { + a = function() {}; + return [ arguments[0], a ]; + })(42).forEach(function(b) { + console.log(typeof b); + }); + } + expect_stdout: [ + "function", + "function", + ] +} + +issue_4558_1: { + options = { + evaluate: true, + pure_getters: true, + reduce_vars: true, + sequences: true, + side_effects: true, + toplevel: true, + unused: true, + } + input: { + var a = 0; + var b = 1, b = c >>>= a; + var c = 0; + b && 0[a++], + console.log(a); + } + expect: { + var a = 0; + var b = c >>>= a; + var c = 0; + b && a++, + console.log(a); + } + expect_stdout: "0" +} + +issue_4558_2: { + options = { + evaluate: true, + ie: true, + reduce_vars: true, + unused: true, + } + input: { + (function() { + var a = 1; + var b = (a = NaN) || (console.log("PASS"), 2); + return a; + })(); + } + expect: { + (function() { + var a; + (a = NaN) || console.log("PASS"); + return a; + })(); + } + expect_stdout: "PASS" +} + +issue_4662: { + options = { + evaluate: true, + reduce_vars: true, + toplevel: true, + unused: true, + } + input: { + var a = 0; + function f(b, c) { + console.log(b, c); + } + f(++a, a = a, a); + } + expect: { + var a = 0; + (function(b, c) { + console.log(b, c); + })(++a, a = 1); + } + expect_stdout: "1 1" +} + +issue_4806_1: { + options = { + evaluate: true, + reduce_vars: true, + toplevel: true, + unused: true, + } + input: { + O = { + f: function() { + console.log(this === O ? "FAIL" : "PASS"); + }, + }; + var a; + (a = 42, O.f)(); + a; + } + expect: { + O = { + f: function() { + console.log(this === O ? "FAIL" : "PASS"); + }, + }; + (0, O.f)(); + 42; + } + expect_stdout: "PASS" +} + +issue_4806_2: { + options = { + sequences: true, + side_effects: true, + toplevel: true, + unused: true, + } + input: { + O = { + f: function() { + console.log(this === O ? "FAIL" : "PASS"); + }, + }; + var a; + (a = 42, O.f)(); + a; + } + expect: { + O = { + f: function() { + console.log(this === O ? "FAIL" : "PASS"); + }, + }, + (0, O.f)(); + } + expect_stdout: "PASS" +} + +issue_4806_3: { + options = { + side_effects: true, + toplevel: true, + unused: true, + } + input: { + O = { + f: function() { + console.log(this === O ? "FAIL" : "PASS"); + }, + }; + var a; + (a = 42, O.f)(); + a; + } + expect: { + O = { + f: function() { + console.log(this === O ? "FAIL" : "PASS"); + }, + }; + (0, O.f)(); + } + expect_stdout: "PASS" +} + +issue_4834: { + options = { + inline: true, + keep_fargs: false, + pure_getters: "strict", + reduce_vars: true, + side_effects: true, + toplevel: true, + unused: true, + } + input: { + try { + new function(a, b) { + b; + b.p; + }(42); + } catch (e) { + console.log("PASS"); + } + } + expect: { + try { + b.p; + } catch (e) { + console.log("PASS"); + } + var b; + } + expect_stdout: "PASS" +} + +issue_4912_1: { + options = { + pure_getters: "strict", + reduce_vars: true, + toplevel: true, + unused: true, + } + input: { + var a = A = function() {}; + A; + a.prototype = { + f: function() { + console.log("PASS"); + }, + }; + new A().f(); + } + expect: { + var a = A = function() {}; + A; + a.prototype = { + f: function() { + console.log("PASS"); + }, + }; + new A().f(); + } + expect_stdout: "PASS" +} + +issue_4912_2: { + options = { + pure_getters: "strict", + unused: true, + } + input: { + console.log(function() { + var g, f = function() {}; + f.p = {}; + (g = f.p.q = function() {}).r = "PASS"; + return f; + }().p.q.r); + } + expect: { + console.log(function() { + var g, f = function() {}; + f.p = {}; + (f.p.q = function() {}).r = "PASS"; + return f; + }().p.q.r); + } + expect_stdout: "PASS" +} + +issue_4912_3: { + options = { + pure_getters: "strict", + reduce_vars: true, + side_effects: true, + unused: true, + } + input: { + console.log(function(f, g) { + f = function() {}; + f.p = {}; + g = f.p.q = function() {}; + g.r = "PASS"; + return f; + }().p.q.r); + } + expect: { + console.log(function(f, g) { + f = function() {}; + f.p = {}; + g = f.p.q = function() {}; + g.r = "PASS"; + return f; + }().p.q.r); + } + expect_stdout: "PASS" +} + +issue_5079: { + options = { + collapse_vars: true, + pure_getters: "strict", + reduce_vars: true, + toplevel: true, + unused: true, + } + input: { + var a; + do { + (a = 123456).p = a; + a.q = null; + } while (console.log("PASS")); + } + expect: { + do { + 0, 0, null; + } while (console.log("PASS")); + } + expect_stdout: "PASS" +} + +issue_5224: { + options = { + evaluate: true, + keep_fargs: false, + reduce_vars: true, + toplevel: true, + unused: true, + } + input: { + function f() { + try { + var b = function() { + var a = "FAIL 1"; + null && a; + a = console.log(a); + }(new function(c, d) { + console.log(d); + a; + }("FAIL 2", Infinity)); + } finally { + return f; + } + } + f(); + } + expect: { + (function f() { + try { + (function() { + var a = "FAIL 1"; + null; + console.log(a); + })(function() { + console.log(1 / 0); + a; + }()); + } finally { + return f; + } + })(); + } + expect_stdout: "Infinity" +} + +issue_5271: { + options = { + evaluate: true, + reduce_vars: true, + toplevel: true, + unused: true, + } + input: { + function f() { + do { + var a = b = 0 ^ f, b = b; + } while (console.log(42 - b)); + } + f(); + } + expect: { + (function f() { + do { + var b; + b = 0 ^ f; + } while (console.log(42 - b)); + })(); + } + expect_stdout: "42" +} + +issue_5533_keep_fargs: { + options = { + evaluate: true, + inline: true, + join_vars: true, + keep_fargs: true, + loops: true, + side_effects: true, + unused: true, + } + input: { + "use strict"; + try { + (function() { + var a; + for (; 1;) + a = function() { + (function f(b) { + b; + throw "PASS"; + })(); + }(); + })(); + } catch (e) { + console.log(e); + } + } + expect: { + "use strict"; + try { + (function() { + for (;;) + throw "PASS"; + })(); + } catch (e) { + console.log(e); + } + } + expect_stdout: "PASS" +} + +issue_5533_drop_fargs: { + options = { + evaluate: true, + inline: true, + join_vars: true, + keep_fargs: false, + loops: true, + side_effects: true, + unused: true, + } + input: { + "use strict"; + try { + (function() { + var a; + for (; 1;) + a = function() { + (function f(b) { + b; + throw "PASS"; + })(); + }(); + })(); + } catch (e) { + console.log(e); + } + } + expect: { + "use strict"; + try { + (function() { + for (;;) + throw "PASS"; + })(); + } catch (e) { + console.log(e); + } + } + expect_stdout: "PASS" +} diff --git a/test/compress/evaluate.js b/test/compress/evaluate.js new file mode 100644 index 00000000000..eb8a0583b8c --- /dev/null +++ b/test/compress/evaluate.js @@ -0,0 +1,3418 @@ +and: { + options = { + evaluate: true, + side_effects: true, + } + input: { + var a; + // compress these + + a = true && condition; + a = 1 && console.log("a"); + a = 2 * 3 && 2 * condition; + a = 5 == 5 && condition + 3; + a = "string" && 4 - condition; + a = 5 + "" && condition / 5; + a = -4.5 && 6 << condition; + a = 6 && 7; + + a = false && condition; + a = NaN && console.log("b"); + a = 0 && console.log("c"); + a = undefined && 2 * condition; + a = null && condition + 3; + a = 2 * 3 - 6 && 4 - condition; + a = 10 == 7 && condition / 5; + a = !"string" && 6 % condition; + a = 0 && 7; + + // don't compress these + + a = condition && true; + a = console.log("a") && 2; + a = 4 - condition && "string"; + a = 6 << condition && -4.5; + + a = condition && false; + a = console.log("b") && NaN; + a = console.log("c") && 0; + a = 2 * condition && undefined; + a = condition + 3 && null; + + } + expect: { + var a; + + a = condition; + a = console.log("a"); + a = 2 * condition; + a = condition + 3; + a = 4 - condition; + a = condition / 5; + a = 6 << condition; + a = 7; + + a = false; + a = NaN; + a = 0; + a = void 0; + a = null; + a = 0; + a = false; + a = false; + a = 0; + + a = condition && true; + a = console.log("a") && 2; + a = 4 - condition && "string"; + a = 6 << condition && -4.5; + + a = condition && false; + a = console.log("b") && NaN; + a = console.log("c") && 0; + a = 2 * condition && void 0; + a = condition + 3 && null; + } +} + +or: { + options = { + evaluate: true, + side_effects: true, + } + input: { + var a; + // compress these + + a = true || condition; + a = 1 || console.log("a"); + a = 2 * 3 || 2 * condition; + a = 5 == 5 || condition + 3; + a = "string" || 4 - condition; + a = 5 + "" || condition / 5; + a = -4.5 || 6 << condition; + a = 6 || 7; + + a = false || condition; + a = 0 || console.log("b"); + a = NaN || console.log("c"); + a = undefined || 2 * condition; + a = null || condition + 3; + a = 2 * 3 - 6 || 4 - condition; + a = 10 == 7 || condition / 5; + a = !"string" || 6 % condition; + a = null || 7; + + a = console.log(undefined && condition || null); + a = console.log(undefined || condition && null); + + // don't compress these + + a = condition || true; + a = console.log("a") || 2; + a = 4 - condition || "string"; + a = 6 << condition || -4.5; + + a = condition || false; + a = console.log("b") || NaN; + a = console.log("c") || 0; + a = 2 * condition || undefined; + a = condition + 3 || null; + + } + expect: { + var a; + + a = true; + a = 1; + a = 6; + a = true; + a = "string"; + a = "5"; + a = -4.5; + a = 6; + + a = condition; + a = console.log("b"); + a = console.log("c"); + a = 2 * condition; + a = condition + 3; + a = 4 - condition; + a = condition / 5; + a = 6 % condition; + a = 7; + + a = console.log(null); + a = console.log(condition && null); + + a = condition || true; + a = console.log("a") || 2; + a = 4 - condition || "string"; + a = 6 << condition || -4.5; + + a = condition || false; + a = console.log("b") || NaN; + a = console.log("c") || 0; + a = 2 * condition || void 0; + a = condition + 3 || null; + } +} + +unary_prefix: { + options = { + evaluate: true, + side_effects: true, + } + input: { + a = !0 && b; + a = !0 || b; + a = ~1 && b; + a = ~1 || b; + a = -2 && b; + a = -2 || b; + a = +3 && b; + a = +3 || b; + } + expect: { + a = b; + a = !0; + a = b; + a = -2; + a = b; + a = -2; + a = b; + a = 3; + } +} + +negative_zero: { + options = { + evaluate: true, + } + input: { + console.log( + -"", + - -"", + 1 / (-0), + 1 / (-"") + ); + } + expect: { + console.log( + -0, + 0, + -1/0, + -1/0 + ); + } + expect_stdout: true +} + +positive_zero: { + options = { + evaluate: true, + } + input: { + console.log( + +"", + + -"", + 1 / (+0), + 1 / (+"") + ); + } + expect: { + console.log( + 0, + -0, + 1/0, + 1/0 + ); + } + expect_stdout: true +} + +unsafe_constant: { + options = { + evaluate: true, + unsafe: true, + } + input: { + console.log(true.a, false.a); + console.log(true.valueOf(), false.valueOf()); + try { + console.log(null.a); + } catch (e) { + console.log("PASS"); + } + try { + console.log(undefined.a); + } catch (e) { + console.log("PASS"); + } + } + expect: { + console.log(void 0, void 0); + console.log(true, false); + try { + console.log(null.a); + } catch (e) { + console.log("PASS"); + } + try { + console.log((void 0).a); + } catch (e) { + console.log("PASS"); + } + } + expect_stdout: [ + "undefined undefined", + "true false", + "PASS", + "PASS", + ] +} + +unsafe_object: { + options = { + evaluate: true, + reduce_funcs: true, + reduce_vars: true, + toplevel: true, + unsafe: true, + } + input: { + var o = { a: 1 }; + console.log( + o + 1, + o.a + 1, + o.b + 1, + o.a.b + 1 + ); + } + expect: { + var o = { a: 1 }; + console.log( + o + 1, + 2, + o.b + 1, + NaN + ); + } + expect_stdout: true +} + +unsafe_object_nested: { + options = { + evaluate: true, + reduce_funcs: true, + reduce_vars: true, + toplevel: true, + unsafe: true, + } + input: { + var o = { a: { b: 1 } }; + console.log( + o + 1, + o.a + 1, + o.b + 1, + o.a.b + 1 + ); + } + expect: { + var o = { a: { b: 1 } }; + console.log( + o + 1, + o.a + 1, + o.b + 1, + 2 + ); + } + expect_stdout: true +} + +unsafe_object_complex: { + options = { + evaluate: true, + reduce_funcs: true, + reduce_vars: true, + toplevel: true, + unsafe: true, + } + input: { + var o = { a: { b: 1 }, b: 1 }; + console.log( + o + 1, + o.a + 1, + o.b + 1, + o.a.b + 1 + ); + } + expect: { + var o = { a: { b: 1 }, b: 1 }; + console.log( + o + 1, + o.a + 1, + 2, + 2 + ); + } + expect_stdout: true +} + +unsafe_object_repeated: { + options = { + evaluate: true, + reduce_funcs: true, + reduce_vars: true, + toplevel: true, + unsafe: true, + } + input: { + var o = { a: { b: 1 }, a: 1 }; + console.log( + o + 1, + o.a + 1, + o.b + 1, + o.a.b + 1 + ); + } + expect: { + var o = { a: { b: 1 }, a: 1 }; + console.log( + o + 1, + 2, + o.b + 1, + NaN + ); + } + expect_stdout: true +} + +unsafe_object_accessor: { + options = { + evaluate: true, + reduce_funcs: true, + reduce_vars: true, + unsafe: true, + } + input: { + function f() { + var a = { + get b() {}, + set b(v) {}, + }; + return { a: a }; + } + } + expect: { + function f() { + var a = { + get b() {}, + set b(v) {}, + }; + return { a: a }; + } + } +} + +prop_function: { + options = { + evaluate: true, + properties: true, + side_effects: true, + } + input: { + console.log( + ({a:{b:1},b:function(){}}) + 1, + ({a:{b:1},b:function(){}}).a + 1, + ({a:{b:1},b:function(){}}).b + 1, + ({a:{b:1},b:function(){}}).a.b + 1 + ); + } + expect: { + console.log( + ({a:{b:1},b:function(){}}) + 1, + ({b:1}) + 1, + function(){} + 1, + 2 + ); + } + expect_stdout: true +} + +unsafe_integer_key: { + options = { + evaluate: true, + unsafe: true, + } + input: { + console.log( + ({0:1}) + 1, + ({0:1})[0] + 1, + ({0:1})["0"] + 1, + ({0:1})[1] + 1, + ({0:1})[0][1] + 1, + ({0:1})[0]["1"] + 1 + ); + } + expect: { + console.log( + ({0:1}) + 1, + 2, + 2, + ({0:1})[1] + 1, + NaN, + NaN + ); + } + expect_stdout: true +} + +unsafe_integer_key_complex: { + options = { + evaluate: true, + unsafe: true, + } + input: { + console.log( + ({0:{1:1},1:1}) + 1, + ({0:{1:1},1:1})[0] + 1, + ({0:{1:1},1:1})["0"] + 1, + ({0:{1:1},1:1})[1] + 1, + ({0:{1:1},1:1})[0][1] + 1, + ({0:{1:1},1:1})[0]["1"] + 1 + ); + } + expect: { + console.log( + ({0:{1:1},1:1}) + 1, + "[object Object]1", + "[object Object]1", + 2, + 2, + 2 + ); + } + expect_stdout: true +} + +unsafe_float_key: { + options = { + evaluate: true, + unsafe: true, + } + input: { + console.log( + ({2.72:1}) + 1, + ({2.72:1})[2.72] + 1, + ({2.72:1})["2.72"] + 1, + ({2.72:1})[3.14] + 1, + ({2.72:1})[2.72][3.14] + 1, + ({2.72:1})[2.72]["3.14"] + 1 + ); + } + expect: { + console.log( + ({2.72:1}) + 1, + 2, + 2, + ({2.72:1})[3.14] + 1, + NaN, + NaN + ); + } + expect_stdout: true +} + +unsafe_float_key_complex: { + options = { + evaluate: true, + unsafe: true, + } + input: { + console.log( + ({2.72:{3.14:1},3.14:1}) + 1, + ({2.72:{3.14:1},3.14:1})[2.72] + 1, + ({2.72:{3.14:1},3.14:1})["2.72"] + 1, + ({2.72:{3.14:1},3.14:1})[3.14] + 1, + ({2.72:{3.14:1},3.14:1})[2.72][3.14] + 1, + ({2.72:{3.14:1},3.14:1})[2.72]["3.14"] + 1 + ); + } + expect: { + console.log( + "[object Object]1", + "[object Object]1", + "[object Object]1", + 2, + 2, + 2 + ); + } + expect_stdout: true +} + +unsafe_array: { + options = { + evaluate: true, + unsafe: true, + } + input: { + var a = "PASS"; + Array.prototype[1] = a; + console.log([, ].length); + console.log("" + [, , ]); + console.log([1, , 3][1]); + console.log([1, 2, 3, a] + 1); + console.log([1, 2, 3, 4] + 1); + console.log([1, 2, 3, a][0] + 1); + console.log([1, 2, 3, 4][0] + 1); + console.log([1, 2, 3, 4][6 - 5] + 1); + console.log([1, , 3, 4][6 - 5] + 1); + console.log([[1, 2], [3, 4]][0] + 1); + console.log([[1, 2], [3, 4]][6 - 5][1] + 1); + console.log([[1, 2], , [3, 4]][6 - 5][1] + 1); + } + expect: { + var a = "PASS"; + Array.prototype[1] = a; + console.log([, ].length); + console.log("" + [, , ]); + console.log([1, , 3][1]); + console.log([1, 2, 3, a] + 1); + console.log("1,2,3,41"); + console.log([1, 2, 3, a][0] + 1); + console.log(2); + console.log(3); + console.log([1, , 3, 4][1] + 1); + console.log("1,21"); + console.log(5); + console.log([[1, 2], , [3, 4]][1][1] + 1); + } + expect_stdout: [ + "1", + ",PASS", + "PASS", + "1,2,3,PASS1", + "1,2,3,41", + "2", + "2", + "3", + "PASS1", + "1,21", + "5", + "A1", + ] +} + +unsafe_string: { + options = { + evaluate: true, + unsafe: true, + } + input: { + console.log( + "1234" + 1, + "1234"[0] + 1, + "1234"[6 - 5] + 1, + ("12" + "34")[0] + 1, + ("12" + "34")[6 - 5] + 1, + [1, 2, 3, 4].join("")[0] + 1 + ); + } + expect: { + console.log( + "12341", + "11", + "21", + "11", + "21", + "11" + ); + } + expect_stdout: true +} + +unsafe_array_bad_index: { + options = { + evaluate: true, + unsafe: true, + } + input: { + console.log( + [1, 2, 3, 4].a + 1, + [1, 2, 3, 4]["a"] + 1, + [1, 2, 3, 4][3.14] + 1 + ); + } + expect: { + console.log( + [1, 2, 3, 4].a + 1, + [1, 2, 3, 4]["a"] + 1, + [1, 2, 3, 4][3.14] + 1 + ); + } + expect_stdout: true +} + +unsafe_string_bad_index: { + options = { + evaluate: true, + unsafe: true, + } + input: { + console.log( + "1234".a + 1, + "1234"["a"] + 1, + "1234"[3.14] + 1 + ); + } + expect: { + console.log( + NaN, + NaN, + NaN + ); + } + expect_stdout: "NaN NaN NaN" +} + +prototype_function: { + options = { + evaluate: true, + properties: true, + side_effects: true, + } + input: { + function v() { + return this.valueOf === v ? "PASS" : "FAIL"; + } + console.log(({ valueOf: v }) < 1); + console.log(({ valueOf: v }) + ""); + console.log((( {valueOf: v }) + "")[2]); + console.log(({ valueOf: v }).valueOf()); + function t() { + return this.toString === t ? "PASS" : "FAIL"; + } + console.log(({ toString: t }) < 1); + console.log(({ toString: t }) + ""); + console.log((( {toString: t }) + "")[2]); + console.log(({ toString: t }).toString()); + } + expect: { + function v() { + return this.valueOf === v ? "PASS" : "FAIL"; + } + console.log(({ valueOf: v }) < 1); + console.log(({ valueOf: v }) + ""); + console.log((( {valueOf: v }) + "")[2]); + console.log(({ valueOf: v }).valueOf()); + function t() { + return this.toString === t ? "PASS" : "FAIL"; + } + console.log(({ toString: t }) < 1); + console.log(({ toString: t }) + ""); + console.log((( {toString: t }) + "")[2]); + console.log(({ toString: t }).toString()); + } + expect_stdout: [ + "false", + "PASS", + "S", + "PASS", + "false", + "PASS", + "S", + "PASS", + ] +} + +call_args: { + options = { + evaluate: true, + inline: true, + reduce_funcs: true, + reduce_vars: true, + toplevel: true, + } + input: { + var a = 1; + console.log(a); + +function(a) { + return a; + }(a); + } + expect: { + var a = 1; + console.log(1); + 1, 1; + } + expect_stdout: true +} + +call_args_drop_param: { + options = { + evaluate: true, + inline: true, + keep_fargs: false, + reduce_funcs: true, + reduce_vars: true, + toplevel: true, + unused: true, + } + input: { + var a = 1; + console.log(a); + +function(a) { + return a; + }(a, b); + } + expect: { + console.log(1); + b, 1; + } + expect_stdout: true +} + +in_boolean_context: { + options = { + booleans: true, + evaluate: true, + sequences: true, + side_effects: true, + } + input: { + console.log( + !42, + !"foo", + ![1, 2], + !/foo/, + !b(42), + !b("foo"), + !b([1, 2]), + !b(/foo/), + ![1, foo()], + ![1, foo(), 2] + ); + } + expect: { + console.log( + !1, + !1, + !1, + !1, + !b(42), + !b("foo"), + !b([1, 2]), + !b(/foo/), + (foo(), !1), + (foo(), !1) + ); + } + expect_stdout: true +} + +unsafe_charAt: { + options = { + evaluate: true, + unsafe: true, + } + input: { + console.log( + "1234" + 1, + "1234".charAt(0) + 1, + "1234".charAt(6 - 5) + 1, + ("12" + "34").charAt(0) + 1, + ("12" + "34").charAt(6 - 5) + 1, + [1, 2, 3, 4].join("").charAt(0) + 1 + ); + } + expect: { + console.log( + "12341", + "11", + "21", + "11", + "21", + "11" + ); + } + expect_stdout: true +} + +unsafe_charAt_bad_index: { + options = { + evaluate: true, + unsafe: true, + } + input: { + console.log( + "1234".charAt() + 1, + "1234".charAt("a") + 1, + "1234".charAt(3.14) + 1 + ); + } + expect: { + console.log( + "11", + "11", + "41" + ); + } + expect_stdout: true +} + +unsafe_charAt_noop: { + options = { + evaluate: true, + unsafe: true, + } + input: { + s = "foo"; + x = 42; + console.log( + s.charAt(0), + "string".charAt(x), + (typeof x).charAt() + ); + } + expect: { + s = "foo"; + x = 42; + console.log( + s[0] || "", + "string"[0 | x] || "", + (typeof x)[0] || "" + ); + } + expect_stdout: "f n" +} + +chained_side_effects: { + options = { + evaluate: true, + } + input: { + console.log("foo") || (console.log("bar"), "baz") || console.log("moo"); + } + expect: { + console.log("foo") || (console.log("bar"), "baz"); + } + expect_stdout: [ + "foo", + "bar", + ] + expect_warnings: [ + "WARN: Condition left of || always true [test/compress/evaluate.js:1,8]", + ] +} + +instanceof_lambda: { + options = { + evaluate: true, + side_effects: true, + } + input: { + console.log(42 instanceof function() {}); + } + expect: { + console.log(false); + } + expect_stdout: "false" +} + +issue_1649: { + options = { + evaluate: true, + } + input: { + console.log(-1 + -1); + } + expect: { + console.log(-2); + } + expect_stdout: "-2"; +} + +issue_1760_1: { + options = { + evaluate: true, + } + input: { + !function(a) { + try { + throw 0; + } catch (NaN) { + a = +"foo"; + } + console.log(a); + }(); + } + expect: { + !function(a) { + try { + throw 0; + } catch (NaN) { + a = 0 / 0; + } + console.log(a); + }(); + } + expect_stdout: "NaN" +} + +issue_1760_2: { + options = { + evaluate: true, + keep_infinity: true, + } + input: { + !function(a) { + try { + throw 0; + } catch (Infinity) { + a = 123456789 / 0; + } + console.log(a); + }(); + } + expect: { + !function(a) { + try { + throw 0; + } catch (Infinity) { + a = 1 / 0; + } + console.log(a); + }(); + } + expect_stdout: "Infinity" +} + +delete_expr_1: { + options = { + booleans: true, + evaluate: true, + } + input: { + console.log(delete undefined); + console.log(delete void 0); + console.log(delete Infinity); + console.log(delete (1 / 0)); + console.log(delete NaN); + console.log(delete (0 / 0)); + } + expect: { + console.log(delete undefined); + console.log((void 0, !0)); + console.log(delete Infinity); + console.log((1 / 0, !0)); + console.log(delete NaN); + console.log((0 / 0, !0)); + } + expect_stdout: true +} + +delete_expr_2: { + options = { + booleans: true, + evaluate: true, + keep_infinity: true, + } + input: { + console.log(delete undefined); + console.log(delete void 0); + console.log(delete Infinity); + console.log(delete (1 / 0)); + console.log(delete NaN); + console.log(delete (0 / 0)); + } + expect: { + console.log(delete undefined); + console.log((void 0, !0)); + console.log(delete Infinity); + console.log((1 / 0, !0)); + console.log(delete NaN); + console.log((0 / 0, !0)); + } + expect_stdout: true +} + +delete_binary_1: { + options = { + booleans: true, + evaluate: true, + side_effects: true, + } + input: { + console.log(delete (true && undefined)); + console.log(delete (true && void 0)); + console.log(delete (true && Infinity)); + console.log(delete (true && (1 / 0))); + console.log(delete (true && NaN)); + console.log(delete (true && (0 / 0))); + } + expect: { + console.log(!0); + console.log(!0); + console.log(!0); + console.log(!0); + console.log(!0); + console.log(!0); + } + expect_stdout: true +} + +delete_binary_2: { + options = { + booleans: true, + evaluate: true, + keep_infinity: true, + side_effects: true, + } + input: { + console.log(delete (false || undefined)); + console.log(delete (false || void 0)); + console.log(delete (false || Infinity)); + console.log(delete (false || (1 / 0))); + console.log(delete (false || NaN)); + console.log(delete (false || (0 / 0))); + } + expect: { + console.log(!0); + console.log(!0); + console.log(!0); + console.log(!0); + console.log(!0); + console.log(!0); + } + expect_stdout: true +} + +Infinity_NaN_undefined_LHS: { + beautify = { + beautify: true, + } + input: { + function f() { + Infinity = Infinity; + ++Infinity; + Infinity--; + NaN *= NaN; + ++NaN; + NaN--; + undefined |= undefined; + ++undefined; + undefined--; + } + } + expect_exact: [ + "function f() {", + " Infinity = 1 / 0;", + " ++Infinity;", + " Infinity--;", + " NaN *= NaN;", + " ++NaN;", + " NaN--;", + " undefined |= void 0;", + " ++undefined;", + " undefined--;", + "}", + ] +} + +issue_1964_1: { + options = { + evaluate: true, + reduce_funcs: true, + reduce_vars: true, + unsafe_regexp: false, + unused: true, + } + input: { + function f() { + var long_variable_name = /\s/; + console.log(long_variable_name.source); + return "a b c".split(long_variable_name)[1]; + } + console.log(f()); + } + expect: { + function f() { + var long_variable_name = /\s/; + console.log(long_variable_name.source); + return "a b c".split(long_variable_name)[1]; + } + console.log(f()); + } + expect_stdout: [ + "\\s", + "b", + ] +} + +issue_1964_2: { + options = { + evaluate: true, + reduce_funcs: true, + reduce_vars: true, + unsafe_regexp: true, + unused: true, + } + input: { + function f() { + var long_variable_name = /\s/; + console.log(long_variable_name.source); + return "a b c".split(long_variable_name)[1]; + } + console.log(f()); + } + expect: { + function f() { + console.log(/\s/.source); + return "a b c".split(/\s/)[1]; + } + console.log(f()); + } + expect_stdout: [ + "\\s", + "b", + ] +} + +array_slice_index: { + options = { + evaluate: true, + unsafe: true, + } + input: { + console.log([1,2,3].slice(1)[1]); + } + expect: { + console.log(3); + } + expect_stdout: "3" +} + +string_charCodeAt: { + options = { + evaluate: true, + unsafe: true, + } + input: { + console.log("foo".charCodeAt("bar".length)); + } + expect: { + console.log(NaN); + } + expect_stdout: "NaN" +} + +issue_2207_1: { + options = { + evaluate: true, + unsafe: true, + } + input: { + console.log(String.fromCharCode(65)); + console.log(Math.max(3, 6, 2, 7, 3, 4)); + console.log(Math.cos(1.2345)); + console.log(Math.cos(1.2345) - Math.sin(4.321)); + console.log(Math.pow(Math.PI, Math.E - Math.LN10).toFixed(15)); + } + expect: { + console.log("A"); + console.log(7); + console.log(Math.cos(1.2345)); + console.log(1.2543732512566947); + console.log("1.609398451447204"); + } + expect_stdout: true +} + +issue_2207_2: { + options = { + evaluate: true, + unsafe: true, + } + input: { + console.log(Math.E); + console.log(Math.LN10); + console.log(Math.LN2); + console.log(Math.LOG2E); + console.log(Math.LOG10E); + console.log(Math.PI); + console.log(Math.SQRT1_2); + console.log(Math.SQRT2); + } + expect: { + console.log(Math.E); + console.log(Math.LN10); + console.log(Math.LN2); + console.log(Math.LOG2E); + console.log(Math.LOG10E); + console.log(Math.PI); + console.log(Math.SQRT1_2); + console.log(Math.SQRT2); + } + expect_stdout: true +} + +issue_2207_3: { + options = { + evaluate: true, + unsafe: true, + } + input: { + console.log(Number.MAX_VALUE); + console.log(Number.MIN_VALUE); + console.log(Number.NaN); + console.log(Number.NEGATIVE_INFINITY); + console.log(Number.POSITIVE_INFINITY); + } + expect: { + console.log(Number.MAX_VALUE); + console.log(5e-324); + console.log(NaN); + console.log(-1/0); + console.log(1/0); + } + expect_stdout: true +} + +issue_2231_1: { + options = { + evaluate: true, + unsafe: true, + } + input: { + console.log(Object.keys(void 0)); + } + expect: { + console.log(Object.keys(void 0)); + } + expect_stdout: true + expect_warnings: [ + "WARN: Error evaluating Object.keys(void 0) [test/compress/evaluate.js:1,20]", + ] +} + +issue_2231_2: { + options = { + evaluate: true, + unsafe: true, + } + input: { + console.log(Object.getOwnPropertyNames(null)); + } + expect: { + console.log(Object.getOwnPropertyNames(null)); + } + expect_stdout: true + expect_warnings: [ + "WARN: Error evaluating Object.getOwnPropertyNames(null) [test/compress/evaluate.js:1,20]", + ] +} + +issue_2231_3: { + options = { + evaluate: true, + unsafe: true, + } + input: { + console.log(Object.keys({ foo: "bar" })[0]); + } + expect: { + console.log("foo"); + } + expect_stdout: "foo" +} + +self_comparison_1: { + options = { + evaluate: true, + reduce_funcs: true, + reduce_vars: true, + toplevel: true, + unsafe: true, + unused: true, + } + input: { + var o = { n: NaN }; + console.log(typeof o.n, o.n == o.n, o.n === o.n, o.n != o.n, o.n !== o.n); + } + expect: { + console.log("number", false, false, true, true); + } + expect_stdout: "number false false true true" +} + +self_comparison_2: { + options = { + evaluate: true, + hoist_props: true, + passes: 2, + reduce_funcs: true, + reduce_vars: true, + toplevel: true, + unused: true, + } + input: { + var o = { n: NaN }; + console.log(typeof o.n, o.n == o.n, o.n === o.n, o.n != o.n, o.n !== o.n); + } + expect: { + console.log("number", false, false, true, true); + } + expect_stdout: "number false false true true" +} + +issue_2535_1: { + options = { + booleans: true, + evaluate: true, + sequences: true, + side_effects: true, + } + input: { + if ((x() || true) || y()) z(); + if ((x() || true) && y()) z(); + if ((x() && true) || y()) z(); + if ((x() && true) && y()) z(); + if ((x() || false) || y()) z(); + if ((x() || false) && y()) z(); + if ((x() && false) || y()) z(); + if ((x() && false) && y()) z(); + } + expect: { + if (x(), 1) z(); + if (x(), y()) z(); + if (x() || y()) z(); + if (x() && y()) z(); + if (x() || y()) z(); + if (x() && y()) z(); + if (x(), y()) z(); + if (x(), 0) z(); + } +} + +issue_2535_2: { + options = { + booleans: true, + evaluate: true, + sequences: true, + side_effects: true, + } + input: { + (x() || true) || y(); + (x() || true) && y(); + (x() && true) || y(); + (x() && true) && y(); + (x() || false) || y(); + (x() || false) && y(); + (x() && false) || y(); + (x() && false) && y(); + } + expect: { + x(), + x(), y(), + x() || y(), + x() && y(), + x() || y(), + x() && y(), + x(), y(), + x(); + } +} + +issue_2535_3: { + options = { + booleans: true, + evaluate: true, + } + input: { + console.log(Object(1) && 1 && 2); + console.log(Object(1) && true && 1 && 2 && Object(2)); + console.log(Object(1) && true && 1 && null && 2 && Object(2)); + console.log(2 == Object(1) || 0 || void 0 || null); + console.log(2 == Object(1) || 0 || void 0 || null || Object(2)); + console.log(2 == Object(1) || 0 || void 0 || "ok" || null || Object(2)); + } + expect: { + console.log(Object(1) && 2); + console.log(Object(1) && Object(2)); + console.log(Object(1) && null); + console.log(2 == Object(1) || null); + console.log(2 == Object(1) || Object(2)); + console.log(2 == Object(1) || "ok"); + } + expect_stdout: true + expect_warnings: [ + "WARN: Dropping side-effect-free && [test/compress/evaluate.js:1,20]", + "WARN: Dropping side-effect-free && [test/compress/evaluate.js:2,20]", + "WARN: Dropping side-effect-free && [test/compress/evaluate.js:3,20]", + "WARN: Condition left of && always false [test/compress/evaluate.js:3,20]", + "WARN: Dropping side-effect-free || [test/compress/evaluate.js:4,20]", + "WARN: Dropping side-effect-free || [test/compress/evaluate.js:5,20]", + "WARN: Dropping side-effect-free || [test/compress/evaluate.js:6,20]", + "WARN: Condition left of || always true [test/compress/evaluate.js:6,20]", + ] +} + +issue_2822: { + options = { + evaluate: true, + unsafe: true, + } + input: { + console.log([ function() {}, "PASS", "FAIL" ][1]); + } + expect: { + console.log("PASS"); + } + expect_stdout: "PASS" +} + +string_case: { + options = { + evaluate: true, + unsafe: true, + } + input: { + console.log("İ".toLowerCase().charCodeAt(0)); + console.log("I".toLowerCase().charCodeAt(0)); + console.log("Ş".toLowerCase().charCodeAt(0)); + console.log("Ğ".toLowerCase().charCodeAt(0)); + console.log("Ü".toLowerCase().charCodeAt(0)); + console.log("Ö".toLowerCase().charCodeAt(0)); + console.log("Ç".toLowerCase().charCodeAt(0)); + console.log("i".toUpperCase().charCodeAt(0)); + console.log("ı".toUpperCase().charCodeAt(0)); + console.log("ş".toUpperCase().charCodeAt(0)); + console.log("ğ".toUpperCase().charCodeAt(0)); + console.log("ü".toUpperCase().charCodeAt(0)); + console.log("ö".toUpperCase().charCodeAt(0)); + console.log("ç".toUpperCase().charCodeAt(0)); + } + expect: { + console.log(105); + console.log(105); + console.log(351); + console.log(287); + console.log(252); + console.log(246); + console.log(231); + console.log(73); + console.log(73); + console.log(350); + console.log(286); + console.log(220); + console.log(214); + console.log(199); + } + expect_stdout: [ + "105", + "105", + "351", + "287", + "252", + "246", + "231", + "73", + "73", + "350", + "286", + "220", + "214", + "199", + ] +} + +issue_2916_1: { + options = { + evaluate: true, + reduce_vars: true, + unsafe: true, + } + input: { + var c = "PASS"; + (function(a, b) { + (function(d) { + d[0] = 1; + })(b); + a == b && (c = "FAIL"); + })("", []); + console.log(c); + } + expect: { + var c = "PASS"; + (function(a, b) { + (function(d) { + d[0] = 1; + })(b); + a == b && (c = "FAIL"); + })("", []); + console.log(c); + } + expect_stdout: "PASS" +} + +issue_2916_2: { + options = { + collapse_vars: true, + evaluate: true, + inline: true, + reduce_vars: true, + side_effects: true, + unsafe: true, + unused: true, + } + input: { + var c = "FAIL"; + (function(b) { + (function(d) { + d[0] = 1; + })(b); + +b && (c = "PASS"); + })([]); + console.log(c); + } + expect: { + var c = "FAIL"; + (function(b) { + b[0] = 1; + +b && (c = "PASS"); + })([]); + console.log(c); + } + expect_stdout: "PASS" +} + +issue_2919: { + options = { + evaluate: true, + unsafe: true, + } + input: { + console.log([ function() {} ].toString()); + } + expect: { + console.log("function(){}"); + } +} + +issue_2926_1: { + options = { + evaluate: true, + reduce_vars: true, + unsafe: true, + } + input: { + (function f(a) { + console.log(f.name.length, f.length); + })(); + } + expect: { + (function f(a) { + console.log(1, 1); + })(); + } + expect_stdout: "1 1" +} + +issue_2926_2: { + options = { + evaluate: true, + unsafe: true, + } + input: { + console.log(typeof function() {}.valueOf()); + } + expect: { + console.log("function"); + } + expect_stdout: "function" +} + +issue_2968_1: { + options = { + collapse_vars: true, + evaluate: true, + inline: true, + passes: 2, + reduce_vars: true, + unused: true, + } + input: { + var c = "FAIL"; + (function() { + (function(a, b) { + a <<= 0; + a && (a[(c = "PASS", 0 >>> (b += 1))] = 0); + })(42, -42); + })(); + console.log(c); + } + expect: { + var c = "FAIL"; + (function() { + a = 42, + void ((a <<= 0) && (a[(c = "PASS", 0)] = 0)); + var a; + })(); + console.log(c); + } + expect_stdout: "PASS" +} + +issue_2968_2: { + options = { + assignments: true, + collapse_vars: true, + evaluate: true, + inline: true, + passes: 2, + reduce_vars: true, + side_effects: true, + unused: true, + } + input: { + var c = "FAIL"; + (function() { + (function(a, b) { + a <<= 0; + a && (a[(c = "PASS", 0 >>> (b += 1))] = 0); + })(42, -42); + })(); + console.log(c); + } + expect: { + var c = "FAIL"; + (function() { + a = 42, + ((a <<= 0) && (a[(c = "PASS", 0)] = 0)); + var a; + })(); + console.log(c); + } + expect_stdout: "PASS" +} + +truthy_conditionals: { + options = { + conditionals: true, + evaluate: true, + } + input: { + if (a = {}) x(); + (b = /foo/) && y(); + (c = function() {}) || z(); + } + expect: { + a = {}, x(); + b = /foo/, y(); + c = function() {}; + } +} + +truthy_loops: { + options = { + evaluate: true, + loops: true, + } + input: { + while ([]) x(); + do { + y(); + } while(a = {}); + } + expect: { + for (;;) { + []; + x(); + } + for (;;) { + y(); + a = {}; + } + } +} + +if_increment: { + options = { + evaluate: true, + reduce_vars: true, + unused: true, + } + input: { + console.log(function(a) { + if (console) + return ++a; + }(0)); + } + expect: { + console.log(function(a) { + if (console) + return 1; + }()); + } + expect_stdout: "1" +} + +try_increment: { + options = { + evaluate: true, + reduce_vars: true, + unused: true, + } + input: { + console.log(function(a) { + try { + return ++a; + } catch (e) {} + }(0)); + } + expect: { + console.log(function(a) { + try { + return 1; + } catch (e) {} + }()); + } + expect_stdout: "1" +} + +unsafe_escaped: { + options = { + evaluate: true, + inline: true, + passes: 3, + reduce_vars: true, + side_effects: true, + unsafe: true, + unused: true, + } + input: { + (function(a) { + console.log(function(index) { + return a[index]; + }(function(term) { + return a.indexOf(term); + }("PASS"))); + })([ "PASS" ]); + } + expect: { + console.log("PASS"); + } + expect_stdout: "PASS" +} + +unsafe_string_replace: { + options = { + evaluate: true, + unsafe: true, + } + input: { + "foo".replace("f", function() { + console.log("PASS"); + }); + } + expect: { + "foo".replace("f", function() { + console.log("PASS"); + }); + } + expect_stdout: "PASS" +} + +issue_3387_1: { + options = { + evaluate: true, + } + input: { + console.log(1 + (2 + "3"[4])); + } + expect: { + console.log(1 + (2 + "3"[4])); + } + expect_stdout: "NaN" +} + +issue_3387_2: { + options = { + evaluate: true, + unsafe: true, + } + input: { + console.log(1 + (2 + "3"[4])); + } + expect: { + console.log(NaN); + } + expect_stdout: "NaN" +} + +simple_function_1: { + options = { + evaluate: true, + reduce_vars: true, + toplevel: true, + unused: true, + } + input: { + function sum(a, b) { + return a + b; + } + console.log(sum(1, 2) * sum(3, 4)); + } + expect: { + console.log(21); + } + expect_stdout: "21" +} + +simple_function_2: { + options = { + evaluate: true, + reduce_vars: true, + toplevel: true, + unused: true, + } + input: { + var sum = function(a, b) { + return a + b; + } + console.log(sum(1, 2) * sum(3, 4)); + } + expect: { + console.log(21); + } + expect_stdout: "21" +} + +recursive_function_1: { + options = { + evaluate: true, + reduce_vars: true, + toplevel: true, + unused: true, + } + input: { + function factorial(a) { + return a > 0 ? a * factorial(a - 1) : 1; + } + console.log(factorial(5)); + } + expect: { + console.log(function factorial(a) { + return a > 0 ? a * factorial(a - 1) : 1; + }(5)); + } + expect_stdout: "120" +} + +recursive_function_2: { + options = { + evaluate: true, + reduce_vars: true, + toplevel: true, + unused: true, + } + input: { + var factorial = function(a) { + return a > 0 ? a * factorial(a - 1) : 1; + } + console.log(factorial(5)); + } + expect: { + var factorial = function(a) { + return a > 0 ? a * factorial(a - 1) : 1; + } + console.log(factorial(5)); + } + expect_stdout: "120" +} + +issue_3558: { + options = { + evaluate: true, + reduce_vars: true, + toplevel: true, + } + input: { + function f(a) { + return 1 + --a; + } + console.log(f(true), f(false)); + } + expect: { + function f(a) { + return 1 + --a; + } + console.log(1, 0); + } + expect_stdout: "1 0" +} + +issue_3568: { + options = { + evaluate: true, + reduce_vars: true, + toplevel: true, + unsafe: true, + } + input: { + var a = 0; + function f(b) { + return b && b.p; + } + console.log(f(++a + f())); + } + expect: { + var a = 0; + function f(b) { + return b && b.p; + } + console.log(NaN); + } + expect_stdout: "NaN" +} + +conditional_function: { + options = { + evaluate: true, + reduce_vars: true, + toplevel: true, + } + input: { + function f(a) { + return a && "undefined" != typeof A ? A : 42; + } + console.log(f(0), f(1)); + } + expect: { + function f(a) { + return a && "undefined" != typeof A ? A : 42; + } + console.log(42, f(1)); + } + expect_stdout: "42 42" +} + +best_of_evaluate: { + options = { + evaluate: true, + reduce_vars: true, + toplevel: true, + unused: true, + } + input: { + function d(x, y) { + return x / y; + } + console.log(0 / 3, 1 / 64, 4 / 7, 7 / 7); + console.log(d(0, 3), d(1, 64), d(4, 7), d(7, 7)); + } + expect: { + function d(x, y) { + return x / y; + } + console.log(0, 1 / 64, 4 / 7, 1); + console.log(0, .015625, d(4, 7), 1); + } + expect_stdout: [ + "0 0.015625 0.5714285714285714 1", + "0 0.015625 0.5714285714285714 1", + ] +} + +eager_evaluate: { + options = { + evaluate: "eager", + reduce_vars: true, + toplevel: true, + unused: true, + } + input: { + function d(x, y) { + return x / y; + } + console.log(0 / 3, 1 / 64, 4 / 7, 7 / 7); + console.log(d(0, 3), d(1, 64), d(4, 7), d(7, 7)); + } + expect: { + console.log(0, .015625, .5714285714285714, 1); + console.log(0, .015625, .5714285714285714, 1); + } + expect_stdout: [ + "0 0.015625 0.5714285714285714 1", + "0 0.015625 0.5714285714285714 1", + ] +} + +threshold_evaluate_default: { + options = { + evaluate: true, + reduce_vars: true, + toplevel: true, + unused: true, + } + input: { + function b(x) { + return x + x + x; + } + console.log(b("1"), b(2), b(b(b("ABCDEFGHIJK")))); + } + expect: { + function b(x) { + return x + x + x; + } + console.log("111", 6, b(b(b("ABCDEFGHIJK")))); + } + expect_stdout: "111 6 ABCDEFGHIJKABCDEFGHIJKABCDEFGHIJKABCDEFGHIJKABCDEFGHIJKABCDEFGHIJKABCDEFGHIJKABCDEFGHIJKABCDEFGHIJKABCDEFGHIJKABCDEFGHIJKABCDEFGHIJKABCDEFGHIJKABCDEFGHIJKABCDEFGHIJKABCDEFGHIJKABCDEFGHIJKABCDEFGHIJKABCDEFGHIJKABCDEFGHIJKABCDEFGHIJKABCDEFGHIJKABCDEFGHIJKABCDEFGHIJKABCDEFGHIJKABCDEFGHIJKABCDEFGHIJK" +} + +threshold_evaluate_30: { + options = { + evaluate: 30, + reduce_vars: true, + toplevel: true, + unused: true, + } + input: { + function b(x) { + return x + x + x; + } + console.log(b("1"), b(2), b(b(b("ABCDEFGHIJK")))); + } + expect: { + function b(x) { + return x + x + x; + } + console.log("111", 6, b(b("ABCDEFGHIJKABCDEFGHIJKABCDEFGHIJK"))); + } + expect_stdout: "111 6 ABCDEFGHIJKABCDEFGHIJKABCDEFGHIJKABCDEFGHIJKABCDEFGHIJKABCDEFGHIJKABCDEFGHIJKABCDEFGHIJKABCDEFGHIJKABCDEFGHIJKABCDEFGHIJKABCDEFGHIJKABCDEFGHIJKABCDEFGHIJKABCDEFGHIJKABCDEFGHIJKABCDEFGHIJKABCDEFGHIJKABCDEFGHIJKABCDEFGHIJKABCDEFGHIJKABCDEFGHIJKABCDEFGHIJKABCDEFGHIJKABCDEFGHIJKABCDEFGHIJKABCDEFGHIJK" +} + +threshold_evaluate_100: { + options = { + evaluate: 100, + reduce_vars: true, + toplevel: true, + unused: true, + } + input: { + function b(x) { + return x + x + x; + } + console.log(b("1"), b(2), b(b(b("ABCDEFGHIJK")))); + } + expect: { + function b(x) { + return x + x + x; + } + console.log("111", 6, b("ABCDEFGHIJKABCDEFGHIJKABCDEFGHIJKABCDEFGHIJKABCDEFGHIJKABCDEFGHIJKABCDEFGHIJKABCDEFGHIJKABCDEFGHIJK")); + } + expect_stdout: "111 6 ABCDEFGHIJKABCDEFGHIJKABCDEFGHIJKABCDEFGHIJKABCDEFGHIJKABCDEFGHIJKABCDEFGHIJKABCDEFGHIJKABCDEFGHIJKABCDEFGHIJKABCDEFGHIJKABCDEFGHIJKABCDEFGHIJKABCDEFGHIJKABCDEFGHIJKABCDEFGHIJKABCDEFGHIJKABCDEFGHIJKABCDEFGHIJKABCDEFGHIJKABCDEFGHIJKABCDEFGHIJKABCDEFGHIJKABCDEFGHIJKABCDEFGHIJKABCDEFGHIJKABCDEFGHIJK" +} + +threshold_evaluate_999: { + options = { + evaluate: 999, + reduce_vars: true, + toplevel: true, + unused: true, + } + input: { + function b(x) { + return x + x + x; + } + console.log(b("1"), b(2), b(b(b("ABCDEFGHIJK")))); + } + expect: { + console.log("111", 6, "ABCDEFGHIJKABCDEFGHIJKABCDEFGHIJKABCDEFGHIJKABCDEFGHIJKABCDEFGHIJKABCDEFGHIJKABCDEFGHIJKABCDEFGHIJKABCDEFGHIJKABCDEFGHIJKABCDEFGHIJKABCDEFGHIJKABCDEFGHIJKABCDEFGHIJKABCDEFGHIJKABCDEFGHIJKABCDEFGHIJKABCDEFGHIJKABCDEFGHIJKABCDEFGHIJKABCDEFGHIJKABCDEFGHIJKABCDEFGHIJKABCDEFGHIJKABCDEFGHIJKABCDEFGHIJK"); + } + expect_stdout: "111 6 ABCDEFGHIJKABCDEFGHIJKABCDEFGHIJKABCDEFGHIJKABCDEFGHIJKABCDEFGHIJKABCDEFGHIJKABCDEFGHIJKABCDEFGHIJKABCDEFGHIJKABCDEFGHIJKABCDEFGHIJKABCDEFGHIJKABCDEFGHIJKABCDEFGHIJKABCDEFGHIJKABCDEFGHIJKABCDEFGHIJKABCDEFGHIJKABCDEFGHIJKABCDEFGHIJKABCDEFGHIJKABCDEFGHIJKABCDEFGHIJKABCDEFGHIJKABCDEFGHIJKABCDEFGHIJK" +} + +collapse_vars_regexp: { + options = { + booleans: true, + collapse_vars: true, + comparisons: true, + conditionals: true, + dead_code: true, + evaluate: true, + hoist_funs: true, + if_return: true, + join_vars: true, + keep_fargs: true, + loops: false, + reduce_funcs: true, + reduce_vars: true, + side_effects: true, + unsafe_regexp: true, + unused: true, + } + input: { + function f1() { + var k = 9; + var rx = /[A-Z]+/; + return [rx, k]; + } + function f2() { + var rx = /ab*/g; + return function(s) { + return rx.exec(s); + }; + } + function f3() { + var rx = /ab*/g; + return function() { + return rx; + }; + } + (function() { + var result; + var s = "acdabcdeabbb"; + var rx = /ab*/g; + while (result = rx.exec(s)) + console.log(result[0]); + })(); + (function() { + var result; + var s = "acdabcdeabbb"; + var rx = f2(); + while (result = rx(s)) + console.log(result[0]); + })(); + (function() { + var result; + var s = "acdabcdeabbb"; + var rx = f3(); + while (result = rx().exec(s)) + console.log(result[0]); + })(); + } + expect: { + function f1() { + return [/[A-Z]+/, 9]; + } + function f2() { + var rx = /ab*/g; + return function(s) { + return rx.exec(s); + }; + } + function f3() { + var rx = /ab*/g; + return function() { + return rx; + }; + } + (function() { + var result, rx = /ab*/g; + while (result = rx.exec("acdabcdeabbb")) + console.log(result[0]); + })(); + (function() { + var result, rx = f2(); + while (result = rx("acdabcdeabbb")) + console.log(result[0]); + })(); + (function() { + var result, rx = f3(); + while (result = rx().exec("acdabcdeabbb")) + console.log(result[0]); + })(); + } + expect_stdout: [ + "a", + "ab", + "abbb", + "a", + "ab", + "abbb", + "a", + "ab", + "abbb", + ] +} + +issue_3738: { + options = { + evaluate: true, + } + input: { + console.log(1 / (0 + ([] - 1) % 1)); + } + expect: { + console.log(1 / (0 + ([] - 1) % 1)); + } + expect_stdout: "Infinity" +} + +issue_3755: { + options = { + booleans: true, + evaluate: true, + unsafe: true, + unsafe_math: true, + } + input: { + console.log((/4/.exec(1 + (!0 - 5 / "23")) || 0).p); + } + expect: { + console.log((/4/.exec(!0 - 5 / "23" + 1), 0).p); + } + expect_stdout: "undefined" +} + +void_side_effects: { + options = { + evaluate: true, + reduce_vars: true, + toplevel: true, + unused: true, + } + input: { + var a = void console.log("PASS"); + console.log(a); + } + expect: { + console.log("PASS"); + console.log(void 0); + } + expect_stdout: [ + "PASS", + "undefined", + ] +} + +no_returns: { + options = { + evaluate: true, + reduce_vars: true, + toplevel: true, + unused: true, + } + input: { + var a = function() { + console.log("PASS"); + }(); + console.log(a); + } + expect: { + (function() { + console.log("PASS"); + })(); + console.log(void 0); + } + expect_stdout: [ + "PASS", + "undefined", + ] +} + +void_returns: { + options = { + evaluate: true, + reduce_vars: true, + toplevel: true, + unused: true, + } + input: { + var a = function f() { + function g(b) { + if (b) console.log("FAIL"); + } + while (1) { + console.log("PASS"); + try { + if (console) return; + } catch (e) { + return g(e); + } + } + }(); + console.log(a); + } + expect: { + (function() { + function g(b) { + if (b) console.log("FAIL"); + } + while (1) { + console.log("PASS"); + try { + if (console) return; + } catch (e) { + return g(e); + } + } + })(); + console.log(void 0); + } + expect_stdout: [ + "PASS", + "undefined", + ] +} + +void_returns_recursive: { + options = { + evaluate: true, + reduce_vars: true, + toplevel: true, + unused: true, + } + input: { + var a = function f() { + function g(b) { + return f(); + } + while (1) { + console.log("PASS"); + try { + if (console) return; + } catch (e) { + return g(e); + } + } + }(); + console.log(a); + } + expect: { + var a = function f() { + function g(b) { + return f(); + } + while (1) { + console.log("PASS"); + try { + if (console) return; + } catch (e) { + return g(); + } + } + }(); + console.log(a); + } + expect_stdout: [ + "PASS", + "undefined", + ] +} + +issue_3878_1: { + options = { + evaluate: true, + reduce_vars: true, + toplevel: true, + unused: true, + } + input: { + var b = function(a) { + return (a = 0) == (a && this > (a += 0)); + }(); + console.log(b ? "PASS" : "FAIL"); + } + expect: { + console.log(true ? "PASS" : "FAIL"); + } + expect_stdout: "PASS" +} + +issue_3878_2: { + options = { + evaluate: true, + reduce_vars: true, + toplevel: true, + } + input: { + var a = "foo"; + a++ + a; + a && a; + console.log(a); + } + expect: { + var a = "foo"; + a++ + a; + a; + console.log(a); + } + expect_stdout: "NaN" +} + +issue_3882: { + options = { + evaluate: true, + reduce_vars: true, + toplevel: true, + unused: true, + } + input: { + function f(a) { + return console.log(a++), a && this; + } + var b = f(); + console.log(b); + } + expect: { + var b = function(a) { + return console.log(a++), a && this; + }(); + console.log(b); + } + expect_stdout: [ + "NaN", + "NaN", + ] +} + +issue_3887: { + options = { + evaluate: true, + reduce_vars: true, + unused: true, + } + input: { + (function(b) { + try { + b-- && console.log("PASS"); + } catch (a_2) {} + })(1); + } + expect: { + (function(b) { + try { + 1, console.log("PASS"); + } catch (a_2) {} + })(); + } + expect_stdout: "PASS" +} + +issue_3903: { + options = { + evaluate: true, + reduce_vars: true, + toplevel: true, + unused: true, + } + input: { + var a = "PASS"; + function f(b, c) { + return console, c; + } + var d = f(f(), a = a); + console.log(d); + } + expect: { + function f(b, c) { + return console, c; + } + f(f(), "PASS"); + console.log("PASS"); + } + expect_stdout: "PASS" +} + +issue_3905: { + options = { + evaluate: true, + passes: 2, + unused: true, + } + input: { + (function(a, a) { + return console.log(a = 0), a && console.log("FAIL"); + })("foo", 42); + } + expect: { + (function(a, a) { + return console.log(a = 0), a && console.log("FAIL"); + })("foo", 42); + } + expect_stdout: "0" +} + +issue_3920: { + options = { + evaluate: true, + reduce_vars: true, + toplevel: true, + unused: true, + } + input: { + var a = function(b) { + return (b[b = 0] = 0) >= (b ? 0 : 1); + }("foo"); + console.log(a); + } + expect: { + (function(b) { + "foo"[0] = 0; + })(); + console.log(false); + } + expect_stdout: "false" +} + +inlined_increment_prefix: { + options = { + evaluate: true, + inline: true, + reduce_vars: true, + toplevel: true, + } + input: { + var a = 0; + (function() { + ++a; + })(); + console.log(a += 0); + } + expect: { + var a = 0; + void ++a; + console.log(1); + } + expect_stdout: "1" +} + +inlined_increment_postfix: { + options = { + evaluate: true, + inline: true, + reduce_vars: true, + toplevel: true, + } + input: { + var a = 0; + (function() { + a++; + })(); + console.log(a += 0); + } + expect: { + var a = 0; + void a++; + console.log(1); + } + expect_stdout: "1" +} + +compound_assignment_to_property: { + options = { + evaluate: true, + unsafe: true, + } + input: { + 1 + (0..p >>= 0) && console.log("PASS"); + } + expect: { + 1 + (0..p >>= 0), + console.log("PASS"); + } + expect_stdout: "PASS" +} + +issue_2208_assign: { + options = { + evaluate: true, + reduce_vars: true, + toplevel: true, + } + input: { + a = 42; + console.log({ + p: function() { + return function() { + return this.a; + }(); + } + }.p()); + } + expect: { + a = 42; + console.log({ + p: function() { + return function() { + return this.a; + }(); + } + }.p()); + } + expect_stdout: "42" +} + +issue_2208_postfix: { + options = { + evaluate: true, + reduce_vars: true, + toplevel: true, + } + input: { + a = 41; + a++; + console.log({ + p: function() { + return function() { + return this.a; + }(); + } + }.p()); + } + expect: { + a = 41; + a++; + console.log({ + p: function() { + return function() { + return this.a; + }(); + } + }.p()); + } + expect_stdout: "42" +} + +issue_2208_prefix: { + options = { + evaluate: true, + reduce_vars: true, + toplevel: true, + } + input: { + a = 43; + --a; + console.log({ + p: function() { + return function() { + return this.a; + }(); + } + }.p()); + } + expect: { + a = 43; + --a; + console.log({ + p: function() { + return function() { + return this.a; + }(); + } + }.p()); + } + expect_stdout: "42" +} + +issue_3933: { + options = { + evaluate: true, + reduce_vars: true, + unused: true, + } + input: { + (function(a, b) { + a && (b ^= 1) && console.log("PASS"); + })(1); + } + expect: { + (function(a, b) { + 1, 1, console.log("PASS"); + })(); + } + expect_stdout: "PASS" +} + +issue_3935: { + options = { + evaluate: true, + } + input: { + console.log(function f(a) { + return a++; + }()); + } + expect: { + console.log(NaN); + } + expect_stdout: "NaN" +} + +issue_3937: { + options = { + conditionals: true, + evaluate: true, + reduce_vars: true, + toplevel: true, + unsafe: true, + } + input: { + var a = 123; + (a++ + (b = a))[b] ? 0 ? a : b : 0 ? a : b; + console.log(a, b); + } + expect: { + var a = 123; + (a++ + (b = a))[b], 0, b; + console.log(a, b); + } + expect_stdout: "124 124" +} + +issue_3944: { + options = { + collapse_vars: true, + evaluate: true, + inline: true, + reduce_vars: true, + unused: true, + } + input: { + (function() { + function f() { + while (function() { + var a = 0 == (b && b.p), b = console.log(a); + }()); + f; + } + f(); + })(); + } + expect: { + void function f() { + while (0 == void 0, console.log(false), void 0); + f; + }(); + } + expect_stdout: "false" +} + +issue_3953: { + options = { + evaluate: true, + reduce_vars: true, + } + input: { + function f(a) { + (a += 0 * (a = 0)) && console.log("PASS"); + } + f(1); + } + expect: { + function f(a) { + (a += 0 * (a = 0)) && console.log("PASS"); + } + f(1); + } + expect_stdout: "PASS" +} + +issue_3988: { + options = { + evaluate: true, + reduce_vars: true, + toplevel: true, + unsafe: true, + unused: true, + } + input: { + function f(b) { + return ("" + (b &= 0))[b && this]; + } + var a = f(); + console.log(a); + } + expect: { + var a = function(b) { + return ("" + (b &= 0))[b && this]; + }(); + console.log(a); + } + expect_stdout: "0" +} + +operator_in: { + options = { + evaluate: true, + unsafe: true, + } + input: { + Object.prototype.PASS = 0; + console.log(0 in [ 1 ]); + console.log(0 in [ , ]); + console.log(0 / 0 in { NaN: 2 }); + console.log("PASS" in { }); + console.log("FAIL" in { }); + console.log("toString" in { }); + console.log("toString" in { toString: 3 }); + } + expect: { + Object.prototype.PASS = 0; + console.log(true); + console.log(0 in [ , ]); + console.log(true); + console.log("PASS" in { }); + console.log("FAIL" in { }); + console.log("toString" in { }); + console.log("toString" in { toString: 3 }); + } + expect_stdout: [ + "true", + "false", + "true", + "true", + "false", + "true", + "true", + ] +} + +issue_3997: { + options = { + evaluate: true, + reduce_vars: true, + toplevel: true, + } + input: { + var a = function f(b) { + return b[b += this] = b; + }(0); + console.log(typeof a); + } + expect: { + var a = function f(b) { + return b[b += this] = b; + }(0); + console.log(typeof a); + } + expect_stdout: "string" +} + +issue_4035: { + options = { + evaluate: true, + reduce_vars: true, + } + input: { + var a = 0; + (function() { + var b = --a; + console.log(delete (0 + b)); + console.log(delete (1 * b)); + console.log(delete (b + 0)); + console.log(delete (b - 0)); + console.log(delete (b / 1)); + })(); + } + expect: { + var a = 0; + (function() { + var b = --a; + console.log((0 + b, true)); + console.log((1 * b, true)); + console.log((0 + b, true)); + console.log((b - 0, true)); + console.log((b / 1, true)); + })(); + } + expect_stdout: [ + "true", + "true", + "true", + "true", + "true", + ] +} + +issue_4067: { + options = { + evaluate: true, + reduce_vars: true, + unsafe: true, + } + input: { + (function(a) { + (function(b) { + b[0] += 0; + console.log(+a); + })(a); + })([]); + } + expect: { + (function(a) { + (function(b) { + b[0] += 0; + console.log(+a); + })(a); + })([]); + } + expect_stdout: "NaN" +} + +issue_4077: { + options = { + evaluate: true, + unsafe: true, + } + input: { + console.log((a = []) - (a[0]++, 1) || "PASS"); + } + expect: { + console.log((a = []) - (a[0]++, 1) || "PASS"); + } + expect_stdout: "PASS" +} + +issue_4119_1: { + options = { + conditionals: true, + dead_code: true, + evaluate: true, + reduce_vars: true, + toplevel: true, + unsafe: true, + } + input: { + var a, b; + b = a = []; + a[0] += 0; + if (+b + 1) { + console.log("FAIL"); + } else { + console.log("PASS"); + } + } + expect: { + var a, b; + b = a = []; + a[0] += 0; + +b + 1 ? console.log("FAIL") : console.log("PASS"); + } + expect_stdout: "PASS" +} + +issue_4119_2: { + options = { + conditionals: true, + evaluate: true, + reduce_vars: true, + unsafe: true, + } + input: { + var a; + (function(b) { + a[0] += 0; + console.log(+b + 1 ? "FAIL" : "PASS"); + })(a = []); + } + expect: { + var a; + (function(b) { + a[0] += 0; + console.log(+b + 1 ? "FAIL" : "PASS"); + })(a = []); + } + expect_stdout: "PASS" +} + +issue_4119_3: { + options = { + conditionals: true, + evaluate: true, + reduce_vars: true, + toplevel: true, + unsafe: true, + } + input: { + var a, b; + b = a = { + p: 42, + }; + delete a.p; + console.log(b.p ? "FAIL" : "PASS"); + } + expect: { + var a, b; + b = a = { + p: 42, + }; + delete a.p; + console.log(b.p ? "FAIL" : "PASS"); + } + expect_stdout: "PASS" +} + +issue_4119_4: { + options = { + booleans: true, + conditionals: true, + evaluate: true, + reduce_vars: true, + toplevel: true, + } + input: { + var a, b; + b = a = { + p: 42, + }; + delete a.p; + console.log(!b ? "FAIL" : "PASS"); + } + expect: { + var a, b; + b = a = { + p: 42, + }; + delete a.p; + console.log((b, 0, "PASS")); + } + expect_stdout: "PASS" +} + +issue_4214: { + options = { + evaluate: true, + reduce_vars: true, + toplevel: true, + unused: true, + } + input: { + function f(a) { + return function() { + try { + return a; + } finally { + var b = 0; + } + }(a++ && this()); + } + var c = f(); + console.log(c); + } + expect: { + var c = function(a) { + return function() { + try { + return a; + } finally {} + }(a++ && this()); + }(); + console.log(c); + } + expect_stdout: "NaN" +} + +issue_4271: { + options = { + evaluate: true, + unsafe: true, + } + input: { + ({ + p: null, + q: (console.log("foo"), 42), + p: function() {} + })[console.log("bar"), "p"] && console.log("PASS"); + } + expect: { + ({ + p: null, + q: (console.log("foo"), 42), + p: function() {} + })[console.log("bar"), "p"], + console.log("PASS"); + } + expect_stdout: [ + "foo", + "bar", + "PASS", + ] +} + +issue_4393: { + options = { + evaluate: true, + reduce_vars: true, + } + input: { + (function f(a) { + a = "PASS"; + console.log(arguments[0]); + })("FAIL"); + } + expect: { + (function f(a) { + a = "PASS"; + console.log(arguments[0]); + })("FAIL"); + } + expect_stdout: "PASS" +} + +issue_4422: { + options = { + evaluate: true, + reduce_vars: true, + unused: true, + } + input: { + console.log(function f(a) { + a = "FAIL 1"; + arguments[0] = "PASS"; + return a; + }("FAIL 2")); + } + expect: { + console.log(function(a) { + a = "FAIL 1"; + arguments[0] = "PASS"; + return a; + }("FAIL 2")); + } + expect_stdout: "PASS" +} + +issue_4480: { + options = { + evaluate: true, + reduce_vars: true, + unused: true, + } + input: { + var a = function f(b) { + b = "FAIL"; + arguments[0] = "PASS"; + var arguments = 0; + console.log(b); + }(a); + } + expect: { + var a = function(b) { + b = "FAIL"; + arguments[0] = "PASS"; + var arguments = 0; + console.log(b); + }(a); + } + expect_stdout: "PASS" +} + +issue_4552: { + options = { + evaluate: true, + keep_fnames: true, + reduce_vars: true, + toplevel: true, + unused: true, + } + input: { + var a = function f(b) { + return function() { + b++; + try { + return b; + } catch (e) {} + }(); + }(); + console.log(a); + } + expect: { + var a = function f(b) { + return function() { + b++; + try { + return b; + } catch (e) {} + }(); + }(); + console.log(a); + } + expect_stdout: "NaN" +} + +issue_4886_1: { + options = { + evaluate: true, + unsafe: true, + } + input: { + console.log("length" in { + __proto__: function() {}, + length: void 0, + }); + } + expect: { + console.log("length" in { + __proto__: function() {}, + length: void 0, + }); + } + expect_stdout: "true" +} + +issue_4886_2: { + options = { + evaluate: true, + unsafe: true, + } + input: { + console.log("foo" in { + "foo": null, + __proto__: 42, + }); + } + expect: { + console.log("foo" in { + "foo": null, + __proto__: 42, + }); + } + expect_stdout: "true" +} + +issue_5354: { + options = { + evaluate: true, + unsafe: true, + } + input: { + function f(a) { + return +a.toExponential(1); + } + function g(b) { + return 0 + b.toFixed(2); + } + function h(c) { + return 1 * c.toPrecision(3); + } + console.log(typeof f(45), typeof g(67), typeof h(89)); + } + expect: { + function f(a) { + return +a.toExponential(1); + } + function g(b) { + return 0 + b.toFixed(2); + } + function h(c) { + return +c.toPrecision(3); + } + console.log(typeof f(45), typeof g(67), typeof h(89)); + } + expect_stdout: "number string number" +} + +issue_5356: { + options = { + evaluate: true, + inline: true, + reduce_vars: true, + toplevel: true, + unused: true, + } + input: { + console.log(function() { + return a++; + var a = a; + }()); + } + expect: { + console.log(+a); + var a; + } + expect_stdout: "NaN" +} + +issue_5362_1: { + options = { + evaluate: true, + reduce_vars: true, + toplevel: true, + } + input: { + var a = -console; + console.log(delete +a); + } + expect: { + var a = -console; + console.log((+a, true)); + } + expect_stdout: "true" +} + +issue_5362_2: { + options = { + evaluate: true, + reduce_vars: true, + side_effects: true, + toplevel: true, + unsafe: true, + unused: true, + } + input: { + var a = -console; + console.log(delete +a); + } + expect: { + console.log(true); + } + expect_stdout: "true" +} + +issue_5380: { + options = { + evaluate: true, + keep_fnames: true, + reduce_vars: true, + toplevel: true, + unused: true, + } + input: { + var a = function f(b) { + return function g() { + for (b in { PASS: 42 }); + }(), b; + }("FAIL"); + console.log(a); + } + expect: { + var a = function f(b) { + return function g() { + for (b in { PASS: 42 }); + }(), b; + }("FAIL"); + console.log(a); + } + expect_stdout: "PASS" +} + +issue_5558: { + options = { + collapse_vars: true, + evaluate: true, + reduce_vars: true, + sequences: true, + toplevel: true, + } + input: { + var a = 99, b = 0; + a++; + b++; + b += a; + b *= a; + b += a; + console.log(a); + } + expect: { + var a = 99, b = 0; + b++, + b = (b += ++a) * a + a, + console.log(a); + } + expect_stdout: "100" +} diff --git a/test/compress/exponentiation.js b/test/compress/exponentiation.js new file mode 100644 index 00000000000..995dc8c65a9 --- /dev/null +++ b/test/compress/exponentiation.js @@ -0,0 +1,147 @@ +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" +} + +assignment_1: { + input: { + var a = 2; + a **= 5; + console.log(a); + } + expect_exact: "var a=2;a**=5;console.log(a);" + expect_stdout: "32" + node_version: ">=8" +} + +assignment_2: { + input: { + var a = 8n; + a **= a; + console.log(a); + } + expect_exact: "var a=8n;a**=a;console.log(a);" + expect_stdout: "16777216n" + node_version: ">=10.4.0" +} + +evaluate: { + options = { + evaluate: true, + } + input: { + console.log(1 + 2 ** 3 - 4); + } + expect: { + console.log(5); + } + expect_stdout: "5" + node_version: ">=8" +} + +issue_4664: { + options = { + collapse_vars: true, + evaluate: true, + reduce_vars: true, + side_effects: true, + toplevel: true, + unused: true, + } + input: { + function f() { + new function(a) { + console.log(typeof f, a, typeof this); + }((A = 0, (NaN ^ 1) * 2 ** 30), 0); + } + f(); + } + expect: { + (function f() { + new function(a) { + console.log(typeof f, 2 ** 30, typeof this); + }(A = 0); + })(); + } + expect_stdout: "function 1073741824 object" + node_version: ">=8" +} + +issue_4715: { + options = { + evaluate: true, + } + input: { + A = 1; + console.log((-0) ** A + 0); + console.log((-0) ** A - 0); + console.log((-0) ** A * 1); + console.log((-0) ** A / 1); + console.log(Math.pow(-0, A) + 0); + console.log(Math.pow(-0, A) - 0); + console.log(Math.pow(-0, A) * 1); + console.log(Math.pow(-0, A) / 1); + } + expect: { + A = 1; + console.log((-0) ** A + 0); + console.log((-0) ** A); + console.log((-0) ** A * 1); + console.log((-0) ** A); + console.log(Math.pow(-0, A) + 0); + console.log(+Math.pow(-0, A)); + console.log(+Math.pow(-0, A)); + console.log(+Math.pow(-0, A)); + } + expect_stdout: [ + "0", + "-0", + "-0", + "-0", + "0", + "-0", + "-0", + "-0", + ] + node_version: ">=8" +} diff --git a/test/compress/exports.js b/test/compress/exports.js new file mode 100644 index 00000000000..236a8af465b --- /dev/null +++ b/test/compress/exports.js @@ -0,0 +1,582 @@ +refs: { + input: { + export {}; + export { a, b as B, c as case, d as default }; + } + expect_exact: "export{};export{a,b as B,c as case,d as default};" +} + +var_defs: { + input: { + export const a = 1; + export let b = 2, c = 3; + export var { d, e: [] } = f; + } + expect_exact: "export const a=1;export let b=2,c=3;export var{d,e:[]}=f;" +} + +defuns: { + input: { + export class A {} + export function e() {} + export function* f(a) {} + export async function g(b, c) {} + export async function* h({}, ...[]) {} + } + expect_exact: "export class A{}export function e(){}export function*f(a){}export async function g(b,c){}export async function*h({},...[]){}" +} + +defaults: { + input: { + export default 42; + export default async; + export default (x, y) => x * x; + export default class {}; + export default function*(a, b) {}; + export default async function f({ c }, ...[ d ]) {}; + } + expect_exact: "export default 42;export default async;export default(x,y)=>x*x;export default class{}export default function*(a,b){}export default async function f({c},...[d]){}" +} + +defaults_parentheses_1: { + input: { + export default function() { + console.log("FAIL"); + }(console.log("PASS")); + } + expect_exact: 'export default function(){console.log("FAIL")}console.log("PASS");' +} + +defaults_parentheses_2: { + input: { + export default (async function() { + console.log("PASS"); + })(); + } + expect_exact: 'export default(async function(){console.log("PASS")})();' +} + +defaults_parentheses_3: { + input: { + export default (42, "PASS"); + } + expect_exact: 'export default(42,"PASS");' +} + +defaults_parentheses_4: { + input: { + export default (function f() {}); + } + expect_exact: "export default(function f(){});" +} + +defaults_parentheses_5: { + input: { + export default (function(a) { + console.log(a[0]); + }`PASS`); + } + expect_exact: "export default(function(a){console.log(a[0])})`PASS`;" +} + +defaults_parentheses_6: { + options = { + conditionals: true, + } + input: { + export default !function() { + while (!console); + }() ? "PASS" : "FAIL"; + } + expect_exact: 'export default(function(){while(!console);})()?"FAIL":"PASS";' +} + +defaults_regexp: { + input: { + export default /foo/; + } + expect_exact: "export default/foo/;" +} + +foreign: { + input: { + export * from "foo"; + export {} from "bar"; + export * as a from "baz"; + export { default } from "moo"; + export { b, c as case, default as delete, d } from "moz"; + } + expect_exact: 'export*from"foo";export{}from"bar";export*as a from"baz";export{default}from"moo";export{b,c as case,default as delete,d}from"moz";' +} + +non_identifiers: { + beautify = { + quote_style: 3, + } + input: { + export * as "42" from 'foo'; + export { '42', "delete" as 'foo' } from "bar"; + } + expect_exact: "export*as\"42\"from'foo';export{'42',delete as foo}from\"bar\";" +} + +same_quotes: { + beautify = { + beautify: true, + quote_style: 3, + } + input: { + export * from 'foo'; + export {} from "bar"; + } + expect_exact: [ + "export * from 'foo';", + "", + 'export {} from "bar";', + ] +} + +drop_unused: { + options = { + toplevel: true, + unused: true, + } + input: { + export default 42; + export default (x, y) => x * x; + export default class A extends B { get p() { h() } } + export default function*(a, b) {} + export default async function f({ c }, ...[ d ]) {} + export var e; + export function g(x, [ y ], ...z) {} + function h() {} + } + expect: { + export default 42; + export default (x, y) => x * x; + export default class extends B { get p() { h() } } + export default function*(a, b) {} + export default async function({}) {} + export var e; + export function g(x, []) {} + function h() {} + } +} + +mangle: { + rename = false + mangle = { + toplevel: true, + } + input: { + const a = 42; + export let b, { foo: c } = a; + export function f(d, { [b]: e }) { + d(e, f); + } + export default a; + export default async function g(x, ...{ [c]: y }) { + (await x)(g, y); + } + } + expect: { + const t = 42; + export let b, { foo: c } = t; + export function f(t, { [b]: o }) { + t(o, f); + } + export default t; + export default async function e(t, ...{ [c]: o}) { + (await t)(e, o); + } + } +} + +mangle_rename: { + rename = true + mangle = { + toplevel: true, + } + input: { + const a = 42; + export let b, { foo: c } = a; + export function f(d, { [b]: e }) { + d(e, f); + } + export default a; + export default async function g(x, ...{ [c]: y }) { + (await x)(g, y); + } + } + expect: { + const t = 42; + export let b, { foo: c } = t; + export function f(t, { [b]: o }) { + t(o, f); + } + export default t; + export default async function e(t, ...{ [c]: o}) { + (await t)(e, o); + } + } +} + +hoist_exports_1: { + options = { + hoist_exports: true, + } + input: { + export { a }; + export var b; + export function f() {} + } + expect: { + var b; + function f() {} + export { a, b, f }; + } +} + +hoist_exports_2: { + options = { + evaluate: true, + hoist_exports: true, + reduce_vars: true, + toplevel: true, + unused: true, + } + mangle = { + toplevel: true, + } + input: { + const a = 42; + export let bbb, { foo: ccc } = a; + export function fff(d, { [bbb]: e }) { + d(e, fff); + } + export default a; + export default async function g(x, ...{ [ccc]: y }) { + (await x)(g, y); + } + } + expect: { + let e, a = 42["foo"]; + function f(t, { [e]: o }) { + t(o, f); + } + export default 42; + export default async function n(t, ...{ [a]: o }) { + (await t)(n, o); + }; + export { e as bbb, a as ccc, f as fff }; + } +} + +hoist_vars: { + options = { + hoist_vars: true, + } + input: { + var a; + export var b = 42; + } + expect: { + var a; + export var b = 42; + } +} + +keep_return_values: { + options = { + booleans: true, + evaluate: true, + reduce_vars: true, + toplevel: true, + } + input: { + export default function() { + return []; + } + export default function f() { + return null; + } + } + expect: { + export default function() { + return []; + } + export default function f() { + return null; + } + } +} + +in_use: { + options = { + pure_getters: "strict", + reduce_vars: true, + toplevel: true, + unused: true, + } + input: { + export function f() {} + f.prototype.p = 42; + } + expect: { + export function f() {} + f.prototype.p = 42; + } +} + +in_use_default: { + options = { + pure_getters: "strict", + reduce_vars: true, + toplevel: true, + unused: true, + } + input: { + export default function f() {} + f.prototype.p = 42; + } + expect: { + export default function f() {} + f.prototype.p = 42; + } +} + +single_use: { + options = { + reduce_vars: true, + toplevel: true, + unused: true, + } + input: { + export function f() { + console.log("PASS"); + } + f(); + } + expect: { + export function f() { + console.log("PASS"); + } + f(); + } +} + +single_use_default: { + options = { + reduce_vars: true, + toplevel: true, + unused: true, + } + input: { + export default function f() { + console.log("PASS"); + } + f(); + } + expect: { + export default function f() { + console.log("PASS"); + } + f(); + } +} + +single_use_class: { + options = { + reduce_vars: true, + toplevel: true, + unused: true, + } + input: { + export class A {} + A.prototype.p = "PASS"; + } + expect: { + export class A {} + A.prototype.p = "PASS"; + } +} + +single_use_class_default: { + options = { + reduce_vars: true, + toplevel: true, + unused: true, + } + input: { + export default class A {} + A.prototype.p = "PASS"; + } + expect: { + export default class A {} + A.prototype.p = "PASS"; + } +} + +hoist_funs: { + options = { + hoist_funs: true, + } + input: { + export function f() {} + export default async function* g() {} + } + expect_exact: "export function f(){}export default async function*g(){}" +} + +instanceof_default_class: { + options = { + toplevel: true, + unused: true, + } + input: { + export default class A { + f(a) { + return a instanceof A; + } + } + } + expect: { + export default class A { + f(a) { + return a instanceof A; + } + } + } +} + +instanceof_default_function: { + options = { + toplevel: true, + unused: true, + } + input: { + export default function f() { + if (!(this instanceof f)) + throw new Error("must instantiate"); + } + } + expect: { + export default function f() { + if (!(this instanceof f)) + throw new Error("must instantiate"); + } + } +} + +issue_4742_join_vars_1: { + options = { + join_vars: true, + } + input: { + var a = 42; + export var a; + } + expect: { + var a = 42; + export var a; + } +} + +issue_4742_join_vars_2: { + options = { + join_vars: true, + } + input: { + export var a = "foo"; + var b; + b = "bar"; + } + expect: { + export var a = "foo"; + var b, b = "bar"; + } +} + +issue_4742_unused_1: { + options = { + unused: true, + } + input: { + var a = 42; + export var a; + } + expect: { + var a = 42; + export var a; + } +} + +issue_4742_unused_2: { + options = { + reduce_vars: true, + toplevel: true, + unused: true, + } + input: { + export var a = "foo"; + var a = "bar"; + } + expect: { + export var a = "foo"; + a = "bar"; + } +} + +issue_4761: { + input: { + export default "function" == 42; + } + expect_exact: 'export default"function"==42;' +} + +issue_4766: { + options = { + unused: true, + } + input: { + var a = "foo"; + export var a = "bar"; + } + expect: { + var a = "foo"; + export var a = "bar"; + } +} + +issue_5444: { + options = { + unused: true, + } + input: { + export var a = (console, console); + } + expect: { + console; + export var a = console; + } +} + +issue_5628: { + options = { + unused: true, + } + input: { + var a; + export default function f() { + for (a in 42); + } + console.log(a); + } + expect: { + var a; + export default function f() { + for (a in 42); + } + console.log(a); + } +} diff --git a/test/compress/functions.js b/test/compress/functions.js new file mode 100644 index 00000000000..e0d504d7c23 --- /dev/null +++ b/test/compress/functions.js @@ -0,0 +1,8819 @@ +non_ascii_function_identifier_name: { + input: { + function fooλ(δλ) {} + function λ(δλ) {} + (function λ(δλ) {})() + } + expect_exact: "function fooλ(δλ){}function λ(δλ){}(function λ(δλ){})();" +} + +iifes_returning_constants_keep_fargs_true: { + options = { + booleans: true, + collapse_vars: true, + comparisons: true, + conditionals: true, + dead_code: true, + evaluate: true, + if_return: true, + inline: true, + join_vars: true, + keep_fargs: true, + reduce_funcs: true, + reduce_vars: true, + side_effects: true, + unused: true, + } + input: { + (function(){ return -1.23; }()); + console.log( function foo(){ return "okay"; }() ); + console.log( function foo(x, y, z){ return 123; }() ); + console.log( function(x, y, z){ return z; }() ); + console.log( function(x, y, z){ if (x) return y; return z; }(1, 2, 3) ); + console.log( function(x, y){ return x * y; }(2, 3) ); + console.log( function(x, y){ return x * y; }(2, 3, a(), b()) ); + } + expect: { + console.log("okay"); + console.log(123); + console.log(void 0); + console.log(2); + console.log(6); + console.log((a(), b(), 6)); + } + expect_stdout: true +} + +iifes_returning_constants_keep_fargs_false: { + options = { + booleans: true, + collapse_vars: true, + comparisons: true, + conditionals: true, + dead_code: true, + evaluate: true, + if_return: true, + inline: true, + join_vars: true, + keep_fargs: false, + reduce_funcs: true, + reduce_vars: true, + side_effects: true, + unused: true, + } + input: { + (function(){ return -1.23; }()); + console.log( function foo(){ return "okay"; }() ); + console.log( function foo(x, y, z){ return 123; }() ); + console.log( function(x, y, z){ return z; }() ); + console.log( function(x, y, z){ if (x) return y; return z; }(1, 2, 3) ); + console.log( function(x, y){ return x * y; }(2, 3) ); + console.log( function(x, y){ return x * y; }(2, 3, a(), b()) ); + } + expect: { + console.log("okay"); + console.log(123); + console.log(void 0); + console.log(2); + console.log(6); + console.log((a(), b(), 6)); + } + expect_stdout: true +} + +issue_485_crashing_1530: { + options = { + conditionals: true, + dead_code: true, + evaluate: true, + inline: true, + side_effects: true, + } + input: { + (function(a) { + if (true) return; + var b = 42; + })(this); + } + expect: {} +} + +issue_1841_1: { + options = { + keep_fargs: false, + pure_getters: "strict", + reduce_funcs: true, + reduce_vars: true, + unused: true, + } + input: { + var b = 10; + !function(arg) { + for (var key in "hi") + var n = arg.baz, n = [ b = 42 ]; + }(--b); + console.log(b); + } + expect: { + var b = 10; + !function() { + for (var key in "hi") + b = 42; + }(--b); + console.log(b); + } + expect_exact: "42" +} + +issue_1841_2: { + options = { + keep_fargs: false, + pure_getters: false, + reduce_funcs: true, + reduce_vars: true, + unused: true, + } + input: { + var b = 10; + !function(arg) { + for (var key in "hi") + var n = arg.baz, n = [ b = 42 ]; + }(--b); + console.log(b); + } + expect: { + var b = 10; + !function(arg) { + for (var key in "hi") + arg.baz, b = 42; + }(--b); + console.log(b); + } + expect_exact: "42" +} + +function_returning_constant_literal: { + options = { + inline: true, + passes: 2, + properties: true, + reduce_funcs: true, + reduce_vars: true, + side_effects: true, + toplevel: true, + unused: true, + } + input: { + function greeter() { + return { message: 'Hello there' }; + } + var greeting = greeter(); + console.log(greeting.message); + } + expect: { + console.log("Hello there"); + } + expect_stdout: "Hello there" +} + +hoist_funs: { + options = { + hoist_funs: true, + } + input: { + console.log(1, typeof f, typeof g); + if (console.log(2, typeof f, typeof g)) + console.log(3, typeof f, typeof g); + else { + console.log(4, typeof f, typeof g); + function f() {} + console.log(5, typeof f, typeof g); + } + function g() {} + console.log(6, typeof f, typeof g); + } + expect: { + function f() {} + function g() {} + console.log(1, typeof f, typeof g); + if (console.log(2, typeof f, typeof g)) + console.log(3, typeof f, typeof g); + else { + console.log(4, typeof f, typeof g); + console.log(5, typeof f, typeof g); + } + console.log(6, typeof f, typeof g); + } + expect_stdout: [ + "1 'function' 'function'", + "2 'function' 'function'", + "4 'function' 'function'", + "5 'function' 'function'", + "6 'function' 'function'", + ] + node_version: "<=4" +} + +issue_203: { + options = { + keep_fargs: false, + side_effects: true, + unsafe_Function: true, + unused: true, + } + input: { + var m = {}; + var fn = Function("require", "module", "exports", "module.exports = 42;"); + fn(null, m, m.exports); + console.log(m.exports); + } + expect: { + var m = {}; + var fn = Function("n,o,t", "o.exports=42"); + fn(null, m, m.exports); + console.log(m.exports); + } + expect_stdout: "42" +} + +issue_2084: { + options = { + collapse_vars: true, + conditionals: true, + evaluate: true, + inline: true, + passes: 2, + reduce_funcs: true, + reduce_vars: true, + sequences: true, + side_effects: true, + unused: true, + } + input: { + var c = 0; + !function() { + !function(c) { + c = 1 + c; + var c = 0; + function f14(a_1) { + if (c = 1 + c, 0 !== 23..toString()) + c = 1 + c, a_1 && (a_1[0] = 0); + } + f14(); + }(-1); + }(); + console.log(c); + } + expect: { + var c = 0; + 23..toString(), + console.log(c); + } + expect_stdout: "0" +} + +issue_2097: { + options = { + negate_iife: true, + reduce_funcs: true, + reduce_vars: true, + toplevel: true, + unused: true, + } + input: { + function f() { + try { + throw 0; + } catch (e) { + console.log(arguments[0]); + } + } + f(1); + } + expect: { + !function() { + try { + throw 0; + } catch (e) { + console.log(arguments[0]); + } + }(1); + } + expect_stdout: "1" +} + +issue_2101: { + options = { + inline: true, + } + input: { + a = {}; + console.log(function() { + return function() { + return this.a; + }(); + }() === function() { + return a; + }()); + } + expect: { + a = {}; + console.log(function() { + return this.a; + }() === a); + } + expect_stdout: "true" +} + +inner_ref: { + options = { + inline: true, + unused: true, + } + input: { + console.log(function(a) { + return function() { + return a; + }(); + }(1), function(a) { + return function(a) { + return a; + }(); + }(2)); + } + expect: { + console.log(1, void 0); + } + expect_stdout: "1 undefined" +} + +issue_2107: { + options = { + assignments: true, + collapse_vars: true, + inline: true, + passes: 3, + sequences: true, + side_effects: true, + unused: true, + } + input: { + var c = 0; + !function() { + c++; + }(c++ + new function() { + this.a = 0; + var a = (c = c + 1) + (c = 1 + c); + return c++ + a; + }()); + console.log(c); + } + expect: { + var c = 0; + c++, new function() { + this.a = 0, c = 1 + (c += 1), c++; + }(), c++, console.log(c); + } + expect_stdout: "5" +} + +issue_2114_1: { + options = { + assignments: true, + collapse_vars: true, + if_return: true, + inline: true, + keep_fargs: false, + side_effects: true, + unused: true, + } + input: { + var c = 0; + !function(a) { + a = 0; + }([ { + 0: c = c + 1, + length: c = 1 + c + }, typeof void function a() { + var b = function f1(a) { + }(b && (b.b += (c = c + 1, 0))); + }() ]); + console.log(c); + } + expect: { + var c = 0; + c = 1 + (c += 1), function() { + var b = void (b && (b.b += (c += 1, 0))); + }(); + console.log(c); + } + expect_stdout: "2" +} + +issue_2114_2: { + options = { + assignments: true, + collapse_vars: true, + if_return: true, + inline: true, + keep_fargs: false, + passes: 2, + side_effects: true, + unused: true, + } + input: { + var c = 0; + !function(a) { + a = 0; + }([ { + 0: c = c + 1, + length: c = 1 + c + }, typeof void function a() { + var b = function f1(a) { + }(b && (b.b += (c = c + 1, 0))); + }() ]); + console.log(c); + } + expect: { + var c = 0; + c = 1 + (c += 1), function() { + var b = void (b && (b.b += (c += 1, 0))); + }(); + console.log(c); + } + expect_stdout: "2" +} + +issue_2428: { + options = { + collapse_vars: true, + inline: true, + passes: 3, + pure_getters: "strict", + reduce_funcs: true, + reduce_vars: true, + side_effects: true, + toplevel: true, + unsafe: true, + unused: true, + } + input: { + function bar(k) { + console.log(k); + } + function foo(x) { + return bar(x); + } + function baz(a) { + foo(a); + } + baz(42); + baz("PASS"); + } + expect: { + function baz(a) { + console.log(a); + } + baz(42); + baz("PASS"); + } + expect_stdout: [ + "42", + "PASS", + ] +} + +issue_2531_1: { + options = { + evaluate: true, + inline: true, + reduce_funcs: true, + reduce_vars: true, + unused: true, + } + input: { + function outer() { + function inner(value) { + function closure() { + return value; + } + return function() { + return closure(); + }; + } + return inner("Hello"); + } + console.log("Greeting:", outer()()); + } + expect: { + function outer() { + return value = "Hello", function() { + return value; + }; + var value; + } + console.log("Greeting:", outer()()); + } + expect_stdout: "Greeting: Hello" +} + +issue_2531_2: { + options = { + evaluate: true, + inline: true, + passes: 2, + reduce_funcs: true, + reduce_vars: true, + side_effects: true, + unused: true, + } + input: { + function outer() { + function inner(value) { + function closure() { + return value; + } + return function() { + return closure(); + }; + } + return inner("Hello"); + } + console.log("Greeting:", outer()()); + } + expect: { + function outer() { + return function() { + return "Hello"; + }; + } + console.log("Greeting:", outer()()); + } + expect_stdout: "Greeting: Hello" +} + +issue_2531_3: { + options = { + evaluate: true, + inline: true, + passes: 2, + reduce_funcs: true, + reduce_vars: true, + sequences: true, + side_effects: true, + toplevel: true, + unused: true, + } + input: { + function outer() { + function inner(value) { + function closure() { + return value; + } + return function() { + return closure(); + }; + } + return inner("Hello"); + } + console.log("Greeting:", outer()()); + } + expect: { + console.log("Greeting:", "Hello"); + } + expect_stdout: "Greeting: Hello" +} + +empty_body: { + options = { + reduce_vars: true, + side_effects: true, + } + input: { + function f() { + function noop() {} + noop(); + return noop; + } + } + expect: { + function f() { + function noop() {} + return noop; + } + } +} + +inline_binary_and: { + options = { + inline: true, + } + input: { + console.log(function() { + (function() { + while (console.log("foo")); + return "bar"; + })() && (function() { + while (console.log("baz")); + return "moo"; + })(); + }()); + } + expect: { + console.log(function() { + if (function() { + while (console.log("foo")); + return "bar"; + }()) { + while (console.log("baz")); + return void "moo"; + return; + } else + return void 0; + }()); + } + expect_stdout: [ + "foo", + "baz", + "undefined", + ] +} + +inline_binary_or: { + options = { + inline: true, + } + input: { + (function() { + while (console.log("foo")); + })() || (function() { + while (console.log("bar")); + })(); + } + expect: { + if (!function() { + while (console.log("foo")); + }()) + while (console.log("bar")); + } + expect_stdout: [ + "foo", + "bar", + ] +} + +inline_conditional: { + options = { + inline: true, + } + input: { + (function() { + while (console.log("foo")); + })() ? (function() { + while (console.log("bar")); + })() : (function() { + while (console.log("baz")); + })(); + } + expect: { + if (function() { + while (console.log("foo")); + }()) + while (console.log("bar")); + else + while (console.log("baz")); + } + expect_stdout: [ + "foo", + "baz", + ] +} + +inline_do: { + options = { + inline: true, + } + input: { + do (function() { + while (console.log("foo")); + })(); + while (function() { + while (console.log("bar")); + }()); + } + expect: { + do { + while (console.log("foo")); + } while (function() { + while (console.log("bar")); + }()); + } + expect_stdout: [ + "foo", + "bar", + ] +} + +inline_finally_return: { + options = { + inline: true, + } + input: { + console.log(function() { + try { + throw "FAIL"; + } finally { + return function() { + while (console.log("PASS")); + }(), 42; + } + }()); + } + expect: { + console.log(function() { + try { + throw "FAIL"; + } finally { + while (console.log("PASS")); + return 42; + } + }()); + } + expect_stdout: [ + "PASS", + "42", + ] +} + +inline_for_init: { + options = { + inline: true, + } + input: { + for (function() { + while (console.log("foo")); + }(); function() { + while (console.log("bar")); + }(); function() { + while (console.log("baz")); + }()) (function() { + while (console.log("moo")); + })(); + } + expect: { + while (console.log("foo")); + for (; function() { + while (console.log("bar")); + }(); function() { + while (console.log("baz")); + }()) { + while (console.log("moo")); + } + } + expect_stdout: [ + "foo", + "bar", + ] +} + +inline_for_object: { + options = { + inline: true, + } + input: { + for (var a in function() { + while (console.log("foo")); + }(), function() { + while (console.log("bar")); + }()) (function() { + while (console.log("baz")); + })(); + } + expect: { + while (console.log("foo")); + for (var a in function() { + while (console.log("bar")); + }()) { + while (console.log("baz")); + } + } + expect_stdout: [ + "foo", + "bar", + ] +} + +inline_if_else: { + options = { + inline: true, + } + input: { + if (function() { + while (console.log("foo")); + }(), function() { + while (console.log("bar")); + }()) (function() { + while (console.log("baz")); + })(); + else (function() { + while (console.log("moo")); + })(); + } + expect: { + while (console.log("foo")); + if (function() { + while (console.log("bar")); + }()) { + while (console.log("baz")); + } else { + while (console.log("moo")); + } + } + expect_stdout: [ + "foo", + "bar", + "moo", + ] +} + +inline_label: { + options = { + inline: true, + } + input: { + L: (function() { + while (console.log("PASS")); + })() + } + expect: { + L: { + while (console.log("PASS")); + } + } + expect_stdout: "PASS" +} + +inline_loop_1: { + options = { + inline: true, + reduce_vars: true, + toplevel: true, + unused: true, + } + input: { + function f() { + return x(); + } + for (;;) f(); + } + expect: { + for (;;) x(); + } +} + +inline_loop_2: { + options = { + inline: true, + reduce_vars: true, + toplevel: true, + unused: true, + } + input: { + for (;;) f(); + function f() { + return x(); + } + } + expect: { + for (;;) x(); + } +} + +inline_loop_3: { + options = { + inline: true, + reduce_vars: true, + toplevel: true, + unused: true, + } + input: { + var f = function() { + return x(); + }; + for (;;) f(); + } + expect: { + for (;;) x(); + } +} + +inline_loop_4: { + options = { + inline: true, + reduce_vars: true, + toplevel: true, + unused: true, + } + input: { + for (;;) f(); + var f = function() { + return x(); + }; + } + expect: { + for (;;) f(); + var f = function() { + return x(); + }; + } +} + +inline_loop_5: { + options = { + inline: true, + toplevel: true, + } + input: { + for (var a in "foo") { + (function() { + function f() {} + var f; + console.log(typeof f, a - f); + })(); + } + } + expect: { + for (var a in "foo") + f = void 0, + f = function() {}, + void console.log(typeof f, a - f); + var f; + } + expect_stdout: [ + "function NaN", + "function NaN", + "function NaN", + ] +} + +inline_loop_6: { + options = { + inline: true, + toplevel: true, + } + input: { + for (var a in "foo") { + (function() { + var f; + function f() {} + console.log(typeof f, a - f); + })(); + } + } + expect: { + for (var a in "foo") + f = void 0, + f = function() {}, + void console.log(typeof f, a - f); + var f; + } + expect_stdout: [ + "function NaN", + "function NaN", + "function NaN", + ] +} + +inline_loop_7: { + options = { + inline: true, + toplevel: true, + } + input: { + for (var a in "foo") { + (function() { + function f() {} + var f; + while (console.log(typeof f, a - f)); + })(); + } + } + expect: { + for (var a in "foo") { + f = void 0; + var f = function() {}; + var f; + while (console.log(typeof f, a - f)); + } + } + expect_stdout: [ + "function NaN", + "function NaN", + "function NaN", + ] +} + +inline_loop_8: { + options = { + inline: true, + toplevel: true, + } + input: { + for (var a in "foo") { + (function() { + var f; + function f() {} + while (console.log(typeof f, a - f)); + })(); + } + } + expect: { + for (var a in "foo") { + f = void 0; + var f = function() {}; + var f; + while (console.log(typeof f, a - f)); + } + } + expect_stdout: [ + "function NaN", + "function NaN", + "function NaN", + ] +} + +inline_loop_9: { + options = { + inline: true, + toplevel: true, + } + input: { + for (var a = 0; a < 2; a++) { + (function() { + var b = b && b[console.log("FAIL")] || "PASS"; + while (console.log(b)); + })(); + } + } + expect: { + for (var a = 0; a < 2; a++) { + b = void 0; + var b = b && b[console.log("FAIL")] || "PASS"; + while (console.log(b)); + } + } + expect_stdout: [ + "PASS", + "PASS", + ] +} + +inline_negate_iife: { + options = { + inline: true, + } + input: { + console.log(function() { + return !function() { + while (!console); + }(); + }()); + } + expect: { + console.log(function() { + while (!console); + return !void 0; + }()); + } + expect_stdout: "true" +} + +inline_return_binary: { + options = { + inline: true, + } + input: { + console.log(function() { + return function() { + while (console.log("foo")); + return "bar"; + }() || function() { + while (console.log("baz")); + return "moo"; + }(); + }()); + } + expect: { + console.log(function() { + while (console.log("foo")); + return "bar"; + }() || function() { + while (console.log("baz")); + return "moo"; + }()); + } + expect_stdout: [ + "foo", + "bar", + ] +} + +inline_return_conditional: { + options = { + inline: true, + } + input: { + console.log(function() { + return console ? "foo" : function() { + while (console.log("bar")); + return "baz"; + }(); + }()); + } + expect: { + console.log(function() { + if (console) + return "foo"; + else { + while (console.log("bar")); + return "baz"; + return; + } + }()); + } + expect_stdout: "foo" +} + +inline_while: { + options = { + inline: true, + } + input: { + while (function() { + while (console.log("foo")); + }()) (function() { + while (console.log("bar")); + })(); + } + expect: { + while (function() { + while (console.log("foo")); + }()) { + while (console.log("bar")); + } + } + expect_stdout: "foo" +} + +inline_with: { + options = { + inline: true, + } + input: { + with (+function() { + while (console.log("foo")); + }(), -function() { + while (console.log("bar")); + }()) ~function() { + while (console.log("baz")); + }(); + } + expect: { + while (console.log("foo")); + with (-function() { + while (console.log("bar")); + }()) { + while (console.log("baz")); + } + } + expect_stdout: [ + "foo", + "bar", + "baz", + ] +} + +issue_2476: { + options = { + inline: true, + reduce_vars: true, + toplevel: true, + unused: true, + } + input: { + function foo(x, y, z) { + return x < y ? x * y + z : x * z - y; + } + for (var sum = 0, i = 0; i < 10; i++) + sum += foo(i, i + 1, 3 * i); + console.log(sum); + } + expect: { + for (var sum = 0, i = 0; i < 10; i++) + sum += (x = i, y = i + 1, z = 3 * i, x < y ? x * y + z : x * z - y); + var x, y, z; + console.log(sum); + } + expect_stdout: "465" +} + +issue_2601_1: { + options = { + inline: true, + reduce_vars: true, + sequences: true, + side_effects: true, + unused: true, + } + input: { + var a = "FAIL"; + (function() { + function f(b) { + function g(b) { + b && b(); + } + g(); + (function() { + b && (a = "PASS"); + })(); + } + f("foo"); + })(); + console.log(a); + } + expect: { + var a = "FAIL"; + (function() { + var b; + b = "foo", + function(b) { + b && b(); + }(), + b && (a = "PASS"); + })(), + console.log(a); + } + expect_stdout: "PASS" +} + +issue_2601_2: { + rename = true + options = { + evaluate: true, + inline: true, + passes: 3, + reduce_vars: true, + sequences: true, + side_effects: true, + unused: true, + } + mangle = {} + input: { + var a = "FAIL"; + (function() { + function f(b) { + function g(b) { + b && b(); + } + g(); + (function() { + b && (a = "PASS"); + })(); + } + f("foo"); + })(); + console.log(a); + } + expect: { + var a = "FAIL"; + a = "PASS", + console.log(a); + } + expect_stdout: "PASS" +} + +issue_2604_1: { + options = { + inline: true, + side_effects: true, + unused: true, + } + input: { + var a = "FAIL"; + (function() { + try { + throw 1; + } catch (b) { + (function f(b) { + b && b(); + })(); + b && (a = "PASS"); + } + })(); + console.log(a); + } + expect: { + var a = "FAIL"; + try { + throw 1; + } catch (b) { + (function(b) { + b && b(); + })(); + b && (a = "PASS"); + } + console.log(a); + } + expect_stdout: "PASS" +} + +issue_2604_2: { + rename = true + options = { + evaluate: true, + inline: true, + passes: 3, + reduce_vars: true, + side_effects: true, + unused: true, + } + mangle = {} + input: { + var a = "FAIL"; + (function() { + try { + throw 1; + } catch (b) { + (function f(b) { + b && b(); + })(); + b && (a = "PASS"); + } + })(); + console.log(a); + } + expect: { + var a = "FAIL"; + try { + throw 1; + } catch (o) { + o && (a = "PASS"); + } + console.log(a); + } + expect_stdout: "PASS" +} + +unsafe_apply_1: { + options = { + inline: true, + passes: 2, + reduce_vars: true, + side_effects: true, + unsafe: true, + unused: true, + } + input: { + (function(a, b) { + console.log(a, b); + }).apply("foo", [ "bar" ]); + (function(a, b) { + console.log(this, a, b); + }).apply("foo", [ "bar" ]); + (function(a, b) { + console.log(a, b); + }).apply("foo", [ "bar" ], "baz"); + } + expect: { + console.log("bar", void 0); + (function(a, b) { + console.log(this, a, b); + }).call("foo", "bar"); + (function(a, b) { + console.log(a, b); + }).apply("foo", [ "bar" ], "baz"); + } + expect_stdout: true +} + +unsafe_apply_2: { + options = { + reduce_vars: true, + side_effects: true, + toplevel: true, + unsafe: true, + } + input: { + function foo() { + console.log(a, b); + } + var bar = function(a, b) { + console.log(this, a, b); + } + (function() { + foo.apply("foo", [ "bar" ]); + bar.apply("foo", [ "bar" ]); + })(); + } + expect: { + function foo() { + console.log(a, b); + } + var bar = function(a, b) { + console.log(this, a, b); + } + (function() { + foo("bar"); + bar.call("foo", "bar"); + })(); + } + expect_stdout: true +} + +unsafe_call_1: { + options = { + inline: true, + passes: 2, + reduce_vars: true, + side_effects: true, + unsafe: true, + unused: true, + } + input: { + (function(a, b) { + console.log(a, b); + }).call("foo", "bar"); + (function(a, b) { + console.log(this, a, b); + }).call("foo", "bar"); + } + expect: { + console.log("bar", void 0); + (function(a, b) { + console.log(this, a, b); + }).call("foo", "bar"); + } + expect_stdout: true +} + +unsafe_call_2: { + options = { + reduce_vars: true, + side_effects: true, + toplevel: true, + unsafe: true, + } + input: { + function foo() { + console.log(a, b); + } + var bar = function(a, b) { + console.log(this, a, b); + } + (function() { + foo.call("foo", "bar"); + bar.call("foo", "bar"); + })(); + } + expect: { + function foo() { + console.log(a, b); + } + var bar = function(a, b) { + console.log(this, a, b); + } + (function() { + foo("bar"); + bar.call("foo", "bar"); + })(); + } + expect_stdout: true +} + +unsafe_call_3: { + options = { + side_effects: true, + unsafe: true, + } + input: { + console.log(function() { + return arguments[0] + eval("arguments")[1]; + }.call(0, 1, 2)); + } + expect: { + console.log(function() { + return arguments[0] + eval("arguments")[1]; + }(1, 2)); + } + expect_stdout: "3" +} + +inline_eval_inner: { + options = { + inline: true, + } + input: { + (function() { + console.log(typeof eval("arguments")); + })(); + } + expect: { + (function() { + console.log(typeof eval("arguments")); + })(); + } + expect_stdout: "object" +} + +inline_eval_outer: { + options = { + inline: true, + toplevel: true, + } + input: { + A = 42; + (function(a) { + console.log(a); + })(A); + console.log(eval("typeof a")); + } + expect: { + A = 42; + (function(a) { + console.log(a); + })(A); + console.log(eval("typeof a")); + } + expect_stdout: [ + "42", + "undefined", + ] +} + +issue_2616: { + options = { + evaluate: true, + inline: true, + reduce_vars: true, + side_effects: true, + unused: true, + } + input: { + var c = "FAIL"; + (function() { + function f() { + function g(NaN) { + (true << NaN) - 0/0 || (c = "PASS"); + } + g([]); + } + f(); + })(); + console.log(c); + } + expect: { + var c = "FAIL"; + (true << []) - NaN || (c = "PASS"); + console.log(c); + } + expect_stdout: "PASS" +} + +issue_2620_1: { + options = { + inline: 3, + reduce_vars: true, + sequences: true, + side_effects: true, + unused: true, + } + input: { + var c = "FAIL"; + (function() { + function f(a) { + var b = function g(a) { + a && a(); + }(); + if (a) { + var d = c = "PASS"; + } + } + f(1); + })(); + console.log(c); + } + expect: { + var c = "FAIL"; + !function(a) { + if (function(a) { + a && a(); + }(), a) c = "PASS"; + }(1), + console.log(c); + } + expect_stdout: "PASS" +} + +issue_2620_2: { + options = { + inline: true, + reduce_vars: true, + sequences: true, + side_effects: true, + unused: true, + } + input: { + var c = "FAIL"; + (function() { + function f(a) { + var b = function g(a) { + a && a(); + }(); + if (a) { + var d = c = "PASS"; + } + } + f(1); + })(); + console.log(c); + } + expect: { + var c = "FAIL"; + (function() { + var a = 1; + if (function(a) { + a && a(); + }(), a) c = "PASS"; + })(), + console.log(c); + } + expect_stdout: "PASS" +} + +issue_2620_3: { + options = { + conditionals: true, + evaluate: true, + inline: true, + passes: 2, + reduce_vars: true, + sequences: true, + side_effects: true, + unused: true, + } + input: { + var c = "FAIL"; + (function() { + function f(a) { + var b = function g(a) { + a && a(); + }(); + if (a) { + var d = c = "PASS"; + } + } + f(1); + })(); + console.log(c); + } + expect: { + var c = "FAIL"; + c = "PASS", + console.log(c); + } + expect_stdout: "PASS" +} + +issue_2620_4: { + options = { + evaluate: true, + inline: 3, + reduce_vars: true, + side_effects: true, + unused: true, + } + input: { + var c = "FAIL"; + (function() { + function f(a, NaN) { + function g() { + switch (a) { + case a: + break; + case c = "PASS", NaN: + break; + } + } + g(); + } + f(0/0); + })(); + console.log(c); + } + expect: { + var c = "FAIL"; + !function(a, NaN) { + (function() { + switch (a) { + case a: + break; + case c = "PASS", NaN: + break; + } + })(); + }(NaN); + console.log(c); + } + expect_stdout: "PASS" +} + +issue_2620_5: { + options = { + evaluate: true, + inline: true, + reduce_vars: true, + side_effects: true, + unused: true, + } + input: { + var c = "FAIL"; + (function() { + function f(a, NaN) { + function g() { + switch (a) { + case a: + break; + case c = "PASS", NaN: + break; + } + } + g(); + } + f(0/0); + })(); + console.log(c); + } + expect: { + var c = "FAIL"; + !function(a, NaN) { + switch (a) { + case a: + break; + case c = "PASS", NaN: + break; + } + }(NaN); + console.log(c); + } + expect_stdout: "PASS" +} + +issue_2620_6: { + rename = true + options = { + dead_code: true, + evaluate: true, + inline: true, + passes: 2, + reduce_vars: true, + side_effects: true, + switches: true, + unused: true, + } + input: { + var c = "FAIL"; + (function() { + function f(a, NaN) { + function g() { + switch (a) { + case a: + break; + case c = "PASS", NaN: + break; + } + } + g(); + } + f(0/0); + })(); + console.log(c); + } + expect: { + var c = "FAIL"; + (function() { + switch (NaN) { + case void (c = "PASS"): + } + })(); + console.log(c); + } + expect_stdout: "PASS" +} + +issue_2630_1: { + options = { + collapse_vars: true, + inline: true, + passes: 2, + reduce_funcs: true, + reduce_vars: true, + sequences: true, + side_effects: true, + unused: true, + } + input: { + var c = 0; + (function() { + while (f()); + function f() { + var a = function() { + var b = c++, d = c = 1 + c; + }(); + } + })(); + console.log(c); + } + expect: { + var c = 0; + while (void (c = 1 + ++c)); + console.log(c); + } + expect_stdout: "2" +} + +issue_2630_2: { + options = { + assignments: true, + collapse_vars: true, + inline: true, + passes: 2, + reduce_vars: true, + sequences: true, + side_effects: true, + unused: true, + } + input: { + var c = 0; + !function() { + while (f()) {} + function f() { + var not_used = function() { + c = 1 + c; + }(c = c + 1); + } + }(); + console.log(c); + } + expect: { + var c = 0; + while (void (c = 1 + (c += 1))); + console.log(c); + } + expect_stdout: "2" +} + +issue_2630_3: { + options = { + inline: true, + reduce_vars: true, + unused: true, + } + input: { + var x = 2, a = 1; + (function() { + function f1(a) { + f2(); + --x >= 0 && f1({}); + } + f1(a++); + function f2() { + a++; + } + })(); + console.log(a); + } + expect: { + var x = 2, a = 1; + (function() { + (function f1(a) { + f2(); + --x >= 0 && f1(); + })(a++); + function f2() { + a++; + } + })(); + console.log(a); + } + expect_stdout: "5" +} + +issue_2630_4: { + options = { + collapse_vars: true, + inline: 3, + reduce_vars: true, + side_effects: true, + unused: true, + } + input: { + var x = 3, a = 1, b = 2; + (function() { + (function f1() { + while (--x >= 0 && f2()); + }()); + function f2() { + a++ + (b += a); + } + })(); + console.log(a); + } + expect: { + var x = 3, a = 1, b = 2; + !function() { + while (--x >= 0 && void (b += ++a)); + }(); + console.log(a); + } + expect_stdout: "2" +} + +issue_2630_5: { + options = { + collapse_vars: true, + inline: true, + passes: 2, + reduce_vars: true, + side_effects: true, + unused: true, + } + input: { + var x = 3, a = 1, b = 2; + (function() { + (function f1() { + while (--x >= 0 && f2()); + }()); + function f2() { + a++ + (b += a); + } + })(); + console.log(a); + } + expect: { + var x = 3, a = 1, b = 2; + while (--x >= 0 && void (b += ++a)); + console.log(a); + } + expect_stdout: "2" +} + +issue_2630_6: { + options = { + assignments: true, + collapse_vars: true, + inline: true, + reduce_vars: true, + unused: true, + } + input: { + var c = 1; + !function() { + do { + c *= 10; + } while (f()); + function f() { + return function() { + return (c = 2 + c) < 100; + }(c = c + 3); + } + }(); + console.log(c); + } + expect: { + var c = 1; + !function() { + do { + c *= 10; + } while ((c = 2 + (c += 3)) < 100); + }(); + console.log(c); + } + expect_stdout: "155" +} + +recursive_inline_1: { + options = { + inline: true, + reduce_funcs: true, + reduce_vars: true, + sequences: true, + toplevel: true, + unused: true, + } + input: { + function f() { + h(); + } + function g(a) { + a(); + } + function h(b) { + g(); + if (b) x(); + } + } + expect: {} +} + +recursive_inline_2: { + options = { + reduce_vars: true, + toplevel: true, + unused: true, + } + input: { + function f(n) { + return n ? n * f(n - 1) : 1; + } + console.log(f(5)); + } + expect: { + console.log(function f(n) { + return n ? n * f(n - 1) : 1; + }(5)); + } + expect_stdout: "120" +} + +issue_2657: { + options = { + inline: true, + passes: 2, + reduce_funcs: true, + reduce_vars: true, + sequences: true, + unused: true, + } + input: { + "use strict"; + console.log(function f() { + return h; + function g(b) { + return b || b(); + } + function h(a) { + g(a); + return a; + } + }()(42)); + } + expect: { + "use strict"; + console.log(function(a) { + return b = a, b || b(), a; + var b; + }(42)); + } + expect_stdout: "42" +} + +issue_2663_1: { + options = { + inline: true, + reduce_vars: true, + unused: true, + } + input: { + (function() { + var i, o = {}; + function createFn(j) { + return function() { + console.log(j); + }; + } + for (i in { a: 1, b: 2, c: 3 }) + o[i] = createFn(i); + for (i in o) + o[i](); + })(); + } + expect: { + (function() { + var i, o = {}; + function createFn(j) { + return function() { + console.log(j); + }; + } + for (i in { a: 1, b: 2, c: 3 }) + o[i] = createFn(i); + for (i in o) + o[i](); + })(); + } + expect_stdout: [ + "a", + "b", + "c", + ] +} + +issue_2663_2: { + options = { + inline: true, + reduce_vars: true, + side_effects: true, + unused: true, + } + input: { + (function() { + var i; + function fn(j) { + return function() { + console.log(j); + }(); + } + for (i in { a: 1, b: 2, c: 3 }) + fn(i); + })(); + } + expect: { + (function() { + for (var i in { a: 1, b: 2, c: 3 }) + j = i, console.log(j); + var j; + })(); + } + expect_stdout: [ + "a", + "b", + "c", + ] +} + +issue_2663_3: { + options = { + inline: true, + reduce_vars: true, + unused: true, + } + input: { + (function() { + var outputs = [ + { type: 0, target: null, eventName: "ngSubmit", propName: null }, + { type: 0, target: null, eventName: "submit", propName: null }, + { type: 0, target: null, eventName: "reset", propName: null }, + ]; + function listenToElementOutputs(outputs) { + var handlers = []; + for (var i = 0; i < outputs.length; i++) { + var output = outputs[i]; + var handleEventClosure = renderEventHandlerClosure(output.eventName); + handlers.push(handleEventClosure) + } + var target, name; + return handlers; + } + function renderEventHandlerClosure(eventName) { + return function() { + return console.log(eventName); + }; + } + listenToElementOutputs(outputs).forEach(function(handler) { + return handler() + }); + })(); + } + expect: { + (function() { + function renderEventHandlerClosure(eventName) { + return function() { + return console.log(eventName); + }; + } + (function(outputs) { + var handlers = []; + for (var i = 0; i < outputs.length; i++) { + var output = outputs[i]; + var handleEventClosure = renderEventHandlerClosure(output.eventName); + handlers.push(handleEventClosure); + } + return handlers; + })([ { + type: 0, + target: null, + eventName: "ngSubmit", + propName: null + }, { + type: 0, + target: null, + eventName: "submit", + propName: null + }, { + type: 0, + target: null, + eventName: "reset", + propName: null + } ]).forEach(function(handler) { + return handler(); + }); + })(); + } + expect_stdout: [ + "ngSubmit", + "submit", + "reset", + ] +} + +duplicate_argnames_1: { + options = { + inline: true, + side_effects: true, + } + input: { + console.log(function(a, a, a) { + return a; + }("FAIL", 42, "PASS")); + } + expect: { + console.log("PASS"); + } + expect_stdout: "PASS" +} + +duplicate_argnames_2: { + options = { + inline: true, + reduce_vars: true, + side_effects: true, + toplevel: true, + unused: true, + } + input: { + var a = "PASS"; + function f(b, b, b) { + b && (a = "FAIL"); + } + f(0, console); + console.log(a); + } + expect: { + var a = "PASS"; + console, void 0 && (a = "FAIL"); + console.log(a); + } + expect_stdout: "PASS" +} + +duplicate_argnames_3: { + options = { + inline: true, + reduce_vars: true, + side_effects: true, + toplevel: true, + unused: true, + } + input: { + var a = "FAIL"; + function f(b, b, b) { + b && (a = "PASS"); + } + f(null, 0, console, "42".toString()); + console.log(a); + } + expect: { + var a = "FAIL"; + b = console, "42".toString(), b && (a = "PASS"); + var b; + console.log(a); + } + expect_stdout: "PASS" +} + +duplicate_argnames_4: { + options = { + if_return: true, + inline: true, + } + input: { + (function() { + (function(a, a) { + while (console.log(a || "PASS")); + })("FAIL"); + })(); + } + expect: { + (function() { + var a = "FAIL"; + var a = void 0; + while (console.log(a || "PASS")); + })(); + } + expect_stdout: "PASS" +} + +loop_init_arg: { + options = { + inline: true, + side_effects: true, + toplevel: true, + } + input: { + var a = "PASS"; + for (var k in "12") (function(b) { + (b >>= 1) && (a = "FAIL"), b = 2; + })(); + console.log(a); + } + expect: { + var a = "PASS"; + for (var k in "12") + b = void 0, (b >>= 1) && (a = "FAIL"), b = 2; + var b; + console.log(a); + } + expect_stdout: "PASS" +} + +inline_false: { + options = { + inline: false, + side_effects: true, + toplevel: true, + } + input: { + (function() { + console.log(1); + })(); + (function(a) { + console.log(a); + })(2); + (function(b) { + var c = b; + console.log(c); + })(3); + } + expect: { + (function() { + console.log(1); + })(); + (function(a) { + console.log(a); + })(2); + (function(b) { + var c = b; + console.log(c); + })(3); + } + expect_stdout: [ + "1", + "2", + "3", + ] +} + +inline_0: { + options = { + inline: 0, + side_effects: true, + toplevel: true, + } + input: { + (function() { + console.log(1); + })(); + (function(a) { + console.log(a); + })(2); + (function(b) { + var c = b; + console.log(c); + })(3); + } + expect: { + (function() { + console.log(1); + })(); + (function(a) { + console.log(a); + })(2); + (function(b) { + var c = b; + console.log(c); + })(3); + } + expect_stdout: [ + "1", + "2", + "3", + ] +} + +inline_1: { + options = { + inline: 1, + side_effects: true, + toplevel: true, + } + input: { + (function() { + console.log(1); + })(); + (function(a) { + console.log(a); + })(2); + (function(b) { + var c = b; + console.log(c); + })(3); + } + expect: { + console.log(1); + (function(a) { + console.log(a); + })(2); + (function(b) { + var c = b; + console.log(c); + })(3); + } + expect_stdout: [ + "1", + "2", + "3", + ] +} + +inline_2: { + options = { + inline: 2, + side_effects: true, + toplevel: true, + } + input: { + (function() { + console.log(1); + })(); + (function(a) { + console.log(a); + })(2); + (function(b) { + var c = b; + console.log(c); + })(3); + } + expect: { + console.log(1); + console.log(2); + (function(b) { + var c = b; + console.log(c); + })(3); + } + expect_stdout: [ + "1", + "2", + "3", + ] +} + +inline_3: { + options = { + inline: 3, + side_effects: true, + toplevel: true, + } + input: { + (function() { + console.log(1); + })(); + (function(a) { + console.log(a); + })(2); + (function(b) { + var c = b; + console.log(c); + })(3); + } + expect: { + console.log(1); + console.log(2); + b = 3, c = b, console.log(c); + var b, c; + } + expect_stdout: [ + "1", + "2", + "3", + ] +} + +inline_true: { + options = { + inline: true, + side_effects: true, + toplevel: true, + } + input: { + (function() { + console.log(1); + })(); + (function(a) { + console.log(a); + })(2); + (function(b) { + var c = b; + console.log(c); + })(3); + } + expect: { + console.log(1); + console.log(2); + b = 3, c = b, console.log(c); + var b, c; + } + expect_stdout: [ + "1", + "2", + "3", + ] +} + +use_before_init_in_loop: { + options = { + inline: true, + side_effects: true, + toplevel: true, + } + input: { + var a = "PASS"; + for (var b = 2; --b >= 0;) (function() { + var c = function() { + return 1; + }(c && (a = "FAIL")); + })(); + console.log(a); + } + expect: { + var a = "PASS"; + for (var b = 2; --b >= 0;) + c = void 0, c = (c && (a = "FAIL"), 1); + var c; + console.log(a); + } + expect_stdout: "PASS" +} + +duplicate_arg_var_1: { + options = { + inline: true, + } + input: { + console.log(function(b) { + return b; + var b; + }("PASS")); + } + expect: { + console.log("PASS"); + } + expect_stdout: "PASS" +} + +duplicate_arg_var_2: { + options = { + inline: true, + toplevel: true, + } + input: { + console.log(function(b) { + return b + "SS"; + var b; + }("PA")); + } + expect: { + console.log("PA" + "SS"); + } + expect_stdout: "PASS" +} + +duplicate_arg_var_3: { + options = { + inline: true, + toplevel: true, + } + input: { + console.log(function(b) { + return b + "SS"; + var b; + }("PA", "42".toString())); + } + expect: { + console.log((b = "PA", "42".toString(), b + "SS")); + var b; + } + expect_stdout: "PASS" +} + +issue_2737_1: { + options = { + inline: true, + reduce_vars: true, + unused: true, + } + input: { + (function(a) { + while (a()); + })(function f() { + console.log(typeof f); + }); + } + expect: { + (function(a) { + while (a()); + })(function f() { + console.log(typeof f); + }); + } + expect_stdout: "function" +} + +issue_2737_2: { + options = { + inline: true, + reduce_vars: true, + unused: true, + } + input: { + (function(bar) { + for (;bar();) break; + })(function qux() { + return console.log("PASS"), qux; + }); + } + expect: { + (function(bar) { + for (;bar();) break; + })(function qux() { + return console.log("PASS"), qux; + }); + } + expect_stdout: "PASS" +} + +issue_2783: { + options = { + collapse_vars: true, + conditionals: true, + if_return: true, + inline: true, + reduce_vars: true, + unused: true, + } + input: { + (function() { + return g; + function f(a) { + var b = a.b; + if (b) return b; + return a; + } + function g(o, i) { + while (i--) { + console.log(f(o)); + } + } + })()({ b: "PASS" }, 1); + } + expect: { + (function() { + return function(o,i) { + while (i--) console.log(f(o)); + }; + function f(a) { + var b = a.b; + return b || a; + } + })()({ b: "PASS" },1); + } + expect_stdout: "PASS" +} + +issue_2898: { + options = { + collapse_vars: true, + inline: true, + reduce_vars: true, + sequences: true, + unused: true, + } + input: { + var c = 0; + (function() { + while (f()); + function f() { + var b = (c = 1 + c, void (c = 1 + c)); + b && b[0]; + } + })(); + console.log(c); + } + expect: { + var c = 0; + (function() { + while (b = void 0, void ((b = void (c = 1 + (c = 1 + c))) && b[0])); + var b; + })(), + console.log(c); + } + expect_stdout: "2" +} + +deduplicate_parentheses: { + input: { + ({}).a = b; + (({}).a = b)(); + (function() {}).a = b; + ((function() {}).a = b)(); + } + expect_exact: "({}).a=b;({}.a=b)();(function(){}).a=b;(function(){}.a=b)();" +} + +issue_3016_1: { + options = { + inline: true, + toplevel: true, + } + input: { + var b = 1; + do { + (function(a) { + return a[b]; + var a; + })(3); + } while (0); + console.log(b); + } + expect: { + var b = 1; + do { + 3[b]; + } while (0); + console.log(b); + } + expect_stdout: "1" +} + +issue_3016_2: { + options = { + dead_code: true, + inline: true, + toplevel: true, + } + input: { + var b = 1; + do { + (function(a) { + return a[b]; + try { + a = 2; + } catch (a) { + var a; + } + })(3); + } while (0); + console.log(b); + } + expect: { + var b = 1; + do { + a = 3, + a[b]; + } while (0); + var a; + console.log(b); + } + expect_stdout: "1" +} + +issue_3016_2_ie8: { + options = { + dead_code: true, + ie: true, + inline: true, + toplevel: true, + } + input: { + var b = 1; + do { + (function(a) { + return a[b]; + try { + a = 2; + } catch (a) { + var a; + } + })(3); + } while (0); + console.log(b); + } + expect: { + var b = 1; + do { + a = 3, + a[b]; + } while (0); + var a; + console.log(b); + } + expect_stdout: "1" +} + +issue_3016_3: { + options = { + dead_code: true, + inline: true, + toplevel: true, + } + input: { + var b = 1; + do { + console.log(function() { + return a ? "FAIL" : a = "PASS"; + try { + a = 2; + } catch (a) { + var a; + } + }()); + } while (b--); + } + expect: { + var b = 1; + do { + console.log((a = void 0, a ? "FAIL" : "PASS")); + } while (b--); + var a; + } + expect_stdout: [ + "PASS", + "PASS", + ] +} + +issue_3016_3_ie8: { + options = { + dead_code: true, + ie: true, + inline: true, + toplevel: true, + } + input: { + var b = 1; + do { + console.log(function() { + return a ? "FAIL" : a = "PASS"; + try { + a = 2; + } catch (a) { + var a; + } + }()); + } while (b--); + } + expect: { + var b = 1; + do { + console.log((a = void 0, a ? "FAIL" : "PASS")); + } while (b--); + var a; + } + expect_stdout: [ + "PASS", + "PASS", + ] +} + +issue_3018: { + options = { + inline: true, + side_effects: true, + toplevel: true, + } + input: { + var b = 1, c = "PASS"; + do { + (function() { + (function(a) { + a = 0 != (a && (c = "FAIL")); + })(); + })(); + } while (b--); + console.log(c); + } + expect: { + var b = 1, c = "PASS"; + do { + a = void 0, + a = 0 != (a && (c = "FAIL")); + } while (b--); + var a; + console.log(c); + } + expect_stdout: "PASS" +} + +issue_3054: { + options = { + booleans: true, + collapse_vars: true, + inline: 1, + reduce_vars: true, + toplevel: true, + } + input: { + "use strict"; + function f() { + return { a: true }; + } + console.log(function(b) { + b = false; + return f(); + }().a, f.call().a); + } + expect: { + "use strict"; + function f() { + return { a: !0 }; + } + console.log(function(b) { + b = !1; + return f(); + }().a, f.call().a); + } + expect_stdout: "true true" +} + +issue_3076: { + options = { + dead_code: true, + inline: true, + sequences: true, + unused: true, + } + input: { + var c = "PASS"; + (function(b) { + var n = 2; + while (--b + function() { + e && (c = "FAIL"); + e = 5; + return 1; + try { + var a = 5; + } catch (e) { + var e; + } + }().toString() && --n > 0); + })(2); + console.log(c); + } + expect: { + var c = "PASS"; + (function(b) { + var n = 2; + while (--b + (e = void 0, e && (c = "FAIL"), e = 5, 1..toString()) && --n > 0); + var e; + })(2), + console.log(c); + } + expect_stdout: "PASS" +} + +issue_3125: { + options = { + inline: true, + unsafe: true, + } + input: { + console.log(function() { + return "PASS"; + }.call()); + } + expect: { + console.log("PASS"); + } + expect_stdout: "PASS" +} + +issue_3274: { + options = { + collapse_vars: true, + inline: true, + join_vars: true, + loops: true, + passes: 2, + reduce_vars: true, + unused: true, + } + input: { + (function() { + var g = function(a) { + var c = a.p, b = c; + return b != c; + }; + while (g(1)) + console.log("FAIL"); + console.log("PASS"); + })(); + } + expect: { + (function() { + for (var c; (c = 1..p) != c;) + console.log("FAIL"); + console.log("PASS"); + })(); + } + expect_stdout: "PASS" +} + +issue_3297_1: { + options = { + collapse_vars: true, + inline: true, + reduce_vars: true, + unused: true, + } + mangle = {} + input: { + function function1() { + var r = { + function2: function2 + }; + function function2() { + alert(1234); + function function3() { + function2(); + }; + function3(); + } + return r; + } + } + expect: { + function function1() { + return { + function2: function n() { + alert(1234); + function t() { + n(); + } + t(); + } + }; + } + } +} + +issue_3297_2: { + options = { + collapse_vars: true, + conditionals: true, + inline: true, + reduce_vars: true, + unused: true, + } + mangle = {} + input: { + function function1(session) { + var public = { + processBulk: processBulk + }; + return public; + function processBulk(bulk) { + var subparam1 = session(); + function processOne(param1) { + var param2 = { + subparam1: subparam1 + }; + doProcessOne({ + param1: param1, + param2: param2, + }, function() { + processBulk(bulk); + }); + }; + if (bulk && bulk.length > 0) + processOne(bulk.shift()); + } + function doProcessOne(config, callback) { + console.log(JSON.stringify(config)); + callback(); + } + } + function1(function session() { + return 42; + }).processBulk([1, 2, 3]); + } + expect: { + function function1(o) { + return { + processBulk: function t(u) { + var r = o(); + function n(n) { + var o = { + subparam1: r + }; + c({ + param1: n, + param2: o + }, function() { + t(u); + }); + } + u && u.length > 0 && n(u.shift()); + } + }; + function c(n, o) { + console.log(JSON.stringify(n)); + o(); + } + } + function1(function() { + return 42; + }).processBulk([ 1, 2, 3 ]); + } + expect_stdout: [ + '{"param1":1,"param2":{"subparam1":42}}', + '{"param1":2,"param2":{"subparam1":42}}', + '{"param1":3,"param2":{"subparam1":42}}', + ] +} + +issue_3297_3: { + options = { + collapse_vars: true, + comparisons: true, + conditionals: true, + inline: true, + join_vars: true, + passes: 3, + reduce_funcs: true, + reduce_vars: true, + sequences: true, + side_effects: true, + unused: true, + } + mangle = {} + input: { + function function1(session) { + var public = { + processBulk: processBulk, + }; + return public; + function processBulk(bulk) { + var subparam1 = session(); + function processOne(param1) { + var param2 = { + subparam1: subparam1, + }; + doProcessOne({ + param1: param1, + param2: param2, + }, function() { + processBulk(bulk); + }); + }; + if (bulk && bulk.length > 0) + processOne(bulk.shift()); + } + function doProcessOne(config, callback) { + console.log(JSON.stringify(config)); + callback(); + } + } + function1(function session() { + return 42; + }).processBulk([1, 2, 3]); + } + expect: { + function function1(c) { + return { + processBulk: function n(o) { + var r, t, u = c(); + o && 0 < o.length && (r = o.shift(), + t = function() { + n(o); + }, + console.log(JSON.stringify({ + param1: r, + param2: { + subparam1: u, + }, + })), + t()); + }, + }; + } + function1(function() { + return 42; + }).processBulk([ 1, 2, 3 ]); + } + expect_stdout: [ + '{"param1":1,"param2":{"subparam1":42}}', + '{"param1":2,"param2":{"subparam1":42}}', + '{"param1":3,"param2":{"subparam1":42}}', + ] +} + +cross_references_1: { + options = { + inline: true, + reduce_vars: true, + unused: true, + } + input: { + var Math = { + square: function(n) { + return n * n; + } + }; + console.log((function(factory) { + return factory(); + })(function() { + return function(Math) { + return function(n) { + return Math.square(n); + }; + }(Math); + })(3)); + } + expect: { + var Math = { + square: function(n) { + return n * n; + } + }; + console.log(function(Math) { + return function(n) { + return Math.square(n); + }; + }(Math)(3)); + } + expect_stdout: "9" +} + +cross_references_2: { + options = { + collapse_vars: true, + evaluate: true, + inline: true, + passes: 6, + properties: true, + reduce_vars: true, + sequences: true, + side_effects: true, + toplevel: true, + unused: true, + } + input: { + var Math = { + square: function(n) { + return n * n; + } + }; + console.log((function(factory) { + return factory(); + })(function() { + return function(Math) { + return function(n) { + return Math.square(n); + }; + }(Math); + })(3)); + } + expect: { + console.log(9); + } + expect_stdout: "9" +} + +cross_references_3: { + options = { + inline: true, + reduce_vars: true, + unused: true, + } + input: { + var Math = { + square: function(n) { + return n * n; + }, + cube: function(n) { + return n * n * n; + } + }; + console.log(function(factory) { + return factory(); + }(function() { + return function(Math) { + return function(n) { + Math = { + square: function(x) { + return "(SQUARE" + x + ")"; + }, + cube: function(x) { + return "(CUBE" + x + ")"; + } + }; + return Math.square(n) + Math.cube(n); + }; + }(Math); + })(2)); + console.log(Math.square(3), Math.cube(3)); + } + expect: { + var Math = { + square: function(n) { + return n * n; + }, + cube: function(n) { + return n * n * n; + } + }; + console.log(function(Math) { + return function(n) { + Math = { + square: function(x) { + return "(SQUARE" + x + ")"; + }, + cube: function(x) { + return "(CUBE" + x + ")"; + } + }; + return Math.square(n) + Math.cube(n); + }; + }()(2)); + console.log(Math.square(3), Math.cube(3)); + } + expect_stdout: [ + "(SQUARE2)(CUBE2)", + "9 27", + ] +} + +loop_inline: { + options = { + inline: true, + reduce_vars: true, + unused: true, + } + input: { + console.log(function(o) { + function g(p) { + return o[p]; + } + function h(q) { + while (g(q)); + } + return h; + }([ 1, "foo", 0 ])(2)); + } + expect: { + console.log(function(o) { + return function(q) { + while (p = q, o[p]); + var p; + }; + }([ 1, "foo", 0 ])(2)); + } + expect_stdout: "undefined" +} + +functions: { + options = { + functions: true, + reduce_vars: true, + unused: true, + } + input: { + !function() { + var a = function a() { + return a && "a"; + }; + var b = function x() { + return !!x; + }; + var c = function(c) { + return c; + }; + if (c(b(a()))) { + var d = function() {}; + var e = function y() { + return typeof y; + }; + var f = function(f) { + return f; + }; + console.log(a(d()), b(e()), c(f(42)), typeof d, e(), typeof f); + } + }(); + } + expect: { + !function() { + function a() { + return a && "a"; + } + function b() { + return !!b; + } + function c(c) { + return c; + } + if (c(b(a()))) { + function d() {} + function e() { + return typeof e; + } + function f(f) { + return f; + } + console.log(a(d()), b(e()), c(f(42)), typeof d, e(), typeof f); + } + }(); + } + expect_stdout: "a true 42 function function function" +} + +functions_use_strict: { + options = { + functions: true, + reduce_vars: true, + unused: true, + } + input: { + "use strict"; + !function() { + var a = function a() { + return a && "a"; + }; + var b = function x() { + return !!x; + }; + var c = function(c) { + return c; + }; + if (c(b(a()))) { + var d = function() {}; + var e = function y() { + return typeof y; + }; + var f = function(f) { + return f; + }; + console.log(a(d()), b(e()), c(f(42)), typeof d, e(), typeof f); + } + }(); + } + expect: { + "use strict"; + !function() { + function a() { + return a && "a"; + } + function b() { + return !!b; + } + function c(c) { + return c; + } + if (c(b(a()))) { + var d = function() {}; + var e = function y() { + return typeof y; + }; + var f = function(f) { + return f; + }; + console.log(a(d()), b(e()), c(f(42)), typeof d, e(), typeof f); + } + }(); + } + expect_stdout: "a true 42 function function function" +} + +functions_cross_scope_reference: { + options = { + functions: true, + reduce_vars: true, + toplevel: true, + unused: true, + } + input: { + log = function(fn) { + console.log(typeof fn()); + }; + var a = function() {}; + function f() { + return a; + } + while (log(f)); + } + expect: { + log = function(fn) { + console.log(typeof fn()); + }; + function a() {} + function f() { + return a; + } + while (log(f)); + } + expect_stdout: "function" +} + +functions_inner_var: { + options = { + functions: true, + reduce_vars: true, + toplevel: true, + unused: true, + } + input: { + var a = function() { + var a; + console.log(a, a); + }; + a(a); + } + expect: { + function a() { + var a; + console.log(a, a); + } + a(); + } + expect_stdout: "undefined undefined" +} + +functions_keep_fnames: { + options = { + functions: true, + keep_fnames: true, + reduce_vars: true, + toplevel: true, + unused: true, + } + input: { + var FAIL = function PASS() {}; + FAIL.p = 42; + console.log(FAIL.name, FAIL.p); + } + expect: { + var FAIL = function PASS() {}; + FAIL.p = 42; + console.log(FAIL.name, FAIL.p); + } + expect_stdout: "PASS 42" +} + +issue_2437: { + options = { + collapse_vars: true, + conditionals: true, + functions: true, + if_return: true, + inline: true, + join_vars: true, + passes: 2, + reduce_funcs: true, + reduce_vars: true, + sequences: true, + side_effects: true, + toplevel: true, + unused: true, + } + input: { + function foo() { + return bar(); + } + function bar() { + if (xhrDesc) { + var req = new XMLHttpRequest(); + var result = !!req.onreadystatechange; + Object.defineProperty(XMLHttpRequest.prototype, 'onreadystatechange', xhrDesc || {}); + return result; + } else { + var req = new XMLHttpRequest(); + var detectFunc = function(){}; + req.onreadystatechange = detectFunc; + var result = req[SYMBOL_FAKE_ONREADYSTATECHANGE_1] === detectFunc; + req.onreadystatechange = null; + return result; + } + } + console.log(foo()); + } + expect: { + var req, detectFunc, result; + console.log(( + xhrDesc ? ( + result = !!(req = new XMLHttpRequest).onreadystatechange, + Object.defineProperty(XMLHttpRequest.prototype, "onreadystatechange", xhrDesc || {}) + ) : ( + (req = new XMLHttpRequest).onreadystatechange = detectFunc = function(){}, + result = req[SYMBOL_FAKE_ONREADYSTATECHANGE_1] === detectFunc,req.onreadystatechange = null + ), + result + )); + } +} + +issue_2485_1: { + options = { + functions: true, + passes: 2, + reduce_funcs: true, + reduce_vars: true, + unused: true, + } + input: { + var foo = function(bar) { + var n = function(a, b) { + return a + b; + }; + var sumAll = function(arg) { + return arg.reduce(n, 0); + }; + var runSumAll = function(arg) { + return sumAll(arg); + }; + bar.baz = function(arg) { + var n = runSumAll(arg); + return (n.get = 1), n; + }; + return bar; + }; + var bar = foo({}); + console.log(bar.baz([1, 2, 3])); + } + expect: { + var foo = function(bar) { + function n(a, b) { + return a + b; + } + function runSumAll(arg) { + return function(arg) { + return arg.reduce(n, 0); + }(arg); + } + bar.baz = function(arg) { + var n = runSumAll(arg); + return (n.get = 1), n; + }; + return bar; + }; + var bar = foo({}); + console.log(bar.baz([1, 2, 3])); + } + expect_stdout: "6" +} + +issue_2485_2: { + options = { + functions: true, + inline: true, + passes: 2, + reduce_funcs: true, + reduce_vars: true, + unused: true, + } + input: { + var foo = function(bar) { + var n = function(a, b) { + return a + b; + }; + var sumAll = function(arg) { + return arg.reduce(n, 0); + }; + var runSumAll = function(arg) { + return sumAll(arg); + }; + bar.baz = function(arg) { + var n = runSumAll(arg); + return (n.get = 1), n; + }; + return bar; + }; + var bar = foo({}); + console.log(bar.baz([1, 2, 3])); + } + expect: { + var foo = function(bar) { + function n(a, b) { + return a + b; + } + function runSumAll(arg) { + return arg.reduce(n, 0); + } + bar.baz = function(arg) { + var n = runSumAll(arg); + return (n.get = 1), n; + }; + return bar; + }; + var bar = foo({}); + console.log(bar.baz([1, 2, 3])); + } + expect_stdout: "6" +} + +issue_3364: { + options = { + functions: true, + reduce_vars: true, + toplevel: true, + unused: true, + } + mangle = {} + input: { + var s = 2, a = 100, b = 10, c = 0; + function f(p, e, r) { + try { + for (var i = 1; i-- > 0;) + var a = function(x) { + function g(y) { + y && y[a++]; + } + var x = g(--s >= 0 && f(c++)); + for (var j = 1; --j > 0;); + }(); + } catch (e) { + try { + return; + } catch (z) { + for (var k = 1; --k > 0;) { + for (var l = 1; l > 0; --l) { + var n = function() {}; + for (var k in n) + var o = (n, k); + } + } + } + } + } + var r = f(); + console.log(c); + } + expect: { + var s = 2, c = 0; + (function n(r, o, a) { + try { + for (var f = 1; f-- >0;) + var t = function(r) { + (function(r) { + r && r[t++]; + })(--s >= 0 && n(c++)); + for (var o = 1; --o > 0;); + }(); + } catch (o) { + try { + return; + } catch (r) { + for (var v = 1; --v > 0;) + for (var i = 1; i > 0;--i) { + function u() {} + for (var v in u); + } + } + } + })(); + console.log(c); + } + expect_stdout: "2" +} + +issue_3366: { + options = { + functions: true, + inline: true, + reduce_vars: true, + toplevel: true, + unused: true, + } + input: { + function f() { + function g() { + return function() {}; + } + var a = g(); + (function() { + this && a && console.log("PASS"); + })(); + } + f(); + } + expect: { + void function() { + this && a && console.log("PASS"); + }(); + function a() {} + } + expect_stdout: "PASS" +} + +issue_3371: { + options = { + functions: true, + inline: true, + reduce_vars: true, + side_effects: true, + unused: true, + } + input: { + (function() { + var a = function f() { + (function() { + console.log(typeof f); + })(); + }; + while (a()); + })(); + } + expect: { + (function() { + function a() { + console.log(typeof a); + } + while (a()); + })(); + } + expect_stdout: "function" +} + +class_iife: { + options = { + inline: true, + sequences: true, + toplevel: true, + } + input: { + var A = function() { + function B() {} + B.prototype.m = function() { + console.log("PASS"); + }; + return B; + }(); + new A().m(); + } + expect: { + var A = (B.prototype.m = function() { + console.log("PASS"); + }, B); + function B() {} + new A().m(); + } + expect_stdout: "PASS" +} + +issue_3400_1: { + options = { + collapse_vars: true, + inline: true, + reduce_funcs: true, + reduce_vars: true, + unused: true, + } + input: { + (function(f) { + console.log(f()()[0].p); + })(function() { + function g() { + function h(u) { + var o = { + p: u + }; + return console.log(o[g]), o; + } + function e() { + return [ 42 ].map(function(v) { + return h(v); + }); + } + return e(); + } + return g; + }); + } + expect: { + void console.log(function g() { + function h(u) { + var o = { + p: u + }; + return console.log(o[g]), o; + } + function e() { + return [ 42 ].map(h); + } + return e(); + }()[0].p); + } + expect_stdout: [ + "undefined", + "42", + ] +} + +issue_3400_2: { + options = { + collapse_vars: true, + inline: true, + passes: 3, + reduce_funcs: true, + reduce_vars: true, + unused: true, + } + input: { + (function(f) { + console.log(f()()[0].p); + })(function() { + function g() { + function h(u) { + var o = { + p: u + }; + return console.log(o[g]), o; + } + function e() { + return [ 42 ].map(function(v) { + return h(v); + }); + } + return e(); + } + return g; + }); + } + expect: { + void console.log(function g() { + return [ 42 ].map(function(u) { + var o = { + p: u + }; + return console.log(o[g]), o; + }); + }()[0].p); + } + expect_stdout: [ + "undefined", + "42", + ] +} + +issue_3402: { + options = { + evaluate: true, + functions: true, + reduce_vars: true, + side_effects: true, + toplevel: true, + typeofs: true, + unused: true, + } + input: { + var f = function f() { + f = 42; + console.log(typeof f); + }; + "function" == typeof f && f(); + "function" == typeof f && f(); + console.log(typeof f); + } + expect: { + var f = function f() { + f = 42; + console.log(typeof f); + }; + f(); + f(); + console.log(typeof f); + } + expect_stdout: [ + "function", + "function", + "function", + ] +} + +issue_3439_1: { + options = { + inline: 3, + } + input: { + console.log(typeof function() { + return function(a) { + function a() {} + return a; + }(42); + }()); + } + expect: { + console.log(typeof function(a) { + function a() {} + return a; + }(42)); + } + expect_stdout: "function" +} + +issue_3439_2: { + options = { + inline: true, + } + input: { + console.log(typeof function() { + return function(a) { + function a() {} + return a; + }(42); + }()); + } + expect: { + console.log(typeof function(a) { + function a() {} + return a; + }(42)); + } + expect_stdout: "function" +} + +issue_3444: { + options = { + inline: true, + reduce_vars: true, + unused: true, + } + input: { + (function(h) { + return f; + function f() { + g(); + } + function g() { + h("PASS"); + } + })(console.log)(); + } + expect: { + (function(h) { + return function() { + void h("PASS"); + }; + })(console.log)(); + } + expect_stdout: "PASS" +} + +issue_3506_1: { + options = { + collapse_vars: true, + evaluate: true, + inline: true, + reduce_vars: true, + side_effects: true, + unused: true, + } + input: { + var a = "FAIL"; + (function(b) { + (function(b) { + b && (a = "PASS"); + })(b); + })(a); + console.log(a); + } + expect: { + var a = "FAIL"; + !function(b) { + b && (a = "PASS"); + }(a); + console.log(a); + } + expect_stdout: "PASS" +} + +issue_3506_2: { + options = { + collapse_vars: true, + evaluate: true, + inline: 3, + reduce_vars: true, + side_effects: true, + unused: true, + } + input: { + var a = "FAIL"; + (function(b) { + (function(c) { + var d = 1; + for (;c && (a = "PASS") && 0 < --d;); + })(b); + })(a); + console.log(a); + } + expect: { + var a = "FAIL"; + !function(c) { + var d = 1; + for (;c && (a = "PASS") && 0 < --d;); + }(a); + console.log(a); + } + expect_stdout: "PASS" +} + +issue_3506_3: { + options = { + collapse_vars: true, + evaluate: true, + inline: true, + reduce_vars: true, + side_effects: true, + unused: true, + } + input: { + var a = "FAIL"; + (function(b) { + (function(c) { + var d = 1; + for (;c && (a = "PASS") && 0 < --d;); + })(b); + })(a); + console.log(a); + } + expect: { + var a = "FAIL"; + (function(b) { + var c = a; + var d = 1; + for (;c && (a = "PASS") && 0 < --d;); + })(); + console.log(a); + } + expect_stdout: "PASS" +} + +issue_3506_4: { + options = { + collapse_vars: true, + dead_code: true, + evaluate: true, + inline: 3, + loops: true, + reduce_vars: true, + side_effects: true, + unused: true, + } + input: { + var a = "FAIL"; + (function(b) { + (function(c) { + var d = 1; + for (;c && (a = "PASS") && 0 < --d;); + })(b); + })(a); + console.log(a); + } + expect: { + var a = "FAIL"; + !function(c) { + var d = 1; + for (;c && (a = "PASS") && 0 < --d;); + }(a); + console.log(a); + } + expect_stdout: "PASS" +} + +issue_3506_5: { + options = { + collapse_vars: true, + dead_code: true, + evaluate: true, + inline: true, + loops: true, + reduce_vars: true, + side_effects: true, + unused: true, + } + input: { + var a = "FAIL"; + (function(b) { + (function(c) { + var d = 1; + for (;c && (a = "PASS") && 0 < --d;); + })(b); + })(a); + console.log(a); + } + expect: { + var a = "FAIL"; + (function(b) { + var c = a; + var d = 1; + for (;c && (a = "PASS") && 0 < --d;); + })(); + console.log(a); + } + expect_stdout: "PASS" +} + +issue_3512: { + options = { + collapse_vars: true, + pure_getters: "strict", + sequences: true, + side_effects: true, + unused: true, + } + input: { + var a = "PASS"; + (function(b) { + (function() { + b <<= this || 1; + b.a = "FAIL"; + })(); + })(); + console.log(a); + } + expect: { + var a = "PASS"; + (function(b) { + (function() { + (b <<= this || 1).a = "FAIL"; + })(); + })(), + console.log(a); + } + expect_stdout: "PASS" +} + +issue_3562: { + options = { + collapse_vars: true, + reduce_vars: true, + } + input: { + var a = "PASS"; + function f(b) { + f = function() { + console.log(b); + }; + return "FAIL"; + } + a = f(a); + f(a); + } + expect: { + var a = "PASS"; + function f(b) { + f = function() { + console.log(b); + }; + return "FAIL"; + } + a = f(a); + f(a); + } + expect_stdout: "PASS" +} + +hoisted_inline: { + options = { + inline: true, + reduce_vars: true, + toplevel: true, + unused: true, + } + input: { + function f() { + console.log("PASS"); + } + function g() { + for (var console in [ 0 ]) + h(); + } + function h() { + f(); + } + g(); + } + expect: { + function f() { + console.log("PASS"); + } + (function() { + for (var console in [ 0 ]) + void f(); + })(); + } + expect_stdout: "PASS" +} + +hoisted_single_use: { + options = { + reduce_funcs: true, + reduce_vars: true, + toplevel: true, + unused: true, + } + input: { + function f(a) { + for (var r in a) g(r); + } + function g(a) { + console.log(a); + } + function h(a) { + var g = a.bar; + g(); + g(); + i(a); + } + function i(b) { + f(b); + } + h({ + bar: function() { + console.log("foo"); + } + }); + } + expect: { + function f(a) { + for (var r in a) (function(a) { + console.log(a); + })(r); + } + (function(a) { + var g = a.bar; + g(); + g(); + (function(b) { + f(b); + })(a); + })({ + bar: function() { + console.log("foo"); + } + }); + } + expect_stdout: [ + "foo", + "foo", + "bar", + ] +} + +inlined_single_use: { + options = { + inline: true, + reduce_vars: true, + unused: true, + } + input: { + console.log(function(f) { + f(); + }(function() { + var a = function() { + A; + }; + var b = function() { + a(B); + }; + (function() { + b; + }); + var c = 42; + })); + } + expect: { + console.log(function(f) { + a = function() { + A; + }, + b = function() { + a(B); + }, + void 0; + var a, b; + }()); + } + expect_stdout: "undefined" +} + +pr_3592_1: { + options = { + inline: true, + reduce_funcs: false, + reduce_vars: true, + toplevel: true, + unused: true, + } + input: { + function problem(w) { + return g.indexOf(w); + } + function unused(x) { + return problem(x); + } + function B(problem) { + return g[problem]; + } + function A(y) { + return problem(y); + } + function main(z) { + return B(A(z)); + } + var g = [ "PASS" ]; + console.log(main("PASS")); + } + expect: { + function problem(w) { + return g.indexOf(w); + } + function B(problem) { + return g[problem]; + } + var g = [ "PASS" ]; + console.log((z = "PASS", B((y = z, problem(y))))); + var z, y; + } + expect_stdout: "PASS" +} + +pr_3592_2: { + options = { + inline: true, + reduce_funcs: true, + reduce_vars: true, + toplevel: true, + unused: true, + } + input: { + function problem(w) { + return g.indexOf(w); + } + function unused(x) { + return problem(x); + } + function B(problem) { + return g[problem]; + } + function A(y) { + return problem(y); + } + function main(z) { + return B(A(z)); + } + var g = [ "PASS" ]; + console.log(main("PASS")); + } + expect: { + function problem(w) { + return g.indexOf(w); + } + var g = [ "PASS" ]; + console.log((z = "PASS", function(problem) { + return g[problem]; + }(problem(z)))); + var z; + } + expect_stdout: "PASS" +} + +inline_use_strict: { + options = { + evaluate: true, + inline: true, + reduce_vars: true, + sequences: true, + side_effects: true, + unused: true, + } + input: { + console.log(function() { + "use strict"; + return function() { + "use strict"; + var a = "foo"; + a += "bar"; + return a; + }; + }()()); + } + expect: { + console.log("foobar"); + } + expect_stdout: "foobar" +} + +pr_3595_1: { + options = { + collapse_vars: false, + inline: true, + reduce_funcs: true, + reduce_vars: true, + toplevel: true, + unused: true, + } + input: { + var g = [ "PASS" ]; + function problem(arg) { + return g.indexOf(arg); + } + function unused(arg) { + return problem(arg); + } + function a(arg) { + return problem(arg); + } + function b(problem) { + return g[problem]; + } + function c(arg) { + return b(a(arg)); + } + console.log(c("PASS")); + } + expect: { + var g = [ "PASS" ]; + function problem(arg) { + return g.indexOf(arg); + } + console.log((arg = "PASS", function(problem) { + return g[problem]; + }(problem(arg)))); + var arg; + } + expect_stdout: "PASS" +} + +pr_3595_2: { + options = { + collapse_vars: true, + inline: true, + reduce_funcs: true, + reduce_vars: true, + toplevel: true, + unused: true, + } + input: { + var g = [ "PASS" ]; + function problem(arg) { + return g.indexOf(arg); + } + function unused(arg) { + return problem(arg); + } + function a(arg) { + return problem(arg); + } + function b(problem) { + return g[problem]; + } + function c(arg) { + return b(a(arg)); + } + console.log(c("PASS")); + } + expect: { + var g = [ "PASS" ]; + function problem(arg) { + return g.indexOf(arg); + } + console.log(function(problem) { + return g[problem]; + }(problem("PASS"))); + } + expect_stdout: "PASS" +} + +pr_3595_3: { + options = { + collapse_vars: true, + inline: true, + passes: 2, + reduce_funcs: true, + reduce_vars: true, + toplevel: true, + unused: true, + } + input: { + var g = [ "PASS" ]; + function problem(arg) { + return g.indexOf(arg); + } + function unused(arg) { + return problem(arg); + } + function a(arg) { + return problem(arg); + } + function b(problem) { + return g[problem]; + } + function c(arg) { + return b(a(arg)); + } + console.log(c("PASS")); + } + expect: { + var g = [ "PASS" ]; + console.log(function(problem) { + return g[problem]; + }(g.indexOf("PASS"))); + } + expect_stdout: "PASS" +} + +pr_3595_4: { + options = { + collapse_vars: true, + inline: true, + passes: 3, + reduce_funcs: true, + reduce_vars: true, + toplevel: true, + unused: true, + } + input: { + var g = [ "PASS" ]; + function problem(arg) { + return g.indexOf(arg); + } + function unused(arg) { + return problem(arg); + } + function a(arg) { + return problem(arg); + } + function b(problem) { + return g[problem]; + } + function c(arg) { + return b(a(arg)); + } + console.log(c("PASS")); + } + expect: { + var g = [ "PASS" ]; + console.log((problem = g.indexOf("PASS"), g[problem])); + var problem; + } + expect_stdout: "PASS" +} + +issue_3679_1: { + options = { + collapse_vars: true, + inline: true, + passes: 2, + pure_getters: "strict", + reduce_vars: true, + side_effects: true, + unused: true, + } + input: { + (function() { + var f = function() {}; + f.g = function() { + console.log("PASS"); + }; + f.g(); + })(); + } + expect: { + console.log("PASS"); + } + expect_stdout: "PASS" +} + +issue_3679_2: { + options = { + collapse_vars: true, + inline: true, + passes: 2, + pure_getters: "strict", + reduce_vars: true, + side_effects: true, + unused: true, + } + input: { + (function() { + "use strict"; + var f = function() {}; + f.g = function() { + console.log("PASS"); + }; + f.g(); + })(); + } + expect: { + (function() { + "use strict"; + console.log("PASS"); + })(); + } + expect_stdout: "PASS" +} + +issue_3679_3: { + options = { + collapse_vars: true, + inline: true, + functions: true, + pure_getters: "strict", + reduce_vars: true, + side_effects: true, + unused: true, + } + input: { + (function() { + var f = function() {}; + f.p = "PASS"; + f.g = function() { + console.log(f.p); + }; + f.g(); + })(); + } + expect: { + (function() { + function f() {}; + f.p = "PASS"; + (f.g = function() { + console.log(f.p); + })(); + })(); + } + expect_stdout: "PASS" +} + +preceding_side_effects: { + options = { + inline: true, + } + input: { + console.log(function(a, b, c) { + return b; + }(console, "PASS", 42)); + } + expect: { + console.log((console, 42, "PASS")); + } + expect_stdout: "PASS" +} + +trailing_side_effects: { + options = { + inline: true, + } + input: { + console.log(function(a, b, c) { + return b; + }(42, "PASS", console)); + } + expect: { + console.log(function(a, b, c) { + return b; + }(42, "PASS", console)); + } + expect_stdout: "PASS" +} + +preserve_binding_1: { + options = { + inline: true, + } + input: { + var o = { + f: function() { + return this === o ? "FAIL" : "PASS"; + }, + }; + console.log(function(a) { + return a; + }(o.f)()); + } + expect: { + var o = { + f: function() { + return this === o ? "FAIL" : "PASS"; + }, + }; + console.log((0, o.f)()); + } + expect_stdout: "PASS" +} + +preserve_binding_2: { + options = { + collapse_vars: true, + inline: true, + unused: true, + } + input: { + var o = { + f: function() { + return this === o ? "FAIL" : "PASS"; + }, + }; + console.log(function(a) { + return a; + }(o.f)()); + } + expect: { + var o = { + f: function() { + return this === o ? "FAIL" : "PASS"; + }, + }; + console.log((0, o.f)()); + } + expect_stdout: "PASS" +} + +issue_3770: { + options = { + inline: true, + reduce_vars: true, + side_effects: true, + unused: true, + } + input: { + (function() { + function f(a, a) { + var b = function() { + return a || "PASS"; + }(); + console.log(b); + } + f("FAIL"); + })(); + } + expect: { + (function() { + b = a || "PASS", + console.log(b); + var a, b; + })(); + } + expect_stdout: "PASS" +} + +issue_3771: { + options = { + inline: true, + reduce_vars: true, + side_effects: true, + toplevel: true, + unused: true, + } + input: { + try { + function f(a) { + var a = f(1234); + } + f(); + } catch (e) { + console.log("PASS"); + } + } + expect: { + try { + (function f(a) { + f(); + })(); + } catch (e) { + console.log("PASS"); + } + } + expect_stdout: "PASS" +} + +issue_3772: { + options = { + collapse_vars: true, + dead_code: true, + inline: true, + reduce_vars: true, + side_effects: true, + toplevel: true, + unused: true, + } + input: { + var a = "PASS"; + function f() { + return a; + } + var b = f(); + function g() { + console.log(f()); + } + g(); + } + expect: { + var a = "PASS"; + console.log(a); + } + expect_stdout: "PASS" +} + +issue_3777_1: { + options = { + inline: true, + reduce_vars: true, + side_effects: true, + } + input: { + (function() { + ff && ff(NaN); + function ff(a) { + var a = console.log("PASS"); + } + })(); + } + expect: { + (function() { + ff && ff(NaN); + function ff(a) { + var a = console.log("PASS"); + } + })(); + } + expect_stdout: "PASS" +} + +issue_3777_2: { + options = { + inline: true, + pure_getters: "strict", + reduce_vars: true, + side_effects: true, + toplevel: true, + } + input: { + ff(ff.p); + function ff(a) { + var a = console.log("PASS"); + } + } + expect: { + ff(ff.p); + function ff(a) { + var a = console.log("PASS"); + } + } + expect_stdout: "PASS" +} + +issue_3821_1: { + options = { + inline: true, + } + input: { + var a = 0; + console.log(function(b) { + return +a + b; + }(--a)); + } + expect: { + var a = 0; + console.log(function(b) { + return +a + b; + }(--a)); + } + expect_stdout: "-2" +} + +issue_3821_2: { + options = { + inline: true, + reduce_vars: true, + toplevel: true, + } + input: { + var a = "PASS"; + function f(g, b) { + return g(), b; + } + console.log(f(function() { + a = "FAIL"; + }, a)); + } + expect: { + var a = "PASS"; + function f(g, b) { + return g(), b; + } + console.log(f(function() { + a = "FAIL"; + }, a)); + } + expect_stdout: "PASS" +} + +substitute: { + options = { + inline: true, + reduce_vars: true, + toplevel: true, + } + input: { + var o = {}; + function f(a) { + return a === o ? "PASS" : "FAIL"; + } + [ + function() { + return f; + }, + function() { + return function(b) { + return f(b); + }; + }, + function() { + "use strict"; + return function(c) { + return f(c); + }; + }, + function() { + return function(c) { + "use strict"; + return f(c); + }; + }, + function() { + return function(d, e) { + return f(d, e); + }; + }, + ].forEach(function(g) { + console.log(g()(o), g().call(o, o), g().length); + }); + } + expect: { + var o = {}; + function f(a) { + return a === o ? "PASS" : "FAIL"; + } + [ + function() { + return f; + }, + function() { + return f; + }, + function() { + "use strict"; + return f; + }, + function() { + return f; + }, + function() { + return function(d, e) { + return f(d, e); + }; + }, + ].forEach(function(g) { + console.log(g()(o), g().call(o, o), g().length); + }); + } + expect_stdout: [ + "PASS PASS 1", + "PASS PASS 1", + "PASS PASS 1", + "PASS PASS 1", + "PASS PASS 2", + ] +} + +substitute_add_farg_1: { + options = { + inline: 3, + keep_fargs: false, + } + input: { + function f(g) { + console.log(g.length); + g(null, "FAIL"); + } + f(function() { + return function(a, b) { + return function(c) { + do { + console.log("PASS"); + } while (c); + }(a, b); + }; + }()); + } + expect: { + function f(g) { + console.log(g.length); + g(null, "FAIL"); + } + f(function(c, argument_1) { + do { + console.log("PASS"); + } while (c); + }); + } + expect_stdout: [ + "2", + "PASS", + ] +} + +substitute_add_farg_2: { + options = { + if_return: true, + inline: true, + keep_fargs: false, + side_effects: true, + } + input: { + function f(g) { + console.log(g.length); + g(null, "FAIL"); + } + f(function() { + return function(a, b) { + return function(c) { + do { + console.log("PASS"); + } while (c); + }(a, b); + }; + }()); + } + expect: { + function f(g) { + console.log(g.length); + g(null, "FAIL"); + } + f(function(a, b) { + var c = a; + do { + console.log("PASS"); + } while (c); + }); + } + expect_stdout: [ + "2", + "PASS", + ] +} + +substitute_arguments: { + options = { + inline: true, + reduce_vars: true, + toplevel: true, + } + input: { + var o = {}; + function f(a) { + return arguments[0] === o ? "PASS" : "FAIL"; + } + [ + function() { + return f; + }, + function() { + return function(b) { + return f(b); + }; + }, + function() { + "use strict"; + return function(c) { + return f(c); + }; + }, + function() { + return function(c) { + "use strict"; + return f(c); + }; + }, + function() { + return function(d, e) { + return f(d, e); + }; + }, + ].forEach(function(g) { + console.log(g()(o), g().call(o, o), g().length); + }); + } + expect: { + var o = {}; + function f(a) { + return arguments[0] === o ? "PASS" : "FAIL"; + } + [ + function() { + return f; + }, + function() { + return function(b) { + return f(b); + }; + }, + function() { + "use strict"; + return function(c) { + return f(c); + }; + }, + function() { + return function(c) { + "use strict"; + return f(c); + }; + }, + function() { + return function(d, e) { + return f(d, e); + }; + }, + ].forEach(function(g) { + console.log(g()(o), g().call(o, o), g().length); + }); + } + expect_stdout: [ + "PASS PASS 1", + "PASS PASS 1", + "PASS PASS 1", + "PASS PASS 1", + "PASS PASS 2", + ] +} + +substitute_drop_farg: { + options = { + inline: true, + keep_fargs: false, + reduce_vars: true, + toplevel: true, + } + input: { + var o = {}; + function f(a) { + return a === o ? "PASS" : "FAIL"; + } + [ + function() { + return f; + }, + function() { + return function(b) { + return f(b); + }; + }, + function() { + "use strict"; + return function(c) { + return f(c); + }; + }, + function() { + return function(c) { + "use strict"; + return f(c); + }; + }, + function() { + return function(d, e) { + return f(d, e); + }; + }, + ].forEach(function(g) { + console.log(g()(o), g().call(o, o)); + }); + } + expect: { + var o = {}; + function f(a) { + return a === o ? "PASS" : "FAIL"; + } + [ + function() { + return f; + }, + function() { + return f; + }, + function() { + "use strict"; + return f; + }, + function() { + return f; + }, + function() { + return function(d, e) { + return f(d, e); + }; + }, + ].forEach(function(g) { + console.log(g()(o), g().call(o, o)); + }); + } + expect_stdout: [ + "PASS PASS", + "PASS PASS", + "PASS PASS", + "PASS PASS", + "PASS PASS", + ] +} + +substitute_this: { + options = { + inline: true, + reduce_vars: true, + toplevel: true, + } + input: { + var o = {}; + function f(a) { + return a === o ? this === o : "FAIL"; + } + [ + function() { + return f; + }, + function() { + return function(b) { + return f(b); + }; + }, + function() { + "use strict"; + return function(c) { + return f(c); + }; + }, + function() { + return function(c) { + "use strict"; + return f(c); + }; + }, + function() { + return function(d, e) { + return f(d, e); + }; + }, + ].forEach(function(g) { + console.log(g()(o), g().call(o, o), g().length); + }); + } + expect: { + var o = {}; + function f(a) { + return a === o ? this === o : "FAIL"; + } + [ + function() { + return f; + }, + function() { + return function(b) { + return f(b); + }; + }, + function() { + "use strict"; + return function(c) { + return f(c); + }; + }, + function() { + return function(c) { + "use strict"; + return f(c); + }; + }, + function() { + return function(d, e) { + return f(d, e); + }; + }, + ].forEach(function(g) { + console.log(g()(o), g().call(o, o), g().length); + }); + } + expect_stdout: [ + "false true 1", + "false false 1", + "false false 1", + "false false 1", + "false false 2", + ] +} + +substitute_use_strict: { + options = { + inline: true, + reduce_vars: true, + toplevel: true, + } + input: { + var o = {}; + function f(a) { + "use strict"; + return a === o ? "PASS" : "FAIL"; + } + [ + function() { + return f; + }, + function() { + return function(b) { + return f(b); + }; + }, + function() { + "use strict"; + return function(c) { + return f(c); + }; + }, + function() { + return function(c) { + "use strict"; + return f(c); + }; + }, + function() { + return function(d, e) { + return f(d, e); + }; + }, + ].forEach(function(g) { + console.log(g()(o), g().call(o, o), g().length); + }); + } + expect: { + var o = {}; + function f(a) { + "use strict"; + return a === o ? "PASS" : "FAIL"; + } + [ + function() { + return f; + }, + function() { + return f; + }, + function() { + "use strict"; + return f; + }, + function() { + return f; + }, + function() { + return function(d, e) { + return f(d, e); + }; + }, + ].forEach(function(g) { + console.log(g()(o), g().call(o, o), g().length); + }); + } + expect_stdout: [ + "PASS PASS 1", + "PASS PASS 1", + "PASS PASS 1", + "PASS PASS 1", + "PASS PASS 2", + ] +} + +substitute_assignment: { + options = { + evaluate: true, + inline: true, + passes: 2, + properties: true, + reduce_vars: true, + side_effects: true, + toplevel: true, + unused: true, + } + input: { + function f(a, b, c) { + a[b] = c; + } + var o = {}; + f(o, 42, null); + f(o, "foo", "bar"); + for (var k in o) + console.log(k, o[k]); + } + expect: { + var o = {}; + o[42] = null; + o.foo = "bar"; + for (var k in o) + console.log(k, o[k]); + } + expect_stdout: [ + "42 null", + "foo bar", + ] +} + +issue_3833_1: { + options = { + inline: 3, + keep_fargs: false, + reduce_vars: true, + toplevel: true, + unused: true, + } + input: { + function f(a) { + return function() { + while (a); + console.log("PASS"); + }(); + } + f(); + } + expect: { + (function() { + while (a); + console.log("PASS"); + })(); + var a; + } + expect_stdout: "PASS" +} + +issue_3833_2: { + options = { + if_return: true, + inline: true, + keep_fargs: false, + reduce_vars: true, + toplevel: true, + unused: true, + } + input: { + function f(a) { + return function() { + while (a); + console.log("PASS"); + }(); + } + f(); + } + expect: { + var a = void 0; + while (a); + console.log("PASS"); + } + expect_stdout: "PASS" +} + +issue_3835: { + options = { + inline: true, + reduce_vars: true, + } + input: { + (function f() { + return function() { + return f(); + }(); + })(); + } + expect: { + (function f() { + return f(); + })(); + } + expect_stdout: RangeError("Maximum call stack size exceeded") +} + +issue_3836_1: { + options = { + inline: 3, + } + input: { + (function() { + return function() { + for (var a in 0) + console.log(k); + }(console.log("PASS")); + })(); + } + expect: { + (function() { + for (var a in 0) + console.log(k); + })(console.log("PASS")); + } + expect_stdout: "PASS" +} + +issue_3836_2: { + options = { + if_return: true, + inline: true, + } + input: { + (function() { + return function() { + for (var a in 0) + console.log(k); + }(console.log("PASS")); + })(); + } + expect: { + (function() { + console.log("PASS"); + for (var a in 0) + console.log(k); + })(); + } + expect_stdout: "PASS" +} + +issue_3852: { + options = { + collapse_vars: true, + inline: true, + unused: true, + } + input: { + console.log(function(a) { + return function(b) { + return b && (b[0] = 0), "PASS"; + }(a); + }(42)); + } + expect: { + console.log(function(a) { + return a && (a[0] = 0), "PASS"; + }(42)); + } + expect_stdout: "PASS" +} + +issue_3911: { + options = { + collapse_vars: true, + conditionals: true, + inline: true, + reduce_vars: true, + toplevel: true, + unused: true, + } + input: { + function f() { + return function() { + if (a) (a++, b += a); + f(); + }; + } + var a = f, b; + console.log("PASS"); + } + expect: { + console.log("PASS"); + } + expect_stdout: "PASS" +} + +issue_3929: { + options = { + functions: true, + reduce_vars: true, + unused: true, + } + input: { + (function() { + var abc = function f() { + (function() { + switch (f) { + default: + var abc = 0; + case 0: + abc.p; + } + console.log(typeof f); + })(); + }; + typeof abc && abc(); + })(); + } + expect: { + (function() { + var abc = function f() { + (function() { + switch (f) { + default: + var abc = 0; + case 0: + abc.p; + } + console.log(typeof f); + })(); + }; + typeof abc && abc(); + })(); + } + expect_stdout: "function" +} + +issue_4006: { + options = { + dead_code: true, + evaluate: true, + inline: true, + keep_fargs: false, + reduce_vars: true, + sequences: true, + side_effects: true, + unused: true, + } + input: { + var a = 0; + (function() { + (function(b, c) { + for (var k in console.log(c), 0) + return b += 0; + })(0, --a); + return a ? 0 : --a; + })(); + } + expect: { + var a = 0; + (function(c) { + for (var k in console.log(c), 0) + return; + })(--a), a || --a; + } + expect_stdout: "-1" +} + +issue_4155: { + options = { + functions: true, + inline: true, + merge_vars: true, + reduce_vars: true, + unused: true, + } + input: { + (function() { + var a; + (function() { + console.log(a); + })(a); + var b = function() {}; + b && console.log(typeof b); + })(); + } + expect: { + (function() { + var a; + void console.log(a); + function b() {} + b && console.log(typeof b); + })(); + } + expect_stdout: [ + "undefined", + "function", + ] +} + +issue_4159: { + options = { + collapse_vars: true, + inline: true, + reduce_vars: true, + toplevel: true, + unused: true, + } + input: { + var a = 42, c = function(b) { + (b = a) && console.log(a++, b); + }(c = a); + } + expect: { + var a = 42; + (b = a) && console.log(a++, b); + var b; + } + expect_stdout: "42 42" +} + +direct_inline: { + options = { + inline: true, + reduce_vars: true, + unused: true, + } + input: { + function f(a, b) { + function g(c) { + return c >> 1; + } + return g(a) + g(b); + } + console.log(f(13, 31)); + } + expect: { + function f(a, b) { + return (a >> 1) + (b >> 1); + } + console.log(f(13, 31)); + } + expect_stdout: "21" +} + +direct_inline_catch_redefined: { + options = { + inline: true, + reduce_vars: true, + toplevel: true, + } + input: { + var a = 1; + function f() { + return a; + } + try { + throw 2; + } catch (a) { + function g() { + return a; + } + console.log(a, f(), g()); + } + console.log(a, f(), g()); + } + expect: { + var a = 1; + function f() { + return a; + } + try { + throw 2; + } catch (a) { + function g() { + return a; + } + console.log(a, f(), g()); + } + console.log(a, a, g()); + } + expect_stdout: true +} + +statement_var_inline: { + options = { + inline: true, + join_vars: true, + unused: true, + } + input: { + function f() { + (function() { + var a = {}; + function g() { + a.p; + } + g(console.log("PASS")); + var b = function h(c) { + c && c.q; + }(); + })(); + } + f(); + } + expect: { + function f() { + var c, a = {}; + function g() { + a.p; + } + g(console.log("PASS")); + c && c.q; + return; + } + f(); + } + expect_stdout: "PASS" +} + +issue_4171_1: { + options = { + functions: true, + reduce_vars: true, + unused: true, + } + input: { + console.log(function(a) { + try { + while (a) + var e = function() {}; + } catch (e) { + return function() { + return e; + }; + } + }(!console)); + } + expect: { + console.log(function(a) { + try { + while (a) + var e = function() {}; + } catch (e) { + return function() { + return e; + }; + } + }(!console)); + } + expect_stdout: "undefined" +} + +issue_4171_2: { + options = { + functions: true, + reduce_vars: true, + unused: true, + } + input: { + console.log(function(a) { + try { + while (a); + } catch (e) { + return function() { + return e; + }; + } finally { + var e = function() {}; + } + }(!console)); + } + expect: { + console.log(function(a) { + try { + while (a); + } catch (e) { + return function() { + return e; + }; + } finally { + function e() {} + } + }(!console)); + } + expect_stdout: "undefined" +} + +catch_defun: { + mangle = { + toplevel: true, + } + input: { + try { + throw 42; + } catch (a) { + function f() { + return typeof a; + } + } + console.log(f()); + } + expect: { + try { + throw 42; + } catch (o) { + function t() { + return typeof o; + } + } + console.log(t()); + } + expect_stdout: true +} + +catch_no_argname: { + options = { + inline: true, + reduce_vars: true, + toplevel: true, + unused: true, + } + input: { + var a = "PASS"; + function f() { + return a; + } + try { + throw a; + } catch { + function g() { + return a; + } + console.log(a, f(), g()); + } + console.log(a, f(), g()); + } + expect: { + var a = "PASS"; + try { + throw a; + } catch { + function g() { + return a; + } + console.log(a, a, g()); + } + console.log(a, a, g()); + } + expect_stdout: [ + "PASS PASS PASS", + "PASS PASS PASS", + ] + node_version: ">=10" +} + +issue_4186: { + options = { + conditionals: true, + inline: true, + reduce_funcs: true, + reduce_vars: true, + sequences: true, + unused: true, + } + input: { + console.log(typeof function() { + return function() { + function f() { + if (1) + g(); + else + (function() { + return f; + }); + } + return f; + function g() { + if (1) { + if (0) + h; + else + h(); + var key = 0; + } + } + function h() { + return factory; + } + }; + }()()); + } + expect: { + console.log(typeof function() { + return function f() { + 1 ? void (1 && (0 ? h : h(), 0)) : function() { + return f; + }; + }; + function h() { + return factory; + } + }()); + } + expect_stdout: "function" +} + +issue_4233: { + options = { + functions: true, + reduce_vars: true, + unused: true, + } + input: { + (function() { + try { + var a = function() {}; + try { + throw 42; + } catch (a) { + (function() { + console.log(typeof a); + })(); + var a; + } + } catch (e) {} + })(); + } + expect: { + (function() { + try { + var a = function() {}; + try { + throw 42; + } catch (a) { + (function() { + console.log(typeof a); + })(); + var a; + } + } catch (e) {} + })(); + } + expect_stdout: "number" +} + +issue_4259: { + options = { + collapse_vars: true, + functions: true, + reduce_vars: true, + toplevel: true, + unused: true, + } + input: { + var a = function b() { + var c = b; + for (b in c); + }; + a(); + console.log(typeof a); + } + expect: { + var a = function b() { + for (b in b); + } + a(); + console.log(typeof a); + } + expect_stdout: "function" +} + +issue_4261: { + options = { + if_return: true, + inline: true, + reduce_funcs: true, + reduce_vars: true, + toplevel: true, + unused: true, + } + input: { + try { + throw 42; + } catch (e) { + (function() { + function f() { + e.p; + } + function g() { + while (f()); + } + (function() { + while (console.log(g())); + })(); + })(); + } + } + expect: { + try { + throw 42; + } catch (e) { + function g() { + // `ReferenceError: e is not defined` on Node.js v4- + while (void e.p); + } + while (console.log(g())); + } + } + expect_stdout: "undefined" + node_version: ">=6" +} + +issue_4265: { + options = { + conditionals: true, + dead_code: true, + inline: true, + sequences: true, + side_effects: true, + } + input: { + function f() { + console; + if ([ function() { + return this + console.log(a); + a; + var a; + }() ]); + return 42; + } + console.log(f()); + } + expect: { + function f() { + var a; + return console, console.log(a), 42; + } + console.log(f()); + } + expect_stdout: [ + "undefined", + "42", + ] +} + +trailing_comma: { + input: { + new function(a, b,) { + console.log(b, a,); + }(42, "PASS",); + } + expect_exact: 'new function(a,b){console.log(b,a)}(42,"PASS");' + expect_stdout: "PASS 42" +} + +issue_4451: { + options = { + functions: true, + reduce_vars: true, + toplevel: true, + unused: true, + } + input: { + var a = function f() { + for (f in "foo") + return f; + }; + while (console.log(typeof a())); + } + expect: { + var a = function f() { + for (f in "foo") + return f; + }; + while (console.log(typeof a())); + } + expect_stdout: "function" +} + +issue_4471: { + options = { + inline: true, + reduce_funcs: true, + reduce_vars: true, + toplevel: true, + unused: true, + } + input: { + f(f()); + function f() { + return g(); + } + function g() { + { + console.log("PASS"); + } + } + } + expect: { + f(g()); + function f() { + return g(); + } + function g() { + console.log("PASS"); + } + } + expect_stdout: [ + "PASS", + "PASS", + ] +} + +issue_4612_1: { + options = { + evaluate: true, + inline: true, + reduce_vars: true, + side_effects: true, + unused: true, + } + input: { + console.log(function() { + function f() { + return g(); + } + function g(a) { + return a || f(); + } + return g("PASS"); + }()); + } + expect: { + console.log("PASS"); + } + expect_stdout: "PASS" +} + +issue_4612_2: { + options = { + evaluate: true, + inline: true, + reduce_vars: true, + side_effects: true, + unused: true, + } + input: { + console.log(function() { + function fn() { + return h(); + } + function g() { + return fn(); + } + function h(a) { + return a || fn(); + } + return h("PASS"); + }()); + } + expect: { + console.log("PASS"); + } + expect_stdout: "PASS" +} + +issue_4612_3: { + options = { + inline: true, + reduce_vars: true, + } + input: { + console.log(typeof function() { + return g(); + function f() { + return g; + } + function g() { + { + return f; + } + } + }()); + } + expect: { + console.log(typeof function() { + return g(); + function f() { + return g; + } + function g() { + return f; + } + }()); + } + expect_stdout: "function" +} + +issue_4612_4: { + options = { + booleans: true, + evaluate: true, + reduce_vars: true, + } + input: { + console.log(function() { + function f() { + return h(); + } + function g() { + { + return h(); + } + } + function h() { + { + return g(); + } + } + }()); + } + expect: { + console.log(function() { + function f() { + h(); + } + function g() { + return h(); + } + function h() { + return g(); + } + }()); + } + expect_stdout: "undefined" +} + +issue_4655: { + options = { + functions: true, + loops: true, + reduce_vars: true, + side_effects: true, + unused: true, + } + input: { + (function f() { + while (console.log("PASS")) { + var g = function() {}; + for (var a in g) + g(); + } + })(); + } + expect: { + (function() { + for (; console.log("PASS");) { + function g() {}; + } + })(); + } + expect_stdout: "PASS" +} + +issue_4659_1: { + options = { + if_return: true, + inline: true, + reduce_vars: true, + } + input: { + var a = 0; + (function() { + function f() { + return a++; + } + (function() { + f && f(); + (function() { + var a = console && a; + })(); + })(); + })(); + console.log(a); + } + expect: { + var a = 0; + (function() { + function f() { + return a++; + } + f && a++; + (function() { + var a = console && a; + })(); + })(); + console.log(a); + } + expect_stdout: "1" +} + +issue_4659_2: { + options = { + if_return: true, + inline: true, + reduce_vars: true, + } + input: { + var a = 0; + (function() { + function f() { + return a++; + } + (function() { + (function() { + f && f(); + })(); + (function() { + var a = console && a; + })(); + })(); + })(); + console.log(a); + } + expect: { + var a = 0; + (function() { + function f() { + return a++; + } + void (f && a++); + (function() { + var a = console && a; + })(); + })(); + console.log(a); + } + expect_stdout: "1" +} + +issue_4659_3: { + options = { + if_return: true, + inline: true, + reduce_vars: true, + unused: true, + } + input: { + var a = 0; + (function() { + function f() { + return a++; + } + (function() { + function g() { + while (!console); + } + g(f && f()); + (function() { + var a = console && a; + })(); + })(); + })(); + console.log(a); + } + expect: { + var a = 0; + (function() { + function f() { + return a++; + } + f && a++; + while (!console); + (function() { + var a = console && a; + })(); + })(); + console.log(a); + } + expect_stdout: "1" +} + +block_scope_1: { + input: { + console.log(typeof f); + function f() {} + } + expect: { + console.log(typeof f); + function f() {} + } + expect_stdout: "function" +} + +block_scope_1_compress: { + options = { + evaluate: true, + reduce_vars: true, + toplevel: true, + typeofs: true, + unused: true, + } + input: { + console.log(typeof f); + function f() {} + } + expect: { + console.log("function"); + } + expect_stdout: "function" +} + +block_scope_2: { + input: { + { + console.log(typeof f); + } + function f() {} + } + expect: { + console.log(typeof f); + function f() {} + } + expect_stdout: "function" +} + +block_scope_2_compress: { + options = { + evaluate: true, + reduce_vars: true, + toplevel: true, + typeofs: true, + unused: true, + } + input: { + { + console.log(typeof f); + } + function f() {} + } + expect: { + console.log("function"); + } + expect_stdout: "function" +} + +block_scope_3: { + input: { + console.log(typeof f); + { + function f() {} + } + } + expect: { + console.log(typeof f); + { + function f() {} + } + } + expect_stdout: true +} + +block_scope_3_compress: { + options = { + evaluate: true, + reduce_vars: true, + toplevel: true, + typeofs: true, + unused: true, + } + input: { + console.log(typeof f); + { + function f() {} + } + } + expect: { + console.log(typeof f); + { + function f() {} + } + } + expect_stdout: true +} + +block_scope_4: { + input: { + { + console.log(typeof f); + function f() {} + } + } + expect: { + console.log(typeof f); + function f() {} + } + expect_stdout: "function" +} + +block_scope_4_compress: { + options = { + evaluate: true, + reduce_vars: true, + toplevel: true, + typeofs: true, + unused: true, + } + input: { + { + console.log(typeof f); + function f() {} + } + } + expect: { + console.log("function"); + } + expect_stdout: "function" +} + +issue_4725_1: { + options = { + inline: true, + } + input: { + var o = { + f() { + return function g() { + return g; + }(); + } + }; + console.log(typeof o.f()); + } + expect: { + var o = { + f() { + return function g() { + return g; + }(); + } + }; + console.log(typeof o.f()); + } + expect_stdout: "function" + node_version: ">=4" +} + +issue_4725_2: { + options = { + if_return: true, + inline: true, + } + input: { + var o = { + f() { + return function() { + while (console.log("PASS")); + }(); + } + }; + o.f(); + } + expect: { + var o = { + f() { + while (console.log("PASS")); + } + }; + o.f(); + } + expect_stdout: "PASS" + node_version: ">=4" +} + +new_target_1: { + input: { + new function f() { + console.log(new.target === f); + }(); + console.log(function() { + return new.target; + }()); + } + expect: { + new function f() { + console.log(new.target === f); + }(); + console.log(function() { + return new.target; + }()); + } + 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_delete: { + options = { + evaluate: true, + } + input: { + new function() { + console.log(delete new.target); + }(); + } + expect: { + new function() { + console.log(delete new.target); + }(); + } + expect_stdout: true + 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" +} + +issue_4753_1: { + options = { + inline: true, + toplevel: true, + } + input: { + for (var i in [ 1, 2 ]) + (function() { + function f() {} + f && console.log(f.p ^= 42); + })(); + } + expect: { + for (var i in [ 1, 2 ]) + f = function() {}, + void (f && console.log(f.p ^= 42)); + var f; + } + expect_stdout: [ + "42", + "42", + ] +} + +issue_4753_2: { + options = { + inline: true, + reduce_vars: true, + side_effects: true, + toplevel: true, + unused: true, + } + input: { + do { + (function() { + var a = f(); + function f() { + return "PASS"; + } + f; + function g() { + console.log(a); + } + g(); + })(); + } while (0); + } + expect: { + do { + a = void 0, + f = function() { + return "PASS"; + }, + a = f(), + console.log(a); + } while (0); + var f, a; + } + expect_stdout: "PASS" +} + +issue_4788: { + options = { + evaluate: true, + functions: true, + keep_fnames: true, + reduce_vars: true, + toplevel: true, + unused: true, + } + input: { + function f() { + var a = function g() { + if (0) { + var g = 42; + f(); + } + g || console.log("PASS"); + }; + a(a); + } + f(); + } + expect: { + (function f() { + function a() { + if (0) { + var g = 42; + f(); + } + g || console.log("PASS"); + } + a(); + })(); + } + expect_stdout: "PASS" +} + +issue_4823: { + options = { + functions: true, + reduce_vars: true, + unused: true, + } + input: { + console.log(typeof function() { + { + function f() {} + var arguments = f(); + function g() {} + var arguments = g; + } + return f && arguments; + }()); + } + expect: { + console.log(typeof function() { + { + function f() {} + f(); + var arguments = function() {}; + } + return f && arguments; + }()); + } + expect_stdout: "function" +} + +drop_unused_self_reference: { + options = { + pure_getters: "strict", + reduce_vars: true, + sequences: true, + side_effects: true, + toplevel: true, + unused: true, + } + input: { + function f() {} + (f.p = f).q = console.log("PASS"); + } + expect: { + console.log("PASS"); + } + expect_stdout: "PASS" +} + +reduce_cross_reference_1: { + options = { + passes: 3, + pure_getters: "strict", + reduce_vars: true, + sequences: true, + side_effects: true, + unused: true, + } + input: { + (function(a, b) { + a = b = function() {}; + a.p = a; + b = a = function() {}; + b.q = b; + })(); + } + expect: {} + expect_stdout: true +} + +reduce_cross_reference_1_toplevel: { + options = { + passes: 2, + pure_getters: "strict", + reduce_vars: true, + sequences: true, + side_effects: true, + toplevel: true, + unused: true, + } + input: { + var a = b = function() {}; + a.p = a; + var b = a = function() {}; + b.q = b; + } + expect: {} + expect_stdout: true +} + +reduce_cross_reference_2: { + options = { + collapse_vars: true, + passes: 3, + pure_getters: "strict", + reduce_vars: true, + sequences: true, + side_effects: true, + unused: true, + } + input: { + (function(a, b) { + a = b = function() {}; + b.p = a; + b = a = function() {}; + a.q = b; + })(); + } + expect: {} + expect_stdout: true +} + +reduce_cross_reference_2_toplevel: { + options = { + collapse_vars: true, + passes: 2, + pure_getters: "strict", + reduce_vars: true, + sequences: true, + side_effects: true, + toplevel: true, + unused: true, + } + input: { + var a = b = function() {}; + b.p = a; + var b = a = function() {}; + a.q = b; + } + expect: {} + expect_stdout: true +} + +reduce_cross_reference_3: { + options = { + collapse_vars: true, + passes: 3, + pure_getters: "strict", + reduce_vars: true, + sequences: true, + side_effects: true, + unused: true, + } + input: { + (function(a, b) { + a = b = function() {}; + a.p = b; + b = a = function() {}; + b.q = a; + })(); + } + expect: {} + expect_stdout: true +} + +reduce_cross_reference_3_toplevel: { + options = { + collapse_vars: true, + passes: 2, + pure_getters: "strict", + reduce_vars: true, + sequences: true, + side_effects: true, + toplevel: true, + unused: true, + } + input: { + var a = b = function() {}; + a.p = b; + var b = a = function() {}; + b.q = a; + } + expect: {} + expect_stdout: true +} + +reduce_cross_reference_4: { + options = { + passes: 3, + pure_getters: "strict", + reduce_vars: true, + sequences: true, + side_effects: true, + unused: true, + } + input: { + (function(a, b) { + a = b = function() {}; + b.p = b; + b = a = function() {}; + a.q = a; + })(); + } + expect: {} + expect_stdout: true +} + +reduce_cross_reference_4_toplevel: { + options = { + passes: 2, + pure_getters: "strict", + reduce_vars: true, + sequences: true, + side_effects: true, + toplevel: true, + unused: true, + } + input: { + var a = b = function() {}; + b.p = b; + var b = a = function() {}; + a.q = a; + } + expect: {} + expect_stdout: true +} + +recursive_collapse: { + options = { + collapse_vars: true, + reduce_vars: true, + } + input: { + console.log(function f(a) { + var b = a && f(); + return b; + }("FAIL") || "PASS"); + } + expect: { + console.log(function f(a) { + var b; + return a && f(); + }("FAIL") || "PASS"); + } + expect_stdout: "PASS" +} + +issue_5025: { + options = { + collapse_vars: true, + inline: true, + reduce_vars: true, + unused: true, + } + input: { + function f(a) { + function g() { + b = 42; + } + g(b = a); + var b = this; + console.log(typeof b); + } + f(); + } + expect: { + function f(a) { + b = a, + void (b = 42); + var b = this; + console.log(typeof b); + } + f(); + } + expect_stdout: "object" +} + +issue_5036: { + options = { + functions: true, + reduce_vars: true, + unused: true, + } + input: { + console.log(typeof function() { + var await = function f() { + return f; + }; + return await() === await; + }() ? "PASS" : "FAIL"); + } + expect: { + console.log(typeof function() { + function await() { + return await; + } + return await() === await; + }() ? "PASS" : "FAIL"); + } + expect_stdout: "PASS" +} + +issue_5046: { + options = { + conditionals: true, + evaluate: true, + keep_fnames: true, + passes: 2, + reduce_vars: true, + side_effects: true, + toplevel: true, + } + input: { + var a = 0; + if (a) + 0(); + else + (function f() { + f; + return a = "PASS"; + })(); + console.log(a); + } + expect: { + var a = 0; + (a ? 0 : function f() { + return a = "PASS"; + })(); + console.log(a); + } + expect_stdout: "PASS" +} + +issue_5061_1: { + options = { + evaluate: true, + reduce_vars: true, + toplevel: true, + } + input: { + var f, a = 1; + (f = function() { + console.log(a ? "foo" : "bar"); + })(); + f(a = 0); + } + expect: { + var f, a = 1; + (f = function() { + console.log(a ? "foo" : "bar"); + })(); + f(a = 0); + } + expect_stdout: [ + "foo", + "bar", + ] +} + +issue_5061_2: { + options = { + reduce_vars: true, + toplevel: true, + unused: true, + } + input: { + var f, a = 1; + (f = function() { + console.log(a ? "foo" : "bar"); + })(); + f(a = 0); + } + expect: { + var f, a = 1; + (f = function() { + console.log(a ? "foo" : "bar"); + })(); + f(a = 0); + } + expect_stdout: [ + "foo", + "bar", + ] +} + +issue_5067: { + options = { + inline: true, + reduce_vars: true, + toplevel: true, + unused: true, + } + input: { + var f = function() { + f(); + }; + } + expect: {} +} + +issue_5096_1: { + options = { + evaluate: true, + passes: 2, + reduce_vars: true, + side_effects: true, + toplevel: true, + } + input: { + var a, b = "FAIL", c = 1; + do { + a && a(); + a = function() { + b = "PASS"; + }; + } while (c--); + console.log(b); + } + expect: { + var a, b = "FAIL", c = 1; + do { + a && a(); + a = function() { + b = "PASS"; + }; + } while (c--); + console.log(b); + } + expect_stdout: "PASS" +} + +issue_5096_2: { + options = { + passes: 2, + reduce_vars: true, + side_effects: true, + toplevel: true, + unused: true, + } + input: { + var a, b = "FAIL", c = 1; + do { + a && a(); + a = function() { + b = "PASS"; + }; + } while (c--); + console.log(b); + } + expect: { + var a, b = "FAIL", c = 1; + do { + a && a(); + a = function() { + b = "PASS"; + }; + } while (c--); + console.log(b); + } + expect_stdout: "PASS" +} + +issue_5096_3: { + options = { + evaluate: true, + reduce_vars: true, + side_effects: true, + toplevel: true, + } + input: { + var b = "FAIL", c = 1; + do { + a && a(); + var a = function() { + b = "PASS"; + }; + } while (c--); + console.log(b); + } + expect: { + var b = "FAIL", c = 1; + do { + a && a(); + var a = function() { + b = "PASS"; + }; + } while (c--); + console.log(b); + } + expect_stdout: "PASS" +} + +issue_5096_4: { + options = { + reduce_vars: true, + side_effects: true, + toplevel: true, + unused: true, + } + input: { + var b = "FAIL", c = 1; + do { + a && a(); + var a = function() { + b = "PASS"; + }; + } while (c--); + console.log(b); + } + expect: { + var b = "FAIL", c = 1; + do { + a && a(); + var a = function() { + b = "PASS"; + }; + } while (c--); + console.log(b); + } + expect_stdout: "PASS" +} + +issue_5098: { + options = { + reduce_vars: true, + unused: true, + } + input: { + (function(o) { + function f() { + f = console.log; + if (o.p++) + throw "FAIL"; + f("PASS"); + } + return f; + })({ p: 0 })(); + } + expect: { + (function(o) { + function f() { + f = console.log; + if (o.p++) + throw "FAIL"; + f("PASS"); + } + return f; + })({ p: 0 })(); + } + expect_stdout: "PASS" +} + +shorter_without_void: { + options = { + inline: true, + passes: 2, + reduce_vars: true, + side_effects: true, + toplevel: true, + } + input: { + var a; + function f(b) { + a = b; + } + f("foo"); + console.log(a) || f("bar"); + console.log(a, f("baz")); + console.log(a); + } + expect: { + var a; + function f(b) { + a = b; + } + a = "foo"; + console.log(a) || (a = "bar"); + console.log(a, f("baz")); + console.log(a); + } + expect_stdout: [ + "foo", + "bar undefined", + "baz", + ] +} + +issue_5120: { + options = { + functions: true, + reduce_vars: true, + toplevel: true, + unused: true, + } + input: { + var a = function f() { + function g() { + f || g(); + } + g(); + return f.valueOf(); + }; + console.log(a() === a ? "PASS" : "FAIL"); + } + expect: { + function a() { + (function g() { + a || g(); + })(); + return a.valueOf(); + } + console.log(a() === a ? "PASS" : "FAIL"); + } + expect_stdout: "PASS" +} + +issue_5140: { + options = { + collapse_vars: true, + inline: true, + reduce_vars: true, + toplevel: true, + unused: true, + } + input: { + A = 42; + function f(b) { + return b >> 0; + } + var a = f(42 in []); + console.log(f(A)); + } + expect: { + function f(b) { + return b >> 0; + } + A = 42; + console.log(A >> 0); + } + expect_stdout: "42" +} + +issue_5173_1: { + options = { + conditionals: true, + inline: true, + reduce_vars: true, + side_effects: true, + toplevel: true, + } + input: { + function f(a, b) { + console.log(b); + } + f([ A = 42, [] + "" || (A = f) ]); + } + expect: { + function f(a, b) { + console.log(b); + } + f([ A = 42, [] + "" || (A = f) ]); + } + expect_stdout: "undefined" +} + +issue_5173_2: { + options = { + conditionals: true, + inline: true, + reduce_vars: true, + toplevel: true, + unused: true, + } + input: { + function f(a, b) { + console.log(b); + } + f([ A = 42, [] + "" || (A = f) ]); + } + expect: { + function f(a, b) { + console.log(b); + } + f(A = [] + "" ? 42 : f); + } + expect_stdout: "undefined" +} + +issue_5230: { + options = { + collapse_vars: true, + inline: true, + merge_vars: true, + reduce_vars: true, + side_effects: true, + toplevel: true, + } + input: { + while (function() { + function f(a) { + var b = 42, c = (console, [ a ]); + for (var k in c) + c, console.log(b++); + } + f(f); + }()); + } + expect: { + while (void (f = function(a) { + var b = 42; + console; + var a; + for (var k in a = [ a ]) + console.log(b++); + })(f)); + var f; + } + expect_stdout: "42" +} + +issue_5237: { + options = { + evaluate: true, + inline: true, + } + input: { + function f() { + (function() { + while (console.log(0/0)); + })(); + (function() { + var NaN = console && console.log(NaN); + })(); + } + f(); + } + expect: { + function f() { + while (console.log(NaN)); + (function() { + var NaN = console && console.log(NaN); + })(); + } + f(); + } + expect_stdout: [ + "NaN", + "undefined", + ] +} + +issue_5239: { + options = { + functions: true, + inline: true, + reduce_vars: true, + unused: true, + } + input: { + (function() { + (function(f) { + var a = 42, f = function() {}; + while (console.log(f.p || a++)); + })(); + })(); + } + expect: { + (function() { + var f = void 0; + var a = 42, f = function() {}; + while (console.log(f.p || a++)); + return; + })(); + } + expect_stdout: "42" +} + +issue_5240_1: { + options = { + inline: true, + } + input: { + function f() { + try { + throw "FAIL 1"; + } catch (e) { + return function() { + if (console) { + console.log(e); + var e = "FAIL 2"; + } + }(); + } + } + f(); + } + expect: { + function f() { + try { + throw "FAIL 1"; + } catch (e) { + return function() { + if (console) { + console.log(e); + var e = "FAIL 2"; + } + }(); + } + } + f(); + } + expect_stdout: "undefined" +} + +issue_5240_2: { + options = { + inline: true, + } + input: { + function f() { + try { + throw "FAIL 1"; + } catch (e) { + { + return function() { + if (console) { + console.log(e); + var e = "FAIL 2"; + } + }(); + } + } + } + f(); + } + expect: { + function f() { + try { + throw "FAIL 1"; + } catch (e) { + return function() { + if (console) { + console.log(e); + var e = "FAIL 2"; + } + }(); + } + } + f(); + } + expect_stdout: "undefined" +} + +issue_5249_1: { + options = { + inline: true, + } + input: { + console.log(function() { + if (!console) + var a = "FAIL 1"; + else + return void (a && function() { + while (console.log("FAIL 2")); + }()); + throw "FAIL 3"; + }()); + } + expect: { + console.log(function() { + if (!console) + var a = "FAIL 1"; + else if (a) { + while (console.log("FAIL 2")); + return; + } else + return void 0; + throw "FAIL 3"; + }()); + } + expect_stdout: "undefined" +} + +issue_5249_2: { + options = { + conditionals: true, + dead_code: true, + evaluate: true, + if_return: true, + inline: true, + passes: 3, + reduce_vars: true, + sequences: true, + side_effects: true, + unused: true, + } + input: { + console.log(function() { + if (!console) + var a = "FAIL 1"; + else + return void (a && function() { + while (console.log("FAIL 2")); + }()); + throw "FAIL 3"; + }()); + } + expect: { + console.log(function() { + if (!console) + throw "FAIL 3"; + }()); + } + expect_stdout: "undefined" +} + +issue_5254_1: { + options = { + inline: 3, + unused: true, + } + input: { + (function(a) { + while (a--) + (function f() { + var f = new function() { + console.log(f); + }(); + })(); + })(2); + } + expect: { + (function(a) { + while (a--) + f = void 0, + f = new function() { + console.log(f); + }(), + void 0; + var f; + })(2); + } + expect_stdout: [ + "undefined", + "undefined", + ] +} + +issue_5254_2: { + options = { + inline: true, + unused: true, + } + input: { + (function(a) { + while (a--) + (function f() { + var f = new function() { + console.log(f); + }(); + while (!console); + })(); + })(2); + } + expect: { + (function(a) { + while (a--) { + f = void 0; + var f = new function() { + console.log(f); + }(); + while (!console); + } + })(2); + } + expect_stdout: [ + "undefined", + "undefined", + ] +} + +issue_5263: { + options = { + inline: true, + side_effects: true, + toplevel: true, + } + input: { + for (var i = 0; i < 2; i++) (function() { + while (console.log(i)); + (function(a) { + console.log(a) && a, + a++; + })(); + })(); + } + expect: { + for (var i = 0; i < 2; i++) { + a = void 0; + while (console.log(i)); + console.log(a), + a++; + var a; + } + } + expect_stdout: [ + "0", + "undefined", + "1", + "undefined", + ] +} + +issue_5264_1: { + options = { + if_return: true, + inline: true, + reduce_vars: true, + unused: true, + } + input: { + console.log(function() { + function f(arguments) { + console.log(arguments); + (function() { + while (console.log("foo")); + })(); + } + f("bar"); + return arguments; + }("baz")[0]); + } + expect: { + console.log(function() { + (function(arguments) { + console.log(arguments); + while (console.log("foo")); + })("bar"); + return arguments; + }("baz")[0]); + } + expect_stdout: [ + "bar", + "foo", + "baz", + ] +} + +issue_5264_2: { + options = { + inline: true, + reduce_vars: true, + side_effects: true, + unused: true, + } + input: { + console.log(function() { + function f(arguments) { + console.log(arguments); + (function() { + while (console.log("foo")); + })(); + } + f("bar"); + return arguments; + }("baz")[0]); + } + expect: { + console.log(function() { + (function(arguments) { + console.log(arguments); + while (console.log("foo")); + })("bar"); + return arguments; + }("baz")[0]); + } + expect_stdout: [ + "bar", + "foo", + "baz", + ] +} + +issue_5283: { + options = { + conditionals: true, + if_return: true, + inline: true, + pure_getters: "strict", + side_effects: true, + unused: true, + } + input: { + var a = "FAIL 1"; + (function() { + (a = "PASS")[function() { + if (console) + return null; + var b = function f(a) { + console.log("FAIL 2"); + var c = a.p; + }(); + }()]; + })(); + console.log(a); + } + expect: { + var a = "FAIL 1"; + (function() { + a = "PASS"; + console || function(a) { + console.log("FAIL 2"); + a.p; + }(); + })(); + console.log(a); + } + expect_stdout: "PASS" +} + +issue_5290: { + options = { + functions: true, + inline: true, + reduce_vars: true, + side_effects: true, + toplevel: true, + unused: true, + } + input: { + var a = 1; + while (a--) new function(b) { + switch (b) { + case b.p: + case console.log("PASS"): + } + }(function() {}); + } + expect: { + var a = 1; + while (a--) { + b = void 0; + var b = function() {}; + switch (b) { + case b.p: + case console.log("PASS"): + } + } + } + expect_stdout: "PASS" +} + +issue_5296: { + options = { + inline: true, + } + input: { + var a = "PASS"; + (function() { + for (var i = 0; i < 2; i++) + try { + return function() { + while (!console); + var b = b && (a = b) || "FAIL"; + }(); + } finally { + continue; + } + })(); + console.log(a); + } + expect: { + var a = "PASS"; + (function() { + for (var i = 0; i < 2; i++) + try { + b = void 0; + while (!console); + var b = b && (a = b) || "FAIL"; + return; + } finally { + continue; + } + })(); + console.log(a); + } + expect_stdout: "PASS" +} + +issue_5316_1: { + options = { + collapse_vars: true, + evaluate: true, + inline: true, + reduce_vars: true, + toplevel: true, + } + input: { + do { + console.log("PASS"); + } while (function() { + var a, b = 42 && (console[a = b] = a++); + }()); + } + expect: { + do { + console.log("PASS"); + } while (b = a = void 0, b = (42, console[a = a] = a++), void 0); + var a, b; + } + expect_stdout: "PASS" +} + +issue_5316_2: { + options = { + collapse_vars: true, + evaluate: true, + inline: true, + reduce_vars: true, + toplevel: true, + } + input: { + do { + (function() { + var a, b = 42 && (console[a = b] = a++); + while (console.log("PASS")); + })(); + } while (!console); + } + expect: { + do { + a = void 0; + var a, b = (42, console[a = b = void 0] = a++); + while (console.log("PASS")); + } while (!console); + } + expect_stdout: "PASS" +} + +issue_5328: { + options = { + inline: true, + } + input: { + (function(arguments) { + console.log(Object.keys(arguments).join()); + })(this); + } + expect: { + (function(arguments) { + console.log(Object.keys(arguments).join()); + })(this); + } + expect_stdout: "" +} + +issue_5332_1: { + options = { + inline: true, + merge_vars: true, + reduce_vars: true, + side_effects: true, + toplevel: true, + unused: true, + } + input: { + do { + var a = {}; + for (A in a) + a; + } while (function() { + console.log(b); + var b = b; + }()); + } + expect: { + do { + var a = {}; + for (A in a); + } while (a = void 0, void console.log(a)); + } + expect_stdout: "undefined" +} + +issue_5332_2: { + options = { + inline: true, + merge_vars: true, + reduce_vars: true, + side_effects: true, + toplevel: true, + unused: true, + } + input: { + do { + var a = 42 in []; + for (A in a) + a; + } while (function() { + console.log(++b); + var b = b; + }()); + } + expect: { + do { + var a = 42 in []; + for (A in a); + } while (a = void 0, void console.log(++a)); + } + expect_stdout: "NaN" +} + +issue_5366: { + options = { + inline: true, + } + input: { + for (console.log("foo") || function() { + while (console.log("bar")); + }(); console.log("baz") ;); + } + expect: { + if (!console.log("foo")) + while (console.log("bar")); + for (;console.log("baz");); + } + expect_stdout: [ + "foo", + "bar", + "baz", + ] +} + +issue_5376_1: { + options = { + evaluate: true, + inline: true, + join_vars: true, + loops: true, + reduce_vars: true, + toplevel: true, + unused: true, + } + input: { + "use strict"; + var a; + for (;42;) + var b = function() { + var c; + throw new Error(c++); + }(); + } + expect: { + "use strict"; + for (;;) { + 42; + throw new Error(NaN); + } + } + expect_stdout: Error("NaN") +} + +issue_5376_2: { + options = { + evaluate: true, + inline: true, + join_vars: true, + loops: true, + side_effects: true, + toplevel: true, + unused: true, + } + input: { + "use strict"; + var a; + for (;42;) + var b = function() { + var c; + c++; + throw new Error("PASS"); + }(); + } + expect: { + "use strict"; + for (;;) { + 0; + throw new Error("PASS"); + } + } + expect_stdout: Error("PASS") +} + +issue_5401: { + options = { + inline: true, + } + input: { + L: for (var a in function() { + while (console.log("PASS")); + }(), a) do { + continue L; + } while (console.log("FAIL")); + } + expect: { + while (console.log("PASS")); + L: for (var a in a) do { + continue L; + } while (console.log("FAIL")); + } + expect_stdout: "PASS" +} + +issue_5409: { + options = { + inline: true, + merge_vars: true, + reduce_vars: true, + unused: true, + } + input: { + (function(a) { + (a = console) || FAIL(a); + (function(b) { + console.log(b && b); + while (!console); + })(); + })(); + } + expect: { + (function(a) { + (a = console) || FAIL(a); + a = void 0; + console.log(a && a); + while (!console); + return; + })(); + } + expect_stdout: "undefined" +} + +mixed_mode_inline_1: { + options = { + directives: true, + inline: true, + reduce_funcs: true, + reduce_vars: true, + toplevel: true, + unused: true, + } + input: { + function f() { + return this; + } + console.log(function() { + return f(); + }() ? "PASS" : "FAIL"); + } + expect: { + console.log(function() { + return this; + }() ? "PASS" : "FAIL"); + } + expect_stdout: "PASS" +} + +mixed_mode_inline_1_strict: { + options = { + directives: true, + inline: true, + reduce_funcs: true, + reduce_vars: true, + toplevel: true, + unused: true, + } + input: { + "use strict"; + function f() { + return this; + } + console.log(function() { + return f(); + }() ? "FAIL" : "PASS"); + } + expect: { + "use strict"; + console.log(function() { + return this; + }() ? "FAIL" : "PASS"); + } + expect_stdout: "PASS" +} + +mixed_mode_inline_2: { + options = { + directives: true, + inline: true, + reduce_funcs: true, + reduce_vars: true, + toplevel: true, + unused: true, + } + input: { + function f() { + "use strict"; + return this; + } + console.log(function() { + return f(); + }() ? "FAIL" : "PASS"); + } + expect: { + console.log(function() { + "use strict"; + return this; + }() ? "FAIL" : "PASS"); + } + expect_stdout: "PASS" +} + +mixed_mode_inline_2_strict: { + options = { + directives: true, + inline: true, + reduce_funcs: true, + reduce_vars: true, + toplevel: true, + unused: true, + } + input: { + "use strict"; + function f() { + "use strict"; + return this; + } + console.log(function() { + return f(); + }() ? "FAIL" : "PASS"); + } + expect: { + "use strict"; + console.log(function() { + return this; + }() ? "FAIL" : "PASS"); + } + expect_stdout: "PASS" +} + +mixed_mode_inline_3: { + options = { + directives: true, + inline: true, + reduce_funcs: true, + reduce_vars: true, + toplevel: true, + unused: true, + } + input: { + function f() { + return this; + } + console.log(function() { + "use strict"; + return f(); + }() ? "PASS" : "FAIL"); + } + expect: { + function f() { + return this; + } + console.log(function() { + "use strict"; + return f(); + }() ? "PASS" : "FAIL"); + } + expect_stdout: "PASS" +} + +mixed_mode_inline_3_strict: { + options = { + directives: true, + inline: true, + reduce_funcs: true, + reduce_vars: true, + toplevel: true, + unused: true, + } + input: { + "use strict"; + function f() { + return this; + } + console.log(function() { + "use strict"; + return f(); + }() ? "FAIL" : "PASS"); + } + expect: { + "use strict"; + console.log(function() { + return this; + }() ? "FAIL" : "PASS"); + } + expect_stdout: "PASS" +} + +mixed_mode_inline_4: { + options = { + directives: true, + inline: true, + reduce_funcs: true, + reduce_vars: true, + toplevel: true, + unused: true, + } + input: { + function f() { + "use strict"; + return this; + } + console.log(function() { + "use strict"; + return f(); + }() ? "FAIL" : "PASS"); + } + expect: { + console.log(function() { + "use strict"; + return function() { + "use strict"; + return this; + }(); + }() ? "FAIL" : "PASS"); + } + expect_stdout: "PASS" +} + +mixed_mode_inline_4_strict: { + options = { + directives: true, + inline: true, + reduce_funcs: true, + reduce_vars: true, + toplevel: true, + unused: true, + } + input: { + "use strict"; + function f() { + "use strict"; + return this; + } + console.log(function() { + "use strict"; + return f(); + }() ? "FAIL" : "PASS"); + } + expect: { + "use strict"; + console.log(function() { + return this; + }() ? "FAIL" : "PASS"); + } + expect_stdout: "PASS" +} + +module_inline: { + options = { + inline: true, + module: true, + reduce_vars: true, + toplevel: true, + } + input: { + var a = f; + function f() { + return a; + } + console.log(f() === a); + } + expect: { + var a = f; + function f() { + return a; + } + console.log(a === a); + } + expect_stdout: "true" +} + +single_use_inline_collision: { + options = { + inline: true, + reduce_funcs: true, + reduce_vars: true, + unused: true, + } + input: { + var a = "PASS"; + (function() { + var f = function() { + while (console.log(a)); + }; + (function() { + (function() { + f(); + })(); + (function(a) { + a || a("FAIL"); + })(console.log); + })(); + })(); + } + expect: { + var a = "PASS"; + (function() { + (function() { + while (console.log(a)); + return; + })(); + (function(a) { + a || a("FAIL"); + })(console.log); + return; + })(); + } + expect_stdout: "PASS" +} + +issue_5692: { + options = { + inline: true, + } + input: { + (function() { + while (console.log("PASS")) + if (console) + return; + })(); + } + expect: { + (function() { + while (console.log("PASS")) + if (console) + return; + })(); + } + expect_stdout: "PASS" +} + +issue_5766_1: { + options = { + booleans: true, + evaluate: true, + functions: true, + inline: true, + passes: 2, + reduce_vars: true, + toplevel: true, + unused: true, + } + input: { + log = function(a) { + console.log(typeof a); + }; + do { + (function() { + try { + var f = function() {}; + log(f && f); + } catch (e) {} + })(); + } while (0); + } + expect: { + log = function(a) { + console.log(typeof a); + }; + do { + try { + function f() {} + log(f); + } catch (e) {} + } while (0); + } + expect_stdout: "function" +} + +issue_5766_2: { + options = { + evaluate: true, + functions: true, + inline: true, + passes: 2, + reduce_vars: true, + side_effects: true, + toplevel: true, + unused: true, + } + input: { + log = function(a) { + console.log(typeof a); + }; + do { + (function() { + try { + var f = function() {}; + log(f && f); + } catch (e) {} + })(); + } while (0); + } + expect: { + log = function(a) { + console.log(typeof a); + }; + do { + try { + function f() {} + log(f); + } catch (e) {} + } while (0); + } + expect_stdout: "function" +} diff --git a/test/compress/global_defs.js b/test/compress/global_defs.js new file mode 100644 index 00000000000..006be292bfa --- /dev/null +++ b/test/compress/global_defs.js @@ -0,0 +1,232 @@ +must_replace: { + options = { + global_defs: { + D: "foo bar", + }, + } + input: { + console.log(D); + } + expect: { + console.log("foo bar"); + } +} + +repeated_nodes: { + options = { + global_defs: { + "@N": "rand()", + }, + } + input: { + console.log(N, N); + } + expect: { + console.log(rand(), rand()); + } +} + +keyword: { + options = { + global_defs: { + undefined: 0, + NaN: 1, + Infinity: 2, + }, + } + input: { + console.log(undefined, NaN, Infinity); + } + expect: { + console.log(0, 1, 2); + } +} + +object: { + options = { + evaluate: true, + global_defs: { + CONFIG: { + DEBUG: [ 0 ], + VALUE: 42, + }, + }, + side_effects: true, + unsafe: true, + } + input: { + function f(CONFIG) { + // CONFIG not global - do not replace + return CONFIG.VALUE; + } + function g() { + var CONFIG = { VALUE: 1 }; + // CONFIG not global - do not replace + return CONFIG.VALUE; + } + function h() { + return CONFIG.VALUE; + } + if (CONFIG.DEBUG[0]) + console.debug("foo"); + } + expect: { + function f(CONFIG) { + return CONFIG.VALUE; + } + function g() { + var CONFIG = { VALUE: 1 }; + return CONFIG.VALUE; + } + function h() { + return 42; + } + if (0) + console.debug("foo"); + } +} + +expanded: { + options = { + global_defs: { + "CONFIG.DEBUG": [ 0 ], + "CONFIG.VALUE": 42, + }, + } + input: { + function f(CONFIG) { + // CONFIG not global - do not replace + return CONFIG.VALUE; + } + function g() { + var CONFIG = { VALUE: 1 }; + // CONFIG not global - do not replace + return CONFIG.VALUE; + } + function h() { + return CONFIG.VALUE; + } + if (CONFIG.DEBUG[0]) + console.debug("foo"); + } + expect: { + function f(CONFIG) { + return CONFIG.VALUE; + } + function g() { + var CONFIG = { VALUE: 1 }; + return CONFIG.VALUE; + } + function h() { + return 42; + } + if ([0][0]) + console.debug("foo"); + } +} + +mixed: { + options = { + evaluate: true, + global_defs: { + "CONFIG.VALUE": 42, + "FOO.BAR": "moo", + }, + properties: true, + } + input: { + var FOO = { BAR: 0 }; + console.log(FOO.BAR); + console.log(++CONFIG.DEBUG); + console.log(++CONFIG.VALUE); + console.log(++CONFIG["VAL" + "UE"]); + console.log(++DEBUG[CONFIG.VALUE]); + CONFIG.VALUE.FOO = "bar"; + console.log(CONFIG); + } + expect: { + var FOO = { BAR: 0 }; + console.log("moo"); + console.log(++CONFIG.DEBUG); + console.log(++CONFIG.VALUE); + console.log(++CONFIG.VALUE); + console.log(++DEBUG[42]); + CONFIG.VALUE.FOO = "bar"; + console.log(CONFIG); + } + expect_warnings: [ + "WARN: global_defs CONFIG.VALUE redefined [test/compress/global_defs.js:4,22]", + "WARN: global_defs CONFIG.VALUE redefined [test/compress/global_defs.js:7,8]", + ] +} + +issue_1801: { + options = { + booleans: true, + global_defs: { + "CONFIG.FOO.BAR": true, + }, + } + input: { + console.log(CONFIG.FOO.BAR); + } + expect: { + console.log(!0); + } +} + +issue_1986: { + options = { + global_defs: { + "@alert": "console.log", + }, + } + input: { + alert(42); + } + expect: { + console.log(42); + } +} + +issue_2167: { + options = { + conditionals: true, + dead_code: true, + evaluate: true, + global_defs: { + "@isDevMode": "function(){}", + }, + passes: 2, + side_effects: true, + } + input: { + if (isDevMode()) { + greetOverlord(); + } + doWork(); + } + expect: { + doWork(); + } +} + +issue_3217: { + options = { + collapse_vars: true, + global_defs: { + "@o": "{fn:function(){var a=42;console.log(a)}}", + }, + inline: true, + properties: true, + reduce_vars: true, + side_effects: true, + unused: true, + } + input: { + o.fn(); + } + expect: { + console.log(42); + } +} diff --git a/test/compress/hoist_props.js b/test/compress/hoist_props.js new file mode 100644 index 00000000000..35d9127b620 --- /dev/null +++ b/test/compress/hoist_props.js @@ -0,0 +1,1242 @@ +issue_2377_1: { + options = { + evaluate: true, + hoist_props: true, + inline: true, + reduce_funcs: true, + reduce_vars: true, + toplevel: true, + unused: true, + } + input: { + var obj = { + foo: 1, + bar: 2, + square: function(x) { + return x * x; + }, + cube: function(x) { + return x * x * x; + }, + }; + console.log(obj.foo, obj.cube(3)); + } + expect: { + var obj_foo = 1, obj_cube = function(x) { + return x * x * x; + }; + console.log(obj_foo, obj_cube(3)); + } + expect_stdout: "1 27" +} + +issue_2377_2: { + options = { + evaluate: true, + hoist_props: true, + inline: true, + passes: 2, + reduce_funcs: true, + reduce_vars: true, + toplevel: true, + unused: true, + } + input: { + var obj = { + foo: 1, + bar: 2, + square: function(x) { + return x * x; + }, + cube: function(x) { + return x * x * x; + }, + }; + console.log(obj.foo, obj.cube(3)); + } + expect: { + console.log(1, (x = 3, x * x * x)); + var x; + } + expect_stdout: "1 27" +} + +issue_2377_3: { + options = { + evaluate: true, + hoist_props: true, + inline: true, + passes: 4, + reduce_funcs: true, + reduce_vars: true, + side_effects: true, + toplevel: true, + unused: true, + } + input: { + var obj = { + foo: 1, + bar: 2, + square: function(x) { + return x * x; + }, + cube: function(x) { + return x * x * x; + }, + }; + console.log(obj.foo, obj.cube(3)); + } + expect: { + console.log(1, 27); + } + expect_stdout: "1 27" +} + +direct_access_1: { + options = { + hoist_props: true, + reduce_funcs: true, + reduce_vars: true, + toplevel: true, + unused: true, + } + input: { + var a = 0; + var obj = { + a: 1, + b: 2, + }; + for (var k in obj) a++; + console.log(a, obj.a); + } + expect: { + var a = 0; + var obj = { + a: 1, + b: 2, + }; + for (var k in obj) a++; + console.log(a, obj.a); + } + expect_stdout: "2 1" +} + +direct_access_2: { + options = { + hoist_props: true, + reduce_funcs: true, + reduce_vars: true, + toplevel: true, + unused: true, + } + input: { + var o = { a: 1 }; + var f = function(k) { + if (o[k]) return "PASS"; + }; + console.log(f("a")); + } + expect: { + var o = { a: 1 }; + console.log(function(k) { + if (o[k]) return "PASS"; + }("a")); + } + expect_stdout: "PASS" +} + +direct_access_3: { + options = { + hoist_props: true, + reduce_funcs: true, + reduce_vars: true, + toplevel: true, + unused: true, + } + input: { + var o = { a: 1 }; + o.b; + console.log(o.a); + } + expect: { + var o = { a: 1 }; + o.b; + console.log(o.a); + } + expect_stdout: "1" +} + +single_use: { + options = { + hoist_props: true, + reduce_funcs: true, + reduce_vars: true, + toplevel: true, + unused: true, + } + input: { + var obj = { + bar: function() { + return 42; + }, + }; + console.log(obj.bar()); + } + expect: { + console.log({ + bar: function() { + return 42; + }, + }.bar()); + } +} + +name_collision_1: { + options = { + hoist_props: true, + reduce_funcs: true, + reduce_vars: true, + toplevel: true, + } + input: { + var obj_foo = 1; + var obj_bar = 2; + function f() { + var obj = { + foo: 3, + bar: 4, + "b-r": 5, + "b+r": 6, + "b!r": 7, + }; + console.log(obj_foo, obj.foo, obj.bar, obj["b-r"], obj["b+r"], obj["b!r"]); + } + f(); + } + expect: { + var obj_foo = 1; + var obj_bar = 2; + function f() { + var obj, + obj_foo$0 = 3, + obj_bar = 4, + obj_b_r = 5, + obj_b_r$0 = 6, + obj_b_r$1 = 7; + console.log(obj_foo, obj_foo$0, obj_bar, obj_b_r, obj_b_r$0, obj_b_r$1); + } + f(); + } + expect_stdout: "1 3 4 5 6 7" +} + +name_collision_2: { + options = { + hoist_props: true, + reduce_funcs: true, + reduce_vars: true, + toplevel: true, + } + input: { + var o = { + p: 1, + "+": function(x) { + return x; + }, + "-": function(x) { + return x + 1; + } + }, o__$0 = 2, o__$1 = 3; + console.log(o.p === o.p, o["+"](4), o["-"](5), o__$0, o__$1); + } + expect: { + var o, + o_p = 1, + o__ = function(x) { + return x; + }, + o__$2 = function(x) { + return x + 1; + }, + o__$0 = 2, + o__$1 = 3; + console.log(o_p === o_p, o__(4), o__$2(5), o__$0, o__$1); + } + expect_stdout: "true 4 6 2 3" +} + +name_collision_3: { + options = { + hoist_props: true, + reduce_funcs: true, + reduce_vars: true, + toplevel: true, + } + input: { + var o = { + p: 1, + "+": function(x) { + return x; + }, + "-": function(x) { + return x + 1; + } + }, o__$0 = 2, o__$1 = 3; + console.log(o.p === o.p, o["+"](4), o["-"](5)); + } + expect: { + var o, + o_p = 1, + o__ = function(x) { + return x; + }, + o__$2 = function(x) { + return x + 1; + }, + o__$0 = 2, + o__$1 = 3; + console.log(o_p === o_p, o__(4), o__$2(5)); + } + expect_stdout: "true 4 6" +} + +name_collision_4: { + options = { + hoist_props: true, + reduce_vars: true, + } + input: { + console.log(function() { + var o = { + p: 0, + q: "PASS", + }; + return function(o_p) { + if (!o.p) return o_p; + }(o.q); + }()); + } + expect: { + console.log(function() { + var o, o_p$0 = 0, o_q = "PASS"; + return function(o_p) { + if (!o_p$0) return o_p; + }(o_q); + }()); + } + expect_stdout: "PASS" +} + +contains_this_1: { + options = { + evaluate: true, + hoist_props: true, + inline: true, + passes: 2, + reduce_funcs: true, + reduce_vars: true, + toplevel: true, + unused: true, + } + input: { + var o = { + u: function() { + return this === this; + }, + p: 1 + }; + console.log(o.p, o.p); + } + expect: { + console.log(1, 1); + } + expect_stdout: "1 1" +} + +contains_this_2: { + options = { + evaluate: true, + hoist_props: true, + inline: true, + passes: 2, + reduce_funcs: true, + reduce_vars: true, + toplevel: true, + unused: true, + } + input: { + var o = { + u: function() { + return this === this; + }, + p: 1 + }; + console.log(o.p, o.p, o.u); + } + expect: { + console.log(1, 1, function() { + return this === this; + }); + } + expect_stdout: true +} + +contains_this_3: { + options = { + evaluate: true, + hoist_props: true, + inline: true, + passes: 2, + reduce_funcs: true, + reduce_vars: true, + toplevel: true, + unused: true, + } + input: { + var o = { + u: function() { + return this === this; + }, + p: 1 + }; + console.log(o.p, o.p, o.u()); + } + expect: { + var o = { + u: function() { + return this === this; + }, + p: 1 + }; + console.log(o.p, o.p, o.u()); + } + expect_stdout: "1 1 true" +} + +new_this: { + options = { + evaluate: true, + hoist_props: true, + inline: true, + passes: 2, + reduce_funcs: true, + reduce_vars: true, + toplevel: true, + unused: true, + } + input: { + var o = { + a: 1, + b: 2, + f: function(a) { + this.b = a; + } + }; + console.log(new o.f(o.a).b, o.b); + } + expect: { + console.log(new function(a) { + this.b = a; + }(1).b, 2); + } + expect_stdout: "1 2" +} + +issue_2473_1: { + options = { + hoist_props: false, + reduce_vars: true, + top_retain: [ + "x", + "y" + ], + toplevel: true, + unused: true, + } + input: { + var x = {}; + var y = []; + var z = {}; + } + expect: { + var x = {}; + var y = []; + } + expect_warnings: [ + "INFO: Retaining variable x", + "INFO: Retaining variable y", + "WARN: Dropping unused variable z [test/compress/hoist_props.js:3,12]", + ] +} + +issue_2473_2: { + options = { + hoist_props: true, + reduce_vars: true, + top_retain: [ + "x", + "y" + ], + toplevel: true, + unused: true, + } + input: { + var x = {}; + var y = []; + var z = {}; + } + expect: { + var x = {}; + var y = []; + } + expect_warnings: [ + "INFO: Retaining variable x", + "INFO: Retaining variable y", + "WARN: Dropping unused variable z [test/compress/hoist_props.js:3,12]", + ] +} + +issue_2473_3: { + options = { + hoist_props: true, + reduce_vars: true, + top_retain: "o", + toplevel: true, + unused: true, + } + input: { + var o = { + a: 1, + b: 2, + }; + console.log(o.a, o.b); + } + expect: { + var o = { + a: 1, + b: 2, + }; + console.log(o.a, o.b); + } + expect_stdout: "1 2" + expect_warnings: [ + "INFO: Retaining variable o", + ] +} + +issue_2473_4: { + options = { + hoist_props: true, + reduce_vars: true, + top_retain: "o", + toplevel: true, + unused: true, + } + input: { + (function() { + var o = { + a: 1, + b: 2, + }; + console.log(o.a, o.b); + })(); + } + expect: { + (function() { + var o_a = 1, o_b = 2; + console.log(o_a, o_b); + })(); + } + expect_stdout: "1 2" + expect_warnings: [ + "INFO: Dropping unused variable o [test/compress/hoist_props.js:2,16]", + ] +} + +issue_2508_1: { + options = { + collapse_vars: true, + hoist_props: true, + reduce_vars: true, + toplevel: true, + unused: true, + } + input: { + var o = { + a: [ 1 ], + f: function(x) { + console.log(x); + } + }; + o.f(o.a); + } + expect: { + (function(x) { + console.log(x); + })([ 1 ]); + } + expect_stdout: true +} + +issue_2508_2: { + options = { + collapse_vars: true, + hoist_props: true, + reduce_vars: true, + toplevel: true, + unused: true, + } + input: { + var o = { + a: { b: 2 }, + f: function(x) { + console.log(x); + } + }; + o.f(o.a); + } + expect: { + (function(x) { + console.log(x); + })({ b: 2 }); + } + expect_stdout: true +} + +issue_2508_3: { + options = { + collapse_vars: true, + hoist_props: true, + reduce_vars: true, + toplevel: true, + unused: true, + } + input: { + var o = { + a: [ o ], + f: function(x) { + console.log(x); + } + }; + o.f(o.a); + } + expect: { + var o = { + a: [ o ], + f: function(x) { + console.log(x); + } + }; + o.f(o.a); + } + expect_stdout: true +} + +issue_2508_4: { + options = { + collapse_vars: true, + hoist_props: true, + reduce_vars: true, + toplevel: true, + unused: true, + } + input: { + var o = { + a: { b: o }, + f: function(x) { + console.log(x); + } + }; + o.f(o.a); + } + expect: { + var o = { + a: { b: o }, + f: function(x) { + console.log(x); + } + }; + o.f(o.a); + } + expect_stdout: true +} + +issue_2508_5: { + options = { + collapse_vars: true, + hoist_props: true, + reduce_vars: true, + toplevel: true, + unused: true, + } + input: { + var o = { + f: function(x) { + console.log(x); + } + }; + o.f(o.f); + } + expect: { + var o_f = function(x) { + console.log(x); + }; + o_f(o_f); + } + expect_stdout: true +} + +issue_2519: { + options = { + collapse_vars: true, + evaluate: true, + hoist_props: true, + reduce_vars: true, + unused: true, + } + input: { + function testFunc() { + var dimensions = { + minX: 5, + maxX: 6, + }; + var scale = 1; + var d = { + x: (dimensions.maxX + dimensions.minX) / 2, + }; + return d.x * scale; + } + console.log(testFunc()); + } + expect: { + function testFunc() { + return +((6 + 5) / 2); + } + console.log(testFunc()); + } + expect_stdout: "5.5" +} + +undefined_key: { + options = { + evaluate: true, + hoist_props: true, + join_vars: true, + passes: 4, + reduce_vars: true, + toplevel: true, + unused: true, + } + input: { + var a, o = {}; + o[a] = 1; + o.b = 2; + console.log(o[a] + o.b); + } + expect: { + console.log(3); + } + expect_stdout: "3" +} + +issue_3021: { + options = { + hoist_props: true, + reduce_vars: true, + } + input: { + var a = 1, b = 2; + (function() { + b = a; + if (a++ + b--) + return 1; + return; + var b = {}; + })(); + console.log(a, b); + } + expect: { + var a = 1, b = 2; + (function() { + b = a; + if (a++ + b--) + return 1; + return; + var b = {}; + })(); + console.log(a, b); + } + expect_stdout: "2 2" +} + +issue_3046: { + options = { + hoist_props: true, + reduce_vars: true, + } + input: { + console.log(function(a) { + do { + var b = { + c: a++ + }; + } while (b.c && a); + return a; + }(0)); + } + expect: { + console.log(function(a) { + do { + var b, b_c = a++; + } while (b_c && a); + return a; + }(0)); + } + expect_stdout: "1" +} + +issue_3071_1: { + options = { + evaluate: true, + hoist_props: true, + inline: true, + join_vars: true, + passes: 3, + reduce_vars: true, + sequences: true, + side_effects: true, + unused: true, + } + input: { + (function() { + var obj = {}; + obj.one = 1; + obj.two = 2; + console.log(obj.one, obj.two); + })(); + } + expect: { + console.log(1, 2); + } + expect_stdout: "1 2" +} + +issue_3071_1_toplevel: { + options = { + evaluate: true, + hoist_props: true, + inline: true, + join_vars: true, + passes: 3, + reduce_vars: true, + sequences: true, + side_effects: true, + toplevel: true, + unused: true, + } + input: { + (function() { + var obj = {}; + obj.one = 1; + obj.two = 2; + console.log(obj.one, obj.two); + })(); + } + expect: { + console.log(1, 2); + } + expect_stdout: "1 2" +} + +issue_3071_2: { + options = { + hoist_props: true, + inline: true, + join_vars: true, + passes: 3, + reduce_vars: true, + sequences: true, + side_effects: true, + unused: true, + } + input: { + (function() { + obj = {}; + obj.one = 1; + obj.two = 2; + console.log(obj.one, obj.two); + var obj; + })(); + } + expect: { + console.log(1, 2); + } + expect_stdout: "1 2" +} + +issue_3071_2_toplevel: { + options = { + hoist_props: true, + inline: true, + join_vars: true, + passes: 3, + reduce_vars: true, + sequences: true, + side_effects: true, + toplevel: true, + unused: true, + } + input: { + (function() { + obj = {}; + obj.one = 1; + obj.two = 2; + console.log(obj.one, obj.two); + var obj; + })(); + } + expect: { + console.log(1, 2); + } + expect_stdout: "1 2" +} + +issue_3071_3: { + options = { + hoist_props: true, + reduce_vars: true, + } + input: { + var c = 0; + (function(a, b) { + (function f(o) { + var n = 2; + while (--b + (o = { + p: c++, + }) && --n > 0); + })(); + })(); + console.log(c); + } + expect: { + var c = 0; + (function(a, b) { + (function f(o) { + var n = 2; + while (--b + (o = { + p: c++, + }) && --n > 0); + })(); + })(); + console.log(c); + } + expect_stdout: "2" +} + +issue_3411: { + options = { + hoist_props: true, + reduce_vars: true, + } + input: { + var c = 1; + !function f() { + var o = { + p: --c && f() + }; + +o || console.log("PASS"); + }(); + } + expect: { + var c = 1; + !function f() { + var o, o_p = --c && f(); + +{} || console.log("PASS"); + }(); + } + expect_stdout: "PASS" +} + +issue_3440: { + options = { + hoist_props: true, + reduce_vars: true, + unused: true, + } + input: { + (function() { + function f() { + console.log(o.p); + } + var o = { + p: "PASS", + }; + return f; + })()(); + } + expect: { + (function() { + var o_p = "PASS"; + return function() { + console.log(o_p); + }; + })()(); + } + expect_stdout: "PASS" +} + +issue_3868: { + options = { + hoist_props: true, + passes: 2, + reduce_vars: true, + side_effects: true, + } + input: { + (function(t) { + t = {}; + ({ + get p() {}, + q: (console.log("PASS"), +t), + }).r; + })(); + } + expect: { + (function(t) { + t = {}; + ({ + get p() {}, + q: (console.log("PASS"), +t), + }).r; + })(); + } + expect_stdout: "PASS" +} + +issue_3871: { + options = { + hoist_props: true, + reduce_vars: true, + } + input: { + console.log(function() { + do { + var b = { + get null() { + c; + } + }; + } while (!b); + return "PASS"; + }()); + } + expect: { + console.log(function() { + do { + var b = { + get null() { + c; + } + }; + } while (!b); + return "PASS"; + }()); + } + expect_stdout: "PASS" +} + +issue_3945_1: { + options = { + hoist_props: true, + reduce_vars: true, + } + input: { + function f() { + o.p; + var o = { + q: 0, + }; + } + } + expect: { + function f() { + o.p; + var o, o_q = 0; + } + } +} + +issue_3945_2: { + options = { + hoist_props: true, + reduce_vars: true, + toplevel: true, + } + input: { + console.log(typeof o); + var o = { + p: 0, + }; + } + expect: { + console.log(typeof o); + var o, o_p = 0; + } + expect_stdout: "undefined" +} + +issue_4023: { + options = { + comparisons: true, + hoist_props: true, + inline: true, + reduce_vars: true, + toplevel: true, + typeofs: true, + unused: true, + } + input: { + function f() { + var a = function() { + return { p: 0 }; + }(); + return console.log("undefined" != typeof a); + } + f(); + } + expect: { + console.log(void 0 !== {}); + } + expect_stdout: "true" +} + +object_super: { + options = { + hoist_props: true, + reduce_vars: true, + toplevel: true, + } + input: { + var o = { + f(a) { + return a ? console.log("PASS") : super.log("PASS"); + }, + }; + o.f(42); + } + expect: { + var o = { + f(a) { + return a ? console.log("PASS") : super.log("PASS"); + }, + }; + o.f(42); + } + expect_stdout: "PASS" + node_version: ">=4" +} + +issue_4985: { + options = { + hoist_props: true, + reduce_vars: true, + toplevel: true, + } + input: { + var a = { p: 42 }; + console.log(function() { + a; + }()); + } + expect: { + var a, a_p = 42; + console.log(function() { + ({}); + }()); + } + expect_stdout: "undefined" +} + +issue_5182: { + options = { + hoist_props: true, + merge_vars: true, + passes: 2, + reduce_vars: true, + side_effects: true, + toplevel: true, + unused: true, + } + input: { + var o = console; + log = o.log; + o = { + p: function(a) { + console.log(a ? "PASS" : "FAIL"); + return a; + }, + }; + log(o.p(42)); + } + expect: { + var o = console; + log = o.log; + o = function(a) { + console.log(a ? "PASS" : "FAIL"); + return a; + }; + log(o(42)); + } + expect_stdout: [ + "PASS", + "42", + ] +} + +issue_5441: { + options = { + hoist_props: true, + passes: 2, + reduce_vars: true, + side_effects: true, + } + input: { + console.log(function(a) { + (function() { + a = { p: this }; + })(); + return typeof a; + }()); + } + expect: { + console.log(function(a) { + (function() { + a_p = this; + })(); + var a_p; + return typeof {}; + }()); + } + expect_stdout: "object" +} + +issue_5498: { + options = { + hoist_props: true, + reduce_vars: true, + toplevel: true, + } + input: { + var o = { + __proto__: 42, + }; + while (console.log(typeof o.__proto__)); + } + expect: { + var o = { + __proto__: 42, + }; + while (console.log(typeof o.__proto__)); + } + expect_stdout: "object" +} diff --git a/test/compress/hoist_vars.js b/test/compress/hoist_vars.js new file mode 100644 index 00000000000..8eb055233a9 --- /dev/null +++ b/test/compress/hoist_vars.js @@ -0,0 +1,803 @@ +statements: { + options = { + hoist_funs: false, + hoist_vars: true, + join_vars: true, + unused: true, + } + input: { + function f() { + var a = 1; + var b = 2; + var c = 3; + function g() {} + return g(a, b, c); + } + } + expect: { + function f() { + var a = 1, b = 2, c = 3; + function g() {} + return g(a, b, c); + } + } +} + +statements_funs: { + options = { + hoist_funs: true, + hoist_vars: true, + join_vars: true, + unused: true, + } + input: { + function f() { + var a = 1; + var b = 2; + var c = 3; + function g() {} + return g(a, b, c); + } + } + expect: { + function f() { + function g() {} + var a = 1, b = 2, c = 3; + return g(a, b, c); + } + } +} + +sequences: { + options = { + hoist_funs: false, + hoist_vars: true, + join_vars: true, + unused: true, + } + input: { + function f() { + var a = 1, b = 2; + function g() {} + var c = 3; + return g(a, b, c); + } + } + expect: { + function f() { + var c, a = 1, b = 2; + function g() {} + c = 3; + return g(a, b, c); + } + } +} + +sequences_funs: { + options = { + hoist_funs: true, + hoist_vars: true, + join_vars: true, + unused: true, + } + input: { + function f() { + var a = 1, b = 2; + function g() {} + var c = 3; + return g(a, b, c); + } + } + expect: { + function f() { + function g() {} + var a = 1, b = 2, c = 3; + return g(a, b, c); + } + } +} + +catch_var: { + options = { + dead_code: true, + hoist_vars: true, + side_effects: true, + toplevel: true, + unused: true, + } + input: { + var a = "PASS"; + try { + a; + } catch (a) { + var a = 0; + a; + } + console.log(a); + } + expect: { + a = "PASS"; + var a; + console.log(a); + } + expect_stdout: "PASS" +} + +issue_2295: { + options = { + collapse_vars: true, + hoist_vars: true, + join_vars: true, + unused: true, + } + input: { + function foo(o) { + var a = o.a; + if (a) return a; + var a = 1; + } + } + expect: { + function foo(o) { + var a = o.a; + if (a) return a; + a = 1; + } + } +} + +issue_4487_1: { + options = { + functions: true, + hoist_vars: true, + join_vars: true, + keep_fnames: true, + reduce_vars: true, + toplevel: true, + unused: true, + } + input: { + var a = function f() { + var f = console.log(typeof f); + }; + var b = a(); + } + expect: { + var a = function f() { + var f = console.log(typeof f); + }; + a(); + } + expect_stdout: "undefined" +} + +issue_4487_2: { + options = { + functions: true, + hoist_vars: true, + join_vars: true, + keep_fnames: true, + passes: 2, + reduce_vars: true, + toplevel: true, + unused: true, + } + input: { + var a = function f() { + var f = console.log(typeof f); + }; + var b = a(); + } + expect: { + function a() { + var f = console.log(typeof f); + } + a(); + } + expect_stdout: "undefined" +} + +issue_4487_3: { + options = { + functions: true, + hoist_vars: true, + join_vars: true, + keep_fnames: true, + passes: 3, + reduce_vars: true, + toplevel: true, + unused: true, + } + input: { + var a = function f() { + var f = console.log(typeof f); + }; + var b = a(); + } + expect: { + (function a() { + console.log(typeof void 0); + })(); + } + expect_stdout: "undefined" +} + +issue_4489: { + options = { + collapse_vars: true, + evaluate: true, + hoist_vars: true, + reduce_vars: true, + toplevel: true, + unused: true, + } + input: { + A = 0; + var o = !0 || null; + for (var k in o); + console.log(k); + } + expect: { + for (var k in !(A = 0)); + console.log(k); + } + expect_stdout: "undefined" +} + +issue_4517: { + options = { + collapse_vars: true, + hoist_vars: true, + join_vars: true, + reduce_vars: true, + unused: true, + } + input: { + console.log(function() { + var a = 2; + A = a; + var b = typeof !1; + return A + b; + }()); + } + expect: { + console.log(function() { + return (A = 2) + typeof !1; + }()); + } + expect_stdout: "2boolean" +} + +issue_4736: { + options = { + collapse_vars: true, + evaluate: true, + hoist_vars: true, + join_vars: true, + merge_vars: true, + reduce_vars: true, + toplevel: true, + unused: true, + } + input: { + var a; + function f() { + (function g() { + var b = (a = 0, 1 << 30); + var c = (a = 0, console.log(b)); + var d = c; + })(f); + } + f(); + } + expect: { + (function() { + (function() { + 0; + console.log(1 << 30); + })(); + })(); + } + expect_stdout: "1073741824" +} + +issue_4839: { + options = { + evaluate: true, + hoist_vars: true, + join_vars: true, + keep_fargs: false, + reduce_vars: true, + toplevel: true, + unused: true, + } + input: { + var log = console.log, o = function(a, b) { + return b && b; + }("foo"); + for (var k in o) + throw "FAIL"; + log("PASS"); + } + expect: { + var k, log = console.log; + for (k in void 0) + throw "FAIL"; + log("PASS"); + } + expect_stdout: "PASS" +} + +issue_4859: { + options = { + evaluate: true, + hoist_vars: true, + join_vars: true, + keep_infinity: true, + merge_vars: true, + reduce_vars: true, + toplevel: true, + unused: true, + } + input: { + function f(a) { + var b = (a = 2, 1 / 0), c = 3; + var d = a + b; + console.log(d); + return f; + } + f(); + } + expect: { + (function f(a) { + console.log(2 + 1 / 0); + return f; + })(); + } + expect_stdout: "Infinity" +} + +issue_4893_1: { + options = { + collapse_vars: true, + evaluate: true, + hoist_vars: true, + reduce_vars: true, + toplevel: true, + unused: true, + } + input: { + function f() { + function g() {} + var a = null; + var b = null; + var c = null; + b.p += a = 42; + f; + } + try { + f(); + } catch (e) { + console.log("PASS"); + } + } + expect: { + try{ + (function f() { + null.p += 42; + f; + })(); + } catch (e) { + console.log("PASS"); + } + } + expect_stdout: "PASS" +} + +issue_4893_2: { + options = { + collapse_vars: true, + hoist_vars: true, + pure_getters: "strict", + reduce_vars: true, + side_effects: true, + toplevel: true, + unused: true, + } + input: { + function f() { + function g() {} + var a = null; + var b = null; + var c = null; + b.p += a = 42; + f; + } + try { + f(); + } catch (e) { + console.log("PASS"); + } + } + expect: { + try{ + (function() { + var a; + a = null; + a.p += 42; + })(); + } catch (e) { + console.log("PASS"); + } + } + expect_stdout: "PASS" +} + +issue_4898: { + options = { + collapse_vars: true, + evaluate: true, + hoist_vars: true, + loops: true, + reduce_vars: true, + toplevel: true, + unused: true, + } + input: { + do { + var b = [ console.log("PASS") ]; + var c = b; + } while (c.p = 0); + } + expect: { + var b; + b = [ console.log("PASS") ]; + b.p = 0; + } + expect_stdout: "PASS" +} + +issue_5187_1: { + options = { + hoist_props: true, + hoist_vars: true, + reduce_vars: true, + side_effects: true, + toplevel: true, + unused: true, + } + input: { + function f() { + var a = 42; + do { + var b = { 0: a++ }; + } while (console.log(b[b ^= 0])); + } + f(); + } + expect: { + (function() { + var a, b; + a = 42; + do { + b = { 0: a++ }; + } while (console.log(b[b ^= 0])); + })(); + } + expect_stdout: "42" +} + +issue_5187_2: { + options = { + hoist_props: true, + hoist_vars: true, + join_vars: true, + reduce_vars: true, + side_effects: true, + toplevel: true, + unused: true, + } + input: { + function f() { + var a = 42; + do { + var b = { 0: a++ }; + } while (console.log(b[b ^= 0])); + } + f(); + } + expect: { + (function() { + var b, a = 42; + do { + b = { 0: a++ }; + } while (console.log(b[b ^= 0])); + })(); + } + expect_stdout: "42" +} + +issue_5195: { + options = { + hoist_props: true, + hoist_vars: true, + reduce_vars: true, + side_effects: true, + toplevel: true, + unused: true, + } + input: { + function f() { + var a; + do { + var b = { p: a }; + } while (console.log(b += "")); + } + f(); + } + expect: { + (function() { + var a, b; + do { + b = { p: a }; + } while (console.log(b += "")); + })(); + } + expect_stdout: "[object Object]" +} + +issue_5378: { + options = { + hoist_vars: true, + inline: true, + toplevel: true, + } + input: { + var a = 2; + while (a--) + (function() { + var b; + var c; + while (console.log(b)); + --b; + })(); + } + expect: { + var a = 2; + while (a--) { + b = void 0; + var b, c; + while (console.log(b)); + --b; + } + } + expect_stdout: [ + "undefined", + "undefined", + ] +} + +issue_5411_1: { + options = { + collapse_vars: true, + dead_code: true, + hoist_vars: true, + reduce_vars: true, + side_effects: true, + toplevel: true, + } + input: { + var a = "PASS"; + b++; + b = a; + var b = b, c = c && c[b]; + console.log(b); + } + expect: { + var a, b, c; + b++; + b = a = "PASS"; + c = c && c[b]; + console.log(b); + } + expect_stdout: "PASS" +} + +issue_5411_2: { + options = { + collapse_vars: true, + dead_code: true, + evaluate: true, + hoist_vars: true, + reduce_vars: true, + toplevel: true, + unused: true, + } + input: { + var a = "PASS"; + b++; + b = a; + var b = b, c = c && c[b]; + console.log(b); + } + expect: { + var b, c; + b++; + b = "PASS", + c; + console.log(b); + } + expect_stdout: "PASS" +} + +issue_5411_3: { + options = { + collapse_vars: true, + hoist_vars: true, + reduce_vars: true, + toplevel: true, + } + input: { + var a = console; + a++; + var a = A = a; + console.log(A); + } + expect: { + var a; + a = console; + a = A = ++a; + console.log(A); + } + expect_stdout: "NaN" +} + +issue_5411_4: { + options = { + collapse_vars: true, + hoist_vars: true, + join_vars: true, + reduce_vars: true, + toplevel: true, + unused: true, + } + input: { + var a = console; + a++; + var a = A = a; + console.log(A); + } + expect: { + var a = console; + a = A = ++a; + console.log(A); + } + expect_stdout: "NaN" +} + +issue_5626: { + options = { + conditionals: true, + evaluate: true, + hoist_vars: true, + reduce_vars: true, + side_effects: true, + toplevel: true, + unused: true, + } + input: { + var a = function() { + return console.log(arguments[0]), 42; + }("PASS") ? null : "foo"; + for (var b in a) + FAIL; + } + expect: { + (function() { + console.log(arguments[0]); + }("PASS")); + for (var b in null) + FAIL; + } + expect_stdout: "PASS" +} + +issue_5638_1: { + options = { + collapse_vars: true, + hoist_vars: true, + reduce_vars: true, + toplevel: true, + } + input: { + var a = "FAIL"; + var a = [ 42 ]; + console || FAIL(a); + console.log(a++); + } + expect: { + var a; + a = "FAIL"; + a = [ 42 ]; + console || FAIL(a); + console.log(a++); + } + expect_stdout: "42" +} + +issue_5638_2: { + options = { + collapse_vars: true, + hoist_vars: true, + reduce_vars: true, + toplevel: true, + } + input: { + var a = "FAIL"; + var a = [ 6 ]; + console || FAIL(a); + console.log(a *= 7); + } + expect: { + var a; + a = "FAIL"; + a = [ 6 ]; + console || FAIL(a); + console.log(a *= 7); + } + expect_stdout: "42" +} + +issue_5638_3: { + options = { + collapse_vars: true, + hoist_vars: true, + pure_getters: "strict", + reduce_vars: true, + toplevel: true, + } + input: { + var log = console.log; + var o = { foo: 42 }; + for (var k in o) { + var v = o[k]; + log(k || v, v++); + } + } + expect: { + var log, o, k, v; + log = console.log; + for (k in o = { foo: 42 }) { + v = o[k]; + log(k || v, v++); + } + } + expect_stdout: "foo 42" +} + +issue_5638_4: { + options = { + collapse_vars: true, + hoist_vars: true, + pure_getters: "strict", + reduce_vars: true, + toplevel: true, + } + input: { + var log = console.log; + var o = { foo: 6 }; + for (var k in o) { + var v = o[k]; + log(k || v, v *= 7); + } + } + expect: { + var log, o, k, v; + log = console.log; + for (k in o = { foo: 6 }) { + v = o[k]; + log(k || v, v *= 7); + } + } + expect_stdout: "foo 42" +} diff --git a/test/compress/html_comments.js b/test/compress/html_comments.js new file mode 100644 index 00000000000..e208b36d6fd --- /dev/null +++ b/test/compress/html_comments.js @@ -0,0 +1,107 @@ +html_comment_in_expression: { + input: { + (function(a, b) { + console.log(a < !--b && a-- > b, a, b); + })(1, 2); + } + expect_exact: "(function(a,b){console.log(ab,a,b)})(1,2);" + expect_stdout: "false 1 1" +} + +html_comment_in_less_than: { + input: { + (function(a, b, c) { + console.log( + a < !--b, + a < !--b + c, + a + b < !--c, + a, b, c + ); + })(1, 2, 3); + } + expect_exact: "(function(a,b,c){console.log(a b, + a-- > b + c, + a + b-- > c, + a, b, c + ); + })(1, 2, 3); + } + expect_exact: "(function(a,b,c){console.log(a-- >b,a-- >b+c,a+b-- >c,a,b,c)})(1,2,3);" + expect_stdout: "false false false -1 1 3" +} + +html_comment_in_greater_than_or_equal: { + input: { + (function(a, b, c) { + console.log( + a-- >= b, + a-- >= b + c, + a + b-- >= c, + a, b, c + ); + })(1, 2, 3); + } + expect_exact: "(function(a,b,c){console.log(a-- >=b,a-- >=b+c,a+b-- >=c,a,b,c)})(1,2,3);" + expect_stdout: "false false false -1 1 3" +} + +html_comment_in_right_shift: { + input: { + (function(a, b, c) { + console.log( + a-- >> b, + a-- >> b + c, + a + b-- >> c, + a, b, c + ); + })(1, 2, 3); + } + expect_exact: "(function(a,b,c){console.log(a-- >>b,a-- >>b+c,a+b-- >>c,a,b,c)})(1,2,3);" + expect_stdout: "0 0 0 -1 1 3" +} + +html_comment_in_zero_fill_right_shift: { + input: { + (function(a, b, c) { + console.log( + a-- >>> b, + a-- >>> b + c, + a + b-- >>> c, + a, b, c + ); + })(1, 2, 3); + } + expect_exact: "(function(a,b,c){console.log(a-- >>>b,a-- >>>b+c,a+b-- >>>c,a,b,c)})(1,2,3);" + expect_stdout: "0 0 0 -1 1 3" +} + +html_comment_in_string_literal: { + input: { + console.log("comment in".length); + } + expect_exact: 'console.log("\\x3c!--HTML--\\x3ecomment in\\x3c!--string literal--\\x3e".length);' + expect_stdout: "42" +} diff --git a/test/compress/ie.js b/test/compress/ie.js new file mode 100644 index 00000000000..60c48c429d1 --- /dev/null +++ b/test/compress/ie.js @@ -0,0 +1,3476 @@ +do_screw: { + options = { + ie: false, + } + beautify = { + ie: false, + ascii_only: true, + } + input: { + f("\v"); + } + expect_exact: 'f("\\v");' +} + +dont_screw: { + options = { + ie: true, + } + beautify = { + ie: true, + ascii_only: true, + } + input: { + f("\v"); + } + expect_exact: 'f("\\x0B");' +} + +do_screw_constants: { + options = { + ie: false, + } + input: { + f(undefined, Infinity); + } + expect_exact: "f(void 0,1/0);" +} + +dont_screw_constants: { + options = { + ie: true, + } + input: { + f(undefined, Infinity); + } + expect_exact: "f(undefined,Infinity);" +} + +do_screw_try_catch: { + options = { + ie: false, + } + mangle = { + ie: false, + } + beautify = { + ie: false, + } + input: { + good = function(e){ + return function(error){ + try { + e() + } catch (e) { + error(e) + } + } + }; + } + expect: { + good = function(n){ + return function(t){ + try { + n() + } catch (n) { + t(n) + } + } + }; + } +} + +dont_screw_try_catch: { + options = { + ie: true, + } + mangle = { + ie: true, + } + beautify = { + ie: true, + } + input: { + bad = function(e){ + return function(error){ + try { + e() + } catch (e) { + error(e) + } + } + }; + } + expect: { + bad = function(t){ + return function(n){ + try { + t() + } catch (t) { + n(t) + } + } + }; + } +} + +do_screw_try_catch_undefined: { + options = { + ie: false, + } + mangle = { + ie: false, + } + beautify = { + ie: false, + } + input: { + function a(b) { + try { + throw "Stuff"; + } catch (undefined) { + console.log("caught: " + undefined); + } + console.log("undefined is " + undefined); + return b === undefined; + } + console.log(a(42), a(void 0)); + } + expect: { + function a(o) { + try { + throw "Stuff"; + } catch (o) { + console.log("caught: " + o); + } + console.log("undefined is " + void 0); + return void 0 === o; + } + console.log(a(42), a(void 0)); + } + expect_stdout: [ + "caught: Stuff", + "undefined is undefined", + "caught: Stuff", + "undefined is undefined", + "false true", + ] +} + +dont_screw_try_catch_undefined: { + options = { + ie: true, + } + mangle = { + ie: true, + } + beautify = { + ie: true, + } + input: { + function a(b) { + try { + throw "Stuff"; + } catch (undefined) { + console.log("caught: " + undefined); + } + // IE8: undefined is Stuff + console.log("undefined is " + undefined); + return b === undefined; + } + console.log(a(42), a(void 0)); + } + expect: { + function a(n) { + try { + throw "Stuff"; + } catch (undefined) { + console.log("caught: " + undefined); + } + console.log("undefined is " + undefined); + return n === undefined; + } + console.log(a(42), a(void 0)); + } + expect_stdout: [ + "caught: Stuff", + "undefined is undefined", + "caught: Stuff", + "undefined is undefined", + "false true", + ] +} + +reduce_vars: { + options = { + evaluate: true, + ie: true, + reduce_funcs: true, + reduce_vars: true, + unused: true, + } + mangle = { + ie: true, + } + input: { + function f() { + var a; + try { + x(); + } catch (a) { + y(); + } + alert(a); + } + } + expect: { + function f() { + var t; + try { + x(); + } catch (t) { + y(); + } + alert(t); + } + } +} + +typeof_getAttribute: { + options = { + comparisons: true, + ie: false, + typeofs: true, + } + input: { + document = { + createElement: function() { + return { + getAttribute: function() {}, + }; + }, + write: console.log, + }; + document.write(function(element) { + if (element) + return "undefined" === typeof element.getAttribute; + }(document.createElement("foo")) ? "FAIL" : "PASS"); + } + expect: { + document = { + createElement: function() { + return { + getAttribute: function() {}, + }; + }, + write: console.log, + }; + document.write(function(element) { + if (element) + // IE6~10: Access is denied. + return void 0 === element.getAttribute; + }(document.createElement("foo")) ? "FAIL" : "PASS"); + } + expect_stdout: "PASS" +} + +typeof_getAttribute_ie6: { + options = { + comparisons: true, + ie: true, + typeofs: true, + } + input: { + document = { + createElement: function() { + return { + getAttribute: function() {}, + }; + }, + write: console.log, + }; + document.write(function(element) { + if (element) + return "undefined" === typeof element.getAttribute; + }(document.createElement("foo")) ? "FAIL" : "PASS"); + } + expect: { + document = { + createElement: function() { + return { + getAttribute: function() {}, + }; + }, + write: console.log, + }; + document.write(function(element) { + if (element) + return "undefined" == typeof element.getAttribute; + }(document.createElement("foo")) ? "FAIL" : "PASS"); + } + expect_stdout: "PASS" +} + +issue_1586_1: { + options = { + ie: true, + } + mangle = { + ie: true, + } + input: { + function f() { + try { + x(); + } catch (err) { + console.log(err.message); + } + } + } + expect_exact: "function f(){try{x()}catch(c){console.log(c.message)}}" +} + +issue_1586_2: { + options = { + ie: false, + } + mangle = { + ie: false, + } + input: { + function f() { + try { + x(); + } catch (err) { + console.log(err.message); + } + } + } + expect_exact: "function f(){try{x()}catch(c){console.log(c.message)}}" +} + +issue_2120_1: { + mangle = { + ie: false, + } + input: { + "aaaaaaaa"; + var a = 1, b = "FAIL"; + try { + throw 1; + } catch (c) { + try { + throw 0; + } catch (a) { + if (c) b = "PASS"; + } + } + console.log(b); + } + expect: { + "aaaaaaaa"; + var a = 1, b = "FAIL"; + try { + throw 1; + } catch (t) { + try { + throw 0; + } catch (a) { + if (t) b = "PASS"; + } + } + console.log(b); + } + expect_stdout: "PASS" +} + +issue_2120_2: { + mangle = { + ie: true, + } + input: { + "aaaaaaaa"; + var a = 1, b = "FAIL"; + try { + throw 1; + } catch (c) { + try { + throw 0; + } catch (a) { + if (c) b = "PASS"; + } + } + console.log(b); + } + expect: { + "aaaaaaaa"; + var a = 1, b = "FAIL"; + try { + throw 1; + } catch (c) { + try { + throw 0; + } catch (a) { + if (c) b = "PASS"; + } + } + console.log(b); + } + expect_stdout: "PASS" +} + +issue_2254_1: { + mangle = { + ie: false, + } + input: { + "eeeeee"; + try { + console.log(f("PASS")); + } catch (e) {} + function f(s) { + try { + throw "FAIL"; + } catch (e) { + return s; + } + } + } + expect: { + "eeeeee"; + try { + console.log(f("PASS")); + } catch (e) {} + function f(t) { + try { + throw "FAIL"; + } catch (e) { + return t; + } + } + } + expect_stdout: "PASS" +} + +issue_2254_2: { + mangle = { + ie: true, + } + input: { + "eeeeee"; + try { + console.log(f("PASS")); + } catch (e) {} + function f(s) { + try { + throw "FAIL"; + } catch (e) { + return s; + } + } + } + expect: { + "eeeeee"; + try { + console.log(f("PASS")); + } catch (e) {} + function f(t) { + try { + throw "FAIL"; + } catch (e) { + return t; + } + } + } + expect_stdout: "PASS" +} + +issue_24_1: { + mangle = { + ie: false, + } + input: { + (function(a) { + console.log(typeof function f(){} === typeof a ? "FAIL" : "PASS"); + })(); + } + expect: { + (function(o) { + console.log(typeof function o(){} === typeof o ? "FAIL" : "PASS"); + })(); + } + expect_stdout: "PASS" +} + +issue_24_2: { + mangle = { + ie: true, + } + input: { + (function(a) { + console.log(typeof function f(){} === typeof a ? "FAIL" : "PASS"); + })(); + } + expect: { + (function(o) { + console.log(typeof function n(){} === typeof o ? "FAIL" : "PASS"); + })(); + } + expect_stdout: "PASS" +} + +issue_2976_1: { + mangle = { + ie: false, + } + input: { + console.log(function f() { + var a; + return a === f ? "FAIL" : "PASS"; + }()); + } + expect: { + console.log(function n() { + var o; + return o === n ? "FAIL" : "PASS"; + }()); + } + expect_stdout: "PASS" +} + +issue_2976_2: { + mangle = { + ie: true, + } + input: { + console.log(function f() { + var a; + return a === f ? "FAIL" : "PASS"; + }()); + } + expect: { + console.log(function f() { + var n; + return n === f ? "FAIL" : "PASS"; + }()); + } + expect_stdout: "PASS" +} + +issue_2976_3: { + mangle = { + ie: true, + toplevel: true, + } + input: { + console.log(function f() { + var a; + return a === f ? "FAIL" : "PASS"; + }()); + } + expect: { + console.log(function o() { + var n; + return n === o ? "FAIL" : "PASS"; + }()); + } + expect_stdout: "PASS" +} + +issue_3035: { + mangle = { + ie: false, + } + input: { + var c = "FAIL"; + (function(a) { + try { + throw 1; + } catch (b) { + try { + throw 0; + } catch (a) { + b && (c = "PASS"); + } + } + })(); + console.log(c); + } + expect: { + var c = "FAIL"; + (function(o) { + try { + throw 1; + } catch (t) { + try { + throw 0; + } catch (o) { + t && (c = "PASS"); + } + } + })(); + console.log(c); + } + expect_stdout: "PASS" +} + +issue_3035_ie8: { + mangle = { + ie: true, + } + input: { + var c = "FAIL"; + (function(a) { + try { + throw 1; + } catch (b) { + try { + throw 0; + } catch (a) { + b && (c = "PASS"); + } + } + })(); + console.log(c); + } + expect: { + var c = "FAIL"; + (function(t) { + try { + throw 1; + } catch (o) { + try { + throw 0; + } catch (t) { + o && (c = "PASS"); + } + } + })(); + console.log(c); + } + expect_stdout: "PASS" +} + +issue_3197_1: { + options = { + ie: false, + inline: true, + reduce_vars: true, + side_effects: true, + unused: true, + } + mangle = { + ie: false, + } + input: { + !function() { + function Foo() { + console.log(this instanceof Foo); + } + window.Foo = Foo; + }(); + new window.Foo(); + } + expect: { + window.Foo = function o() { + console.log(this instanceof o); + }; + new window.Foo(); + } + expect_stdout: "true" +} + +issue_3197_1_ie8: { + options = { + ie: true, + inline: true, + reduce_vars: true, + side_effects: true, + unused: true, + } + mangle = { + ie: true, + } + input: { + !function() { + function Foo() { + console.log(this instanceof Foo); + } + window.Foo = Foo; + }(); + new window.Foo(); + } + expect: { + window.Foo = function Foo() { + console.log(this instanceof Foo); + }; + new window.Foo(); + } + expect_stdout: "true" +} + +issue_3197_2: { + mangle = { + ie: false, + } + input: { + (function(a) { + var f = function f() { + console.log(this instanceof f); + }; + new f(a); + })(); + } + expect: { + (function(n) { + var o = function n() { + console.log(this instanceof n); + }; + new o(n); + })(); + } + expect_stdout: "true" +} + +issue_3197_2_ie8: { + mangle = { + ie: true, + } + input: { + (function(a) { + var f = function f() { + console.log(this instanceof f); + }; + new f(a); + })(); + } + expect: { + (function(n) { + var o = function o() { + console.log(this instanceof o); + }; + new o(n); + })(); + } + expect_stdout: "true" +} + +issue_3206_1: { + options = { + evaluate: true, + ie: false, + reduce_vars: true, + typeofs: true, + unused: true, + } + input: { + console.log(function() { + var foo = function bar() {}; + var baz = function moo() {}; + return "function" == typeof bar; + }()); + } + expect: { + console.log(function() { + return "function" == typeof bar; + }()); + } + expect_stdout: "false" +} + +issue_3206_2: { + options = { + evaluate: true, + ie: true, + reduce_vars: true, + typeofs: true, + unused: true, + } + input: { + console.log(function() { + var foo = function bar() {}; + var baz = function moo() {}; + return "function" == typeof bar; + }()); + } + expect: { + console.log(function() { + (function bar() {}); + return "function" == typeof bar; + }()); + } + expect_stdout: "false" +} + +issue_3215_1: { + mangle = { + ie: false, + } + input: { + console.log(function foo() { + var bar = function bar(name) { + return "PASS"; + }; + try { + "moo"; + } catch (e) { + bar = function bar(name) { + return "FAIL"; + }; + } + return bar; + }()()); + } + expect: { + console.log(function n() { + var o = function n(o) { + return "PASS"; + }; + try { + "moo"; + } catch (n) { + o = function n(o) { + return "FAIL"; + }; + } + return o; + }()()); + } + expect_stdout: "PASS" +} + +issue_3215_2: { + mangle = { + ie: true, + } + input: { + console.log(function foo() { + var bar = function bar(name) { + return "PASS"; + }; + try { + "moo"; + } catch (e) { + bar = function bar(name) { + return "FAIL"; + }; + } + return bar; + }()()); + } + expect: { + console.log(function foo() { + var o = function o(n) { + return "PASS"; + }; + try { + "moo"; + } catch (n) { + o = function o(n) { + return "FAIL"; + }; + } + return o; + }()()); + } + expect_stdout: "PASS" +} + +issue_3215_3: { + mangle = { + ie: false, + } + input: { + console.log(function foo() { + var bar = function bar(name) { + return "FAIL"; + }; + try { + moo; + } catch (e) { + bar = function bar(name) { + return "PASS"; + }; + } + return bar; + }()()); + } + expect: { + console.log(function n() { + var o = function n(o) { + return "FAIL"; + }; + try { + moo; + } catch (n) { + o = function n(o) { + return "PASS"; + }; + } + return o; + }()()); + } + expect_stdout: "PASS" +} + +issue_3215_4: { + mangle = { + ie: true, + } + input: { + console.log(function foo() { + var bar = function bar(name) { + return "FAIL"; + }; + try { + moo; + } catch (e) { + bar = function bar(name) { + return "PASS"; + }; + } + return bar; + }()()); + } + expect: { + console.log(function foo() { + var o = function o(n) { + return "FAIL"; + }; + try { + moo; + } catch (n) { + o = function o(n) { + return "PASS"; + }; + } + return o; + }()()); + } + expect_stdout: "PASS" +} + +issue_3355_1: { + mangle = { + ie: false, + } + input: { + (function f() { + var f; + })(); + (function g() { + })(); + console.log(typeof f === typeof g); + } + expect: { + (function o() { + var o; + })(); + (function o() { + })(); + console.log(typeof f === typeof g); + } + expect_stdout: "true" +} + +issue_3355_2: { + mangle = { + ie: true, + } + input: { + (function f() { + var f; + })(); + (function g() { + })(); + console.log(typeof f === typeof g); + } + expect: { + (function f() { + var f; + })(); + (function g() { + })(); + console.log(typeof f === typeof g); + } + expect_stdout: "true" +} + +issue_3355_3: { + mangle = { + ie: false, + } + input: { + !function(a) { + "aaaaaaaaaa"; + a(); + var b = function c() { + var c = 42; + console.log("FAIL"); + }; + }(function() { + console.log("PASS"); + }); + } + expect: { + !function(a) { + "aaaaaaaaaa"; + a(); + var o = function a() { + var a = 42; + console.log("FAIL"); + }; + }(function() { + console.log("PASS"); + }); + } + expect_stdout: "PASS" +} + +issue_3355_4: { + mangle = { + ie: true, + } + input: { + !function(a) { + "aaaaaaaaaa"; + a(); + var b = function c() { + var c = 42; + console.log("FAIL"); + }; + }(function() { + console.log("PASS"); + }); + } + expect: { + !function(a) { + "aaaaaaaaaa"; + a(); + var o = function n() { + var n = 42; + console.log("FAIL"); + }; + }(function() { + console.log("PASS"); + }); + } + expect_stdout: "PASS" +} + +issue_3468: { + options = { + collapse_vars: true, + ie: false, + } + input: { + var a = 42; + console.log(function a() { + a++; + return typeof a; + }()); + } + expect: { + var a = 42; + console.log(function a() { + a++; + return typeof a; + }()); + } + expect_stdout: "function" +} + +issue_3468_ie8: { + options = { + collapse_vars: true, + ie: true, + } + input: { + var a = 42; + console.log(function a() { + a++; + return typeof a; + }()); + } + expect: { + var a = 42; + console.log(function a() { + a++; + return typeof a; + }()); + } + expect_stdout: "function" +} + +issue_3471: { + options = { + ie: false, + functions: true, + reduce_vars: true, + toplevel: true, + unused: true, + } + input: { + var c = 1; + function f() { + var a = function g() { + --c && f(); + g.p = 0; + }; + for (var p in a) + a[p]; + } + f(); + } + expect: { + var c = 1; + (function f() { + function a() { + --c && f(); + a.p = 0; + } + for (var p in a) + a[p]; + })(); + } + expect_stdout: true +} + +issue_3471_ie8: { + options = { + ie: true, + functions: true, + reduce_vars: true, + toplevel: true, + unused: true, + } + input: { + var c = 1; + function f() { + var a = function g() { + --c && f(); + g.p = 0; + }; + for (var p in a) + a[p]; + } + f(); + } + expect: { + var c = 1; + (function f() { + var a = function g() { + --c && f(); + g.p = 0; + }; + for (var p in a) + a[p]; + })(); + } + expect_stdout: true +} + +issue_3473: { + rename = true + mangle = { + ie: false, + toplevel: false, + } + input: { + var d = 42, a = 100, b = 10, c = 0; + (function b() { + try { + c++; + } catch (b) {} + })(); + console.log(a, b, c); + } + expect: { + var d = 42, a = 100, b = 10, c = 0; + (function a() { + try { + c++; + } catch (a) {} + })(); + console.log(a, b, c); + } + expect_stdout: "100 10 1" +} + +issue_3473_ie8: { + rename = true + mangle = { + ie: true, + toplevel: false, + } + input: { + var d = 42, a = 100, b = 10, c = 0; + (function b() { + try { + c++; + } catch (b) {} + })(); + console.log(a, b, c); + } + expect: { + var d = 42, a = 100, b = 10, c = 0; + (function b() { + try { + c++; + } catch (b) {} + })(); + console.log(a, b, c); + } + expect_stdout: "100 10 1" +} + +issue_3473_toplevel: { + rename = true + mangle = { + ie: false, + toplevel: true, + } + input: { + var d = 42, a = 100, b = 10, c = 0; + (function b() { + try { + c++; + } catch (b) {} + })(); + console.log(a, b, c); + } + expect: { + var c = 42, o = 100, n = 10, t = 0; + (function c() { + try { + t++; + } catch (c) {} + })(); + console.log(o, n, t); + } + expect_stdout: "100 10 1" +} + +issue_3473_ie8_toplevel: { + rename = true + mangle = { + ie: true, + toplevel: true, + } + input: { + var d = 42, a = 100, b = 10, c = 0; + (function b() { + try { + c++; + } catch (b) {} + })(); + console.log(a, b, c); + } + expect: { + var c = 42, o = 100, n = 10, t = 0; + (function n() { + try { + t++; + } catch (n) {} + })(); + console.log(o, n, t); + } + expect_stdout: "100 10 1" +} + +issue_3475: { + rename = true + mangle = { + ie: false, + toplevel: false, + } + input: { + "ooooo ddddd"; + var a = "FAIL"; + try { + throw 42; + } catch (b) { + (function f() { + a = "PASS"; + })(); + } + console.log(a); + } + expect: { + "ooooo ddddd"; + var a = "FAIL"; + try { + throw 42; + } catch (o) { + (function o() { + a = "PASS"; + })(); + } + console.log(a); + } + expect_stdout: "PASS" +} + +issue_3475_ie8: { + rename = true + mangle = { + ie: true, + toplevel: false, + } + input: { + "ooooo ddddd"; + var a = "FAIL"; + try { + throw 42; + } catch (b) { + (function f() { + a = "PASS"; + })(); + } + console.log(a); + } + expect: { + "ooooo ddddd"; + var a = "FAIL"; + try { + throw 42; + } catch (b) { + (function f() { + a = "PASS"; + })(); + } + console.log(a); + } + expect_stdout: "PASS" +} + +issue_3475_toplevel: { + rename = true + mangle = { + ie: false, + toplevel: true, + } + input: { + "ooooo ddddd"; + var a = "FAIL"; + try { + throw 42; + } catch (b) { + (function f() { + a = "PASS"; + })(); + } + console.log(a); + } + expect: { + "ooooo ddddd"; + var d = "FAIL"; + try { + throw 42; + } catch (o) { + (function o() { + d = "PASS"; + })(); + } + console.log(d); + } + expect_stdout: "PASS" +} + +issue_3475_ie8_toplevel: { + rename = true + mangle = { + ie: true, + toplevel: true, + } + input: { + "ooooo ddddd"; + var a = "FAIL"; + try { + throw 42; + } catch (b) { + (function f() { + a = "PASS"; + })(); + } + console.log(a); + } + expect: { + "ooooo ddddd"; + var o = "FAIL"; + try { + throw 42; + } catch (d) { + (function c() { + o = "PASS"; + })(); + } + console.log(o); + } + expect_stdout: "PASS" +} + +issue_3478_1: { + rename = true + mangle = { + ie: false, + toplevel: false, + } + input: { + "aaaaaaaaaa"; + (function f() { + (function f() { + var a; + console.log(typeof f); + })(); + })(); + } + expect: { + "aaaaaaaaaa"; + (function a() { + (function a() { + var o; + console.log(typeof a); + })(); + })(); + } + expect_stdout: "function" +} + +issue_3478_1_ie8: { + rename = true + mangle = { + ie: true, + toplevel: false, + } + input: { + "aaaaaaaaaa"; + (function f() { + (function f() { + var a; + console.log(typeof f); + })(); + })(); + } + expect: { + "aaaaaaaaaa"; + (function f() { + (function f() { + var a; + console.log(typeof f); + })(); + })(); + } + expect_stdout: "function" +} + +issue_3478_1_toplevel: { + rename = true + mangle = { + ie: false, + toplevel: true, + } + input: { + "aaaaaaaaaa"; + (function f() { + (function f() { + var a; + console.log(typeof f); + })(); + })(); + } + expect: { + "aaaaaaaaaa"; + (function a() { + (function a() { + var o; + console.log(typeof a); + })(); + })(); + } + expect_stdout: "function" +} + +issue_3478_1_ie8_toplevel: { + rename = true + mangle = { + ie: true, + toplevel: true, + } + input: { + "aaaaaaaaaa"; + (function f() { + (function f() { + var a; + console.log(typeof f); + })(); + })(); + } + expect: { + "aaaaaaaaaa"; + (function o() { + (function o() { + var a; + console.log(typeof o); + })(); + })(); + } + expect_stdout: "function" +} + +issue_3478_2: { + rename = true + mangle = { + ie: false, + toplevel: false, + } + input: { + "bbbbbbb"; + var c = "FAIL"; + (function f() { + (function f() { + var b = function g() { + f && (c = "PASS"); + }(); + })(); + })(); + console.log(c); + } + expect: { + "bbbbbbb"; + var c = "FAIL"; + (function b() { + (function n() { + var b = function b() { + n && (c = "PASS"); + }(); + })(); + })(); + console.log(c); + } + expect_stdout: "PASS" +} + +issue_3478_2_ie8: { + rename = true + mangle = { + ie: true, + toplevel: false, + } + input: { + "bbbbbbb"; + var c = "FAIL"; + (function f() { + (function f() { + var b = function g() { + f && (c = "PASS"); + }(); + })(); + })(); + console.log(c); + } + expect: { + "bbbbbbb"; + var c = "FAIL"; + (function f() { + (function f() { + var b = function n() { + f && (c = "PASS"); + }(); + })(); + })(); + console.log(c); + } + expect_stdout: "PASS" +} + +issue_3478_2_toplevel: { + rename = true + mangle = { + ie: false, + toplevel: true, + } + input: { + "bbbbbbb"; + var c = "FAIL"; + (function f() { + (function f() { + var b = function g() { + f && (c = "PASS"); + }(); + })(); + })(); + console.log(c); + } + expect: { + "bbbbbbb"; + var o = "FAIL"; + (function b() { + (function n() { + var b = function b() { + n && (o = "PASS"); + }(); + })(); + })(); + console.log(o); + } + expect_stdout: "PASS" +} + +issue_3478_2_ie8_toplevel: { + rename = true + mangle = { + ie: true, + toplevel: true, + } + input: { + "bbbbbbb"; + var c = "FAIL"; + (function f() { + (function f() { + var b = function g() { + f && (c = "PASS"); + }(); + })(); + })(); + console.log(c); + } + expect: { + "bbbbbbb"; + var o = "FAIL"; + (function c() { + (function c() { + var b = function n() { + c && (o = "PASS"); + }(); + })(); + })(); + console.log(o); + } + expect_stdout: "PASS" +} + +issue_3482_1: { + options = { + evaluate: true, + ie: false, + } + input: { + try { + throw 42; + } catch (NaN) { + var a = +"a"; + } + console.log(a, NaN, 0 / 0); + } + expect: { + try { + throw 42; + } catch (NaN) { + var a = 0 / 0; + } + console.log(a, NaN, NaN); + } + expect_stdout: "NaN NaN NaN" +} + +issue_3482_1_ie8: { + options = { + evaluate: true, + ie: true, + } + input: { + try { + throw 42; + } catch (NaN) { + var a = +"a"; + } + // IE8: NaN 42 NaN + console.log(a, NaN, 0 / 0); + } + expect: { + try { + throw 42; + } catch (NaN) { + var a = 0 / 0; + } + console.log(a, NaN, 0 / 0); + } + expect_stdout: "NaN NaN NaN" +} + +issue_3482_2: { + options = { + evaluate: true, + ie: false, + } + input: { + (function() { + try { + throw 42; + } catch (NaN) { + a = +"a"; + } + })(); + console.log(a, NaN, 0 / 0); + } + expect: { + (function() { + try { + throw 42; + } catch (NaN) { + a = 0 / 0; + } + })(); + console.log(a, NaN, NaN); + } + expect_stdout: "NaN NaN NaN" +} + +issue_3482_2_ie8: { + options = { + evaluate: true, + ie: true, + } + input: { + (function() { + try { + throw 42; + } catch (NaN) { + a = +"a"; + } + })(); + console.log(a, NaN, 0 / 0); + } + expect: { + (function() { + try { + throw 42; + } catch (NaN) { + a = 0 / 0; + } + })(); + console.log(a, NaN, 0 / 0); + } + expect_stdout: "NaN NaN NaN" +} + +issue_3484_1: { + options = { + ie: false, + side_effects: true, + toplevel: false, + } + input: { + (function f() {})(); + console.log(typeof f); + } + expect: { + console.log(typeof f); + } + expect_stdout: "undefined" +} + +issue_3484_1_ie8: { + options = { + ie: true, + side_effects: true, + toplevel: false, + } + input: { + (function f() {})(); + // IE8: function + console.log(typeof f); + } + expect: { + (function f() {})(); + console.log(typeof f); + } + expect_stdout: "undefined" +} + +issue_3484_1_toplevel: { + options = { + ie: false, + side_effects: true, + toplevel: true, + } + input: { + (function f() {})(); + console.log(typeof f); + } + expect: { + console.log(typeof f); + } + expect_stdout: "undefined" +} + +issue_3484_1_ie8_toplevel: { + options = { + ie: true, + side_effects: true, + toplevel: true, + } + input: { + (function f() {})(); + // IE8: function + console.log(typeof f); + } + expect: { + (function f() {})(); + console.log(typeof f); + } + expect_stdout: "undefined" +} + +issue_3484_2: { + options = { + evaluate: true, + ie: false, + reduce_vars: true, + toplevel: false, + } + input: { + (function Infinity() { + var Infinity; + })(); + console.log(typeof (1 / 0), typeof Infinity); + } + expect: { + (function Infinity() { + var Infinity; + })(); + console.log("number", "number"); + } + expect_stdout: "number number" +} + +issue_3484_2_ie8: { + options = { + evaluate: true, + ie: true, + reduce_vars: true, + toplevel: false, + } + input: { + (function Infinity() { + var Infinity; + })(); + // IE8: number function + console.log(typeof (1 / 0), typeof Infinity); + } + expect: { + (function Infinity() { + var Infinity; + })(); + console.log("number", typeof Infinity); + } + expect_stdout: "number number" +} + +issue_3484_2_toplevel: { + options = { + evaluate: true, + ie: false, + reduce_vars: true, + toplevel: true, + } + input: { + (function Infinity() { + var Infinity; + })(); + console.log(typeof (1 / 0), typeof Infinity); + } + expect: { + (function Infinity() { + var Infinity; + })(); + console.log("number", "number"); + } + expect_stdout: "number number" +} + +issue_3484_2_ie8_toplevel: { + options = { + evaluate: true, + ie: true, + reduce_vars: true, + toplevel: true, + } + input: { + (function Infinity() { + var Infinity; + })(); + // IE8: number function + console.log(typeof (1 / 0), typeof Infinity); + } + expect: { + (function Infinity() { + var Infinity; + })(); + console.log("number", typeof Infinity); + } + expect_stdout: "number number" +} + +issue_3486: { + options = { + conditionals: true, + ie: false, + reduce_vars: true, + } + input: { + (function a() { + (function a(a) { + console.log(a ? "FAIL" : "PASS"); + })(); + })(); + } + expect: { + (function a() { + (function a(a) { + console.log(a ? "FAIL" : "PASS"); + })(); + })(); + } + expect_stdout: "PASS" +} + +issue_3486_ie8: { + options = { + conditionals: true, + ie: true, + reduce_vars: true, + } + input: { + (function a() { + (function a(a) { + console.log(a ? "FAIL" : "PASS"); + })(); + })(); + } + expect: { + (function a() { + (function a(a) { + console.log(a ? "FAIL" : "PASS"); + })(); + })(); + } + expect_stdout: "PASS" +} + +issue_3493: { + options = { + dead_code: true, + ie: false, + } + input: { + var c = "PASS"; + (function() { + try { + (function a() { + throw {}; + })(); + } catch (a) { + a >>= 0; + a && (c = "FAIL"); + } + })(); + console.log(c); + } + expect: { + var c = "PASS"; + (function() { + try { + (function a() { + throw {}; + })(); + } catch (a) { + a >>= 0; + a && (c = "FAIL"); + } + })(); + console.log(c); + } + expect_stdout: "PASS" +} + +issue_3493_ie8: { + options = { + dead_code: true, + ie: true, + } + input: { + var c = "PASS"; + (function() { + try { + (function a() { + throw {}; + })(); + } catch (a) { + a >>= 0; + a && (c = "FAIL"); + } + })(); + console.log(c); + } + expect: { + var c = "PASS"; + (function() { + try { + (function a() { + throw {}; + })(); + } catch (a) { + a >>= 0; + a && (c = "FAIL"); + } + })(); + console.log(c); + } + expect_stdout: "PASS" +} + +issue_3523: { + mangle = { + ie: false, + toplevel: false, + } + input: { + var a = 0, b, c = "FAIL"; + (function() { + var f, g, h, i, j, k, l, m, n, o, p, q, r, s; + })(); + try { + throw 0; + } catch (t) { + (function() { + (function t() { + c = "PASS"; + })(); + })(); + (function e() { + try {} catch (t) {} + })(); + } + console.log(c); + } + expect: { + var a = 0, b, c = "FAIL"; + (function() { + var c, n, t, o, a, r, f, i, u, h, l, v, y, A; + })(); + try { + throw 0; + } catch (n) { + (function() { + (function n() { + c = "PASS"; + })(); + })(); + (function c() { + try {} catch (c) {} + })(); + } + console.log(c); + } + expect_stdout: "PASS" +} + +issue_3523_ie8: { + mangle = { + ie: true, + toplevel: false, + } + input: { + var a = 0, b, c = "FAIL"; + (function() { + var f, g, h, i, j, k, l, m, n, o, p, q, r, s; + })(); + try { + throw 0; + } catch (t) { + (function() { + (function t() { + c = "PASS"; + })(); + })(); + (function e() { + try {} catch (t) {} + })(); + } + console.log(c); + } + expect: { + var a = 0, b, c = "FAIL"; + (function() { + var c, t, n, o, a, r, f, i, u, h, e, l, v, y; + })(); + try { + throw 0; + } catch (t) { + (function() { + (function t() { + c = "PASS"; + })(); + })(); + (function e() { + try {} catch (t) {} + })(); + } + console.log(c); + } + expect_stdout: "PASS" +} + +issue_3523_toplevel: { + mangle = { + ie: false, + toplevel: true, + } + input: { + var a = 0, b, c = "FAIL"; + (function() { + var f, g, h, i, j, k, l, m, n, o, p, q, r, s; + })(); + try { + throw 0; + } catch (t) { + (function() { + (function t() { + c = "PASS"; + })(); + })(); + (function e() { + try {} catch (t) {} + })(); + } + console.log(c); + } + expect: { + var c = 0, n, t = "FAIL"; + (function() { + var c, n, t, o, r, a, f, i, u, h, l, v, y, A; + })(); + try { + throw 0; + } catch (c) { + (function() { + (function c() { + t = "PASS"; + })(); + })(); + (function c() { + try {} catch (c) {} + })(); + } + console.log(t); + } + expect_stdout: "PASS" +} + +issue_3523_ie8_toplevel: { + mangle = { + ie: true, + toplevel: true, + } + input: { + var a = 0, b, c = "FAIL"; + (function() { + var f, g, h, i, j, k, l, m, n, o, p, q, r, s; + })(); + try { + throw 0; + } catch (t) { + (function() { + (function t() { + c = "PASS"; + })(); + })(); + (function e() { + try {} catch (t) {} + })(); + } + console.log(c); + } + expect: { + var c = 0, n, t = "FAIL"; + (function() { + var c, n, t, o, r, a, f, i, u, h, l, v, y, A; + })(); + try { + throw 0; + } catch (o) { + (function() { + (function o() { + t = "PASS"; + })(); + })(); + (function r() { + try {} catch (o) {} + })(); + } + console.log(t); + } + expect_stdout: "PASS" +} + +issue_3523_rename: { + rename = true + mangle = { + ie: false, + toplevel: false, + } + input: { + var a = 0, b, c = "FAIL"; + (function() { + var d, e, f, g, h, i, j, k, l, m, o, p, q, r; + })(); + try { + throw 0; + } catch (e) { + (function() { + (function e() { + c = "PASS"; + })(); + })(); + (function d() { + try { + } catch (e) { + } + })(); + } + console.log(c); + } + expect: { + var a = 0, b, c = "FAIL"; + (function() { + var c, n, t, o, a, r, f, i, u, h, l, v, y, A; + })(); + try { + throw 0; + } catch (n) { + (function() { + (function n() { + c = "PASS"; + })(); + })(); + (function c() { + try {} catch (c) {} + })(); + } + console.log(c); + } + expect_stdout: "PASS" +} + +issue_3523_rename_ie8: { + rename = true + mangle = { + ie: true, + toplevel: false, + } + input: { + var a = 0, b, c = "FAIL"; + (function() { + var d, e, f, g, h, i, j, k, l, m, o, p, q, r; + })(); + try { + throw 0; + } catch (e) { + (function() { + (function e() { + c = "PASS"; + })(); + })(); + (function d() { + try { + } catch (e) { + } + })(); + } + console.log(c); + } + expect: { + var a = 0, b, c = "FAIL"; + (function() { + var c, n, t, o, a, r, e, f, i, u, h, l, v, y; + })(); + try { + throw 0; + } catch (e) { + (function() { + (function e() { + c = "PASS"; + })(); + })(); + (function d() { + try {} catch (e) {} + })(); + } + console.log(c); + } + expect_stdout: "PASS" +} + +issue_3523_rename_toplevel: { + rename = true + mangle = { + ie: false, + toplevel: true, + } + input: { + var a = 0, b, c = "FAIL"; + (function() { + var d, e, f, g, h, i, j, k, l, m, o, p, q, r; + })(); + try { + throw 0; + } catch (e) { + (function() { + (function e() { + c = "PASS"; + })(); + })(); + (function d() { + try { + } catch (e) { + } + })(); + } + console.log(c); + } + expect: { + var c = 0, n, t = "FAIL"; + (function() { + var c, n, t, o, r, a, f, i, u, h, l, v, y, A; + })(); + try { + throw 0; + } catch (c) { + (function() { + (function c() { + t = "PASS"; + })(); + })(); + (function c() { + try {} catch (c) {} + })(); + } + console.log(t); + } + expect_stdout: "PASS" +} + +issue_3523_rename_ie8_toplevel: { + rename = true + mangle = { + ie: true, + toplevel: true, + } + input: { + var a = 0, b, c = "FAIL"; + (function() { + var d, e, f, g, h, i, j, k, l, m, o, p, q, r; + })(); + try { + throw 0; + } catch (e) { + (function() { + (function e() { + c = "PASS"; + })(); + })(); + (function d() { + try { + } catch (e) { + } + })(); + } + console.log(c); + } + expect: { + var c = 0, n, t = "FAIL"; + (function() { + var c, n, t, o, r, a, f, i, u, h, l, v, y, A; + })(); + try { + throw 0; + } catch (o) { + (function() { + (function o() { + t = "PASS"; + })(); + })(); + (function r() { + try {} catch (o) {} + })(); + } + console.log(t); + } + expect_stdout: "PASS" +} + +issue_3542: { + options = { + ie: true, + reduce_vars: true, + toplevel: true, + unused: true, + } + input: { + var a = 0; + var b = a++; + var c = b && function a() {} || b; + console.log(a); + } + expect: { + var a = 0; + a++; + (function a() {}); + console.log(a); + } + expect_stdout: "1" +} + +issue_3703: { + options = { + ie: true, + reduce_vars: true, + toplevel: true, + unused: true, + } + input: { + var a = "PASS"; + function f() { + var b; + function g() { + a = "FAIL"; + } + var c = g; + function h() { + f; + } + a ? b |= c : b.p; + } + f(); + console.log(a); + } + expect: { + var a = "PASS"; + (function() { + var b; + var c = function() { + a = "FAIL"; + }; + a ? b |= c : b.p; + })(); + console.log(a); + } + expect_stdout: "PASS" +} + +issue_3750: { + options = { + evaluate: true, + ie: true, + } + input: { + (function(a) { + return function a() { + return a && console.log("PASS"); + }(); + })(); + } + expect: { + (function(a) { + return function a() { + return a && console.log("PASS"); + }(); + })(); + } + expect_stdout: "PASS" +} + +issue_3823: { + options = { + ie: true, + toplevel: true, + unused: true, + } + input: { + for (var i = 0; i < 1; i++) { + var a = a ? function f() { + f; + } : 0; + console.log("PASS", typeof f); + } + } + expect: { + for (var i = 0; i < 1; i++) { + (function f() { + f; + }); + console.log("PASS", typeof f); + } + } + expect_stdout: "PASS undefined" +} + +issue_3825: { + options = { + ie: true, + pure_getters: "strict", + side_effects: true, + } + input: { + console.log({}[void (0..length ? 1 : 2)]); + } + expect: { + console.log({}[void 0]); + } + expect_stdout: "undefined" +} + +issue_3889: { + options = { + evaluate: true, + ie: true, + reduce_vars: true, + } + input: { + function f(a) { + a = 0; + (function a() { + var a; + console.log(a); + })(); + } + f(); + } + expect: { + function f(a) { + a = 0; + (function a() { + var a; + console.log(a); + })(); + } + f(); + } + expect_stdout: "undefined" +} + +issue_3918: { + options = { + conditionals: true, + ie: true, + reduce_vars: true, + toplevel: true, + unused: true, + } + mangle = { + ie: true, + } + input: { + if (console.log("PASS")) { + var a = function f() { + f.p; + try { + console.log("FAIL"); + } catch (e) {} + }, b = a; + } + } + expect: { + var a; + console.log("PASS") && (a = function f() { + f.p; + try { + console.log("FAIL"); + } catch (o) {} + }, a); + } + expect_stdout: "PASS" +} + +issue_3999: { + rename = true + mangle = { + ie: true, + } + input: { + (function() { + (function f() { + for (var i = 0; i < 2; i++) + try { + f[0]; + } catch (f) { + var f = 0; + console.log(i); + } + })(); + })(typeof f); + } + expect: { + (function() { + (function f() { + for (var o = 0; o < 2; o++) + try { + f[0]; + } catch (f) { + var f = 0; + console.log(o); + } + })(); + })(typeof f); + } + expect_stdout: [ + "0", + "1", + ] +} + +issue_4001_1: { + options = { + collapse_vars: true, + ie: true, + inline: true, + reduce_vars: true, + sequences: true, + side_effects: false, + toplevel: true, + unused: true, + } + input: { + console.log(function(a) { + function f() { + return a; + var b; + } + var c = f(); + (function g() { + c[42]; + f; + })(); + (function a() {}); + }(42)); + } + expect: { + function f() { + return a; + } + var a; + console.log((a = 42, f()[42], void f, void function a() {})); + } + expect_stdout: "undefined" +} + +issue_4001_2: { + options = { + collapse_vars: true, + ie: true, + inline: true, + reduce_vars: true, + sequences: true, + side_effects: true, + toplevel: true, + unused: true, + } + input: { + console.log(function(a) { + function f() { + return a; + var b; + } + var c = f(); + (function g() { + c[42]; + f; + })(); + (function a() {}); + }(42)); + } + expect: { + function f() { + return a; + } + var a; + console.log((a = 42, void f()[42])); + } + expect_stdout: "undefined" +} + +issue_4015: { + rename = true + mangle = { + ie: true, + toplevel: true, + } + input: { + var n, a = 0, b; + function f() { + try { + throw 0; + } catch (b) { + (function g() { + (function b() { + a++; + })(); + })(); + } + } + f(); + console.log(a); + } + expect: { + var n, o = 0, c; + function t() { + try { + throw 0; + } catch (c) { + (function n() { + (function c() { + o++; + })(); + })(); + } + } + t(); + console.log(o); + } + expect_stdout: "1" +} + +issue_4019: { + options = { + reduce_vars: true, + toplevel: true, + unused: true, + } + mangle = { + ie: true, + toplevel: true, + } + input: { + var a = function() { + try { + console.log("FAIL"); + } catch (b) {} + }, a = (console.log(a.length), ++a); + } + expect: { + var o = function() { + try { + console.log("FAIL"); + } catch (o) {} + }; + console.log(o.length), + ++o; + } + expect_stdout: "0" +} + +issue_4028: { + options = { + reduce_vars: true, + toplevel: true, + unused: true, + } + mangle = { + ie: true, + } + input: { + function a() { + try { + A; + } catch (e) {} + } + var b = a += a; + console.log(typeof b); + } + expect: { + function a() { + try { + A; + } catch (a) {} + } + var b = a += a; + console.log(typeof b); + } + expect_stdout: "string" +} + +issue_2737: { + options = { + ie: true, + reduce_vars: true, + unused: true, + } + input: { + (function(a) { + a(); + })(function f() { + console.log(typeof f); + }); + } + expect: { + (function(a) { + a(); + })(function f() { + console.log(typeof f); + }); + } + expect_stdout: "function" +} + +single_use_catch_redefined: { + options = { + ie: true, + reduce_vars: true, + toplevel: true, + unused: true, + } + input: { + var a = 1; + try { + throw 2; + } catch (a) { + function g() { + return a; + } + } + console.log(g()); + } + expect: { + var a = 1; + try { + throw 2; + } catch (a) { + function g() { + return a; + } + } + console.log(g()); + } + expect_stdout: true +} + +single_use_inline_catch_redefined: { + options = { + ie: true, + inline: true, + reduce_vars: true, + toplevel: true, + unused: true, + } + input: { + var a = 1; + try { + throw 2; + } catch (a) { + function g() { + return a; + } + } + console.log(g()); + } + expect: { + var a = 1; + try { + throw 2; + } catch (a) { + function g() { + return a; + } + } + console.log(g()); + } + expect_stdout: true +} + +direct_inline_catch_redefined: { + options = { + ie: true, + inline: true, + reduce_vars: true, + toplevel: true, + } + input: { + var a = 1; + function f() { + return a; + } + try { + throw 2; + } catch (a) { + function g() { + return a; + } + console.log(a, f(), g()); + } + console.log(a, f(), g()); + } + expect: { + var a = 1; + function f() { + return a; + } + try { + throw 2; + } catch (a) { + function g() { + return a; + } + console.log(a, f(), g()); + } + console.log(a, a, g()); + } + expect_stdout: true +} + +issue_4186: { + options = { + dead_code: true, + evaluate: true, + ie: true, + reduce_vars: true, + toplevel: true, + unused: true, + } + mangle = { + ie: true, + toplevel: true, + } + input: { + function f() { + (function NaN() { + var a = 1; + while (a--) + try {} finally { + console.log(0/0); + var b; + } + })(f); + } + f(); + NaN; + } + expect: { + (function() { + (function NaN() { + var n = 1; + while (n--) + console.log(0/0); + })(); + })(); + NaN; + } + expect_stdout: "NaN" +} + +issue_4235: { + options = { + ie: true, + unused: true, + } + input: { + try {} catch (e) {} + console.log(function e() { + var e = 0; + }()); + } + expect: { + try {} catch (e) {} + console.log(function e() {}()); + } + expect_stdout: "undefined" +} + +issue_4250: { + options = { + ie: true, + loops: true, + unused: true, + } + input: { + console.log(function f() { + (function() { + for (f in "f"); + })(); + return f; + var f; + }()); + } + expect: { + console.log(function f() { + (function() { + for (f in "f"); + })(); + return f; + var f; + }()); + } + expect_stdout: "0" +} + +issue_4568: { + options = { + ie: true, + reduce_vars: true, + unused: true, + } + input: { + console.log(typeof f, function(a) { + return a.length; + }([ function f() {} ])); + } + expect: { + console.log(typeof f, function(a) { + return a.length; + }([ function f() {} ])); + } + expect_stdout: "undefined 1" +} + +issue_4729: { + options = { + ie: true, + pure_getters: true, + toplevel: true, + unused: true, + } + input: { + try { + f; + } catch (e) { + var a = a && a[function f() {}]; + console.log("PASS"); + } + } + expect: { + try { + f; + } catch (e) { + (function f() {}); + console.log("PASS"); + } + } + expect_stdout: "PASS" +} + +issue_4928_1: { + options = { + ie: true, + toplevel: true, + unused: true, + } + input: { + var a = function f() { + f(a); + }; + console.log(typeof f); + } + expect: { + var a = function f() { + f(a); + }; + console.log(typeof f); + } + expect_stdout: "undefined" +} + +issue_4928_2: { + options = { + ie: true, + toplevel: true, + unused: true, + } + input: { + switch (42) { + case console: + var a = function f() { + f(a); + }; + case 42: + var a = console.log("PASS"); + } + } + expect: { + switch (42) { + case console: + var a = function f() { + f(a); + }; + case 42: + a = console.log("PASS"); + } + } + expect_stdout: "PASS" +} + +issue_4958: { + options = { + collapse_vars: true, + ie: true, + } + input: { + console.log(function arguments(a) { + a = 21; + return arguments[0] + 21; + }("FAIL")); + } + expect: { + console.log(function arguments(a) { + a = 21; + return arguments[0] + 21; + }("FAIL")); + } + expect_stdout: "42" +} + +issue_5081_call: { + options = { + ie: false, + merge_vars: true, + unused: true, + } + input: { + function f(a) { + var b; + return a(b = "A") + (b += "SS"); + } + console.log(f(function() { + return "P"; + })); + } + expect: { + function f(a) { + // IE5-10: TypeError: Function expected + return a(a = "A") + (a += "SS"); + } + console.log(f(function() { + return "P"; + })); + } + expect_stdout: "PASS" +} + +issue_5081_call_ie: { + options = { + ie: true, + merge_vars: true, + unused: true, + } + input: { + function f(a) { + var b; + return a(b = "A") + (b += "SS"); + } + console.log(f(function() { + return "P"; + })); + } + expect: { + function f(a) { + var b; + return a(b = "A") + (b += "SS"); + } + console.log(f(function() { + return "P"; + })); + } + expect_stdout: "PASS" +} + +issue_5081_property_access: { + options = { + ie: false, + merge_vars: true, + unused: true, + } + input: { + function f(a) { + var b; + return a[b = "A"] + (b += "SS"); + } + console.log(f({ A: "P" })); + } + expect: { + function f(a) { + return a[a = "A"] + (a += "SS"); + } + // IE9-11: undefinedASS + console.log(f({ A: "P" })); + } + expect_stdout: "PASS" +} + +issue_5081_property_access_ie: { + options = { + ie: true, + merge_vars: true, + unused: true, + } + input: { + function f(a) { + var b; + return a[b = "A"] + (b += "SS"); + } + console.log(f({ A: "P" })); + } + expect: { + function f(a) { + var b; + return a[b = "A"] + (b += "SS"); + } + console.log(f({ A: "P" })); + } + expect_stdout: "PASS" +} + +issue_5269_1: { + options = { + ie: false, + inline: true, + toplevel: true, + } + input: { + "use strict"; + do { + (function() { + try { + throw "PASS"; + } catch (e) { + console.log(e); + } + })(); + } while (!console); + } + expect: { + "use strict"; + do { + try { + throw "PASS"; + } catch (e) { + console.log(e); + } + } while (!console); + } + expect_stdout: "PASS" +} + +issue_5269_1_ie: { + options = { + ie: true, + inline: true, + toplevel: true, + } + input: { + "use strict"; + do { + (function() { + try { + throw "PASS"; + } catch (e) { + console.log(e); + } + })(); + } while (!console); + } + expect: { + "use strict"; + do { + try { + throw "PASS"; + } catch (e) { + console.log(e); + } + } while (!console); + } + expect_stdout: "PASS" +} + +issue_5269_2: { + options = { + ie: false, + inline: true, + toplevel: true, + } + input: { + for (var i = 0; i < 2; i++) + (function() { + console.log(e); + try { + console; + } catch (e) { + var e = "FAIL 1"; + } + e = "FAIL 2"; + console; + })(); + } + expect: { + for (var i = 0; i < 2; i++) { + e = void 0; + console.log(e); + try { + console; + } catch (e) { + var e = "FAIL 1"; + } + e = "FAIL 2"; + console; + } + } + expect_stdout: [ + "undefined", + "undefined", + ] +} + +issue_5269_2_ie: { + options = { + ie: true, + inline: true, + toplevel: true, + } + input: { + for (var i = 0; i < 2; i++) + (function() { + console.log(e); + try { + console; + } catch (e) { + var e = "FAIL 1"; + } + e = "FAIL 2"; + console; + })(); + } + expect: { + for (var i = 0; i < 2; i++) { + e = void 0; + console.log(e); + try { + console; + } catch (e) { + var e = "FAIL 1"; + } + e = "FAIL 2"; + console; + } + } + expect_stdout: [ + "undefined", + "undefined", + ] +} + +issue_5269_3: { + options = { + ie: false, + inline: true, + toplevel: true, + } + input: { + e = "foo"; + for (var i = 0; i < 2; i++) + (function() { + console.log(e); + try { + console; + } catch (e) { + e = "FAIL"; + } + e = "bar"; + console; + })(); + } + expect: { + e = "foo"; + for (var i = 0; i < 2; i++) { + console.log(e); + try { + console; + } catch (e) { + e = "FAIL"; + } + e = "bar"; + console; + } + } + expect_stdout: [ + "foo", + "bar", + ] +} + +issue_5269_3_ie: { + options = { + ie: true, + inline: true, + toplevel: true, + } + input: { + e = "foo"; + for (var i = 0; i < 2; i++) + (function() { + console.log(e); + try { + console; + } catch (e) { + e = "FAIL"; + } + e = "bar"; + console; + })(); + } + expect: { + e = "foo"; + for (var i = 0; i < 2; i++) { + console.log(e); + try { + console; + } catch (e) { + e = "FAIL"; + } + e = "bar"; + console; + } + } + expect_stdout: [ + "foo", + "bar", + ] +} + +issue_5350: { + options = { + ie: false, + properties: true, + side_effects: true, + } + input: { + console.log(typeof f, [ 42, function f() {} ][0]); + } + expect: { + console.log(typeof f, 42); + } + expect_stdout: "undefined 42" +} + +issue_5350_ie: { + options = { + ie: true, + properties: true, + side_effects: true, + } + input: { + console.log(typeof f, [ 42, function f() {} ][0]); + } + expect: { + console.log(typeof f, (function f() {}, 42)); + } + expect_stdout: "undefined 42" +} diff --git a/test/compress/if_return.js b/test/compress/if_return.js new file mode 100644 index 00000000000..b8a6e2634c7 --- /dev/null +++ b/test/compress/if_return.js @@ -0,0 +1,2482 @@ +if_return_1: { + options = { + booleans: true, + comparisons: true, + conditionals: true, + dead_code: true, + evaluate: true, + if_return: true, + sequences: true, + side_effects: true, + unused: true, + } + input: { + function f(x) { + if (x) { + return true; + } + } + } + expect: { + function f(x){if(x)return!0} + } +} + +if_return_2: { + options = { + booleans: true, + comparisons: true, + conditionals: true, + dead_code: true, + evaluate: true, + if_return: true, + sequences: true, + side_effects: true, + unused: true, + } + input: { + function f(x, y) { + if (x) + return 3; + if (y) + return c(); + } + } + expect: { + function f(x,y){return x?3:y?c():void 0} + } +} + +if_return_3: { + options = { + booleans: true, + comparisons: true, + conditionals: true, + dead_code: true, + evaluate: true, + if_return: true, + sequences: true, + side_effects: true, + unused: true, + } + input: { + function f(x) { + a(); + if (x) { + b(); + return false; + } + } + } + expect: { + function f(x){if(a(),x)return b(),!1} + } +} + +if_return_4: { + options = { + booleans: true, + comparisons: true, + conditionals: true, + dead_code: true, + evaluate: true, + if_return: true, + sequences: true, + side_effects: true, + unused: true, + } + input: { + function f(x, y) { + a(); + if (x) return 3; + b(); + if (y) return c(); + } + } + expect: { + function f(x,y){return a(),x?3:(b(),y?c():void 0)} + } +} + +if_return_5: { + options = { + booleans: true, + comparisons: true, + conditionals: true, + dead_code: true, + evaluate: true, + if_return: true, + sequences: true, + side_effects: true, + unused: true, + } + input: { + function f() { + if (x) + return; + return 7; + if (y) + return j; + } + } + expect: { + function f(){if(!x)return 7} + } +} + +if_return_6: { + options = { + booleans: true, + comparisons: true, + conditionals: true, + dead_code: true, + evaluate: true, + if_return: true, + sequences: true, + side_effects: true, + unused: true, + } + input: { + function f(x) { + return x ? true : void 0; + return y; + } + } + expect: { + // suboptimal + function f(x){return!!x||void 0} + } +} + +if_return_7: { + options = { + booleans: true, + comparisons: true, + conditionals: true, + dead_code: true, + evaluate: true, + if_return: true, + sequences: true, + side_effects: true, + unused: true, + } + input: { + function f(x) { + if (x) { + return true; + } + foo(); + bar(); + } + } + expect: { + function f(x){if(x)return!0;foo(),bar()} + } +} + +if_return_8: { + options = { + conditionals: true, + if_return: true, + sequences: true, + side_effects: true, + } + input: { + function f(e) { + if (2 == e) return foo(); + if (3 == e) return bar(); + if (4 == e) return baz(); + fail(e); + } + + function g(e) { + if (a(e)) return foo(); + if (b(e)) return bar(); + if (c(e)) return baz(); + fail(e); + } + + function h(e) { + if (a(e)) return foo(); + else if (b(e)) return bar(); + else if (c(e)) return baz(); + else fail(e); + } + + function i(e) { + if (a(e)) return foo(); + else if (b(e)) return bar(); + else if (c(e)) return baz(); + fail(e); + } + } + expect: { + function f(e){return 2==e?foo():3==e?bar():4==e?baz():void fail(e)} + function g(e){return a(e)?foo():b(e)?bar():c(e)?baz():void fail(e)} + function h(e){return a(e)?foo():b(e)?bar():c(e)?baz():void fail(e)} + function i(e){return a(e)?foo():b(e)?bar():c(e)?baz():void fail(e)} + } +} + +if_return_9: { + options = { + booleans: true, + conditionals: true, + if_return: true, + sequences: true, + } + input: { + !function() { + if (console.log("foo")) + return 42; + var a = console.log("bar"); + }(); + } + expect: { + !function() { + var a; + return console.log("foo") || (a = console.log("bar"), void 0); + }(); + } + expect_stdout: [ + "foo", + "bar", + ] +} + +if_return_10: { + options = { + booleans: true, + conditionals: true, + if_return: true, + sequences: true, + } + input: { + !function() { + if (console.log("foo")) + return 42; + if (console.log("bar")) + return null; + var a = console.log("baz"); + }(); + } + expect: { + !function() { + var a; + return console.log("foo") || !console.log("bar") && (a = console.log("baz"), void 0); + }(); + } + expect_stdout: [ + "foo", + "bar", + "baz", + ] +} + +if_return_cond_void_1: { + options = { + conditionals: true, + if_return: true, + } + input: { + function f(a) { + if (a) + return console.log("foo") ? console.log("bar") : void 0; + } + f(); + f(42); + } + expect: { + function f(a) { + return a && console.log("foo") ? console.log("bar") : void 0; + } + f(); + f(42); + } + expect_stdout: "foo" +} + +if_return_cond_void_2: { + options = { + conditionals: true, + if_return: true, + } + input: { + function f(a) { + if (a) + return console.log("foo") ? void 0 : console.log("bar"); + } + f(); + f(42); + } + expect: { + function f(a) { + return !a || console.log("foo") ? void 0 : console.log("bar"); + } + f(); + f(42); + } + expect_stdout: [ + "foo", + "bar", + ] +} + +if_return_cond_void_3: { + options = { + if_return: true, + } + input: { + function f(a) { + if (a) + return console.log("foo") ? void console.log("bar") : void console.log("baz"); + } + f(); + f(42); + } + expect: { + function f(a) { + if (a) + return console.log("foo") ? void console.log("bar") : void console.log("baz"); + } + f(); + f(42); + } + expect_stdout: [ + "foo", + "baz", + ] +} + +issue_1089: { + options = { + booleans: true, + comparisons: true, + conditionals: true, + dead_code: true, + evaluate: true, + if_return: true, + sequences: true, + side_effects: true, + unused: true, + } + input: { + function x() { + var f = document.getElementById("fname"); + if (f.files[0].size > 12345) { + alert("alert"); + f.focus(); + return false; + } + } + } + expect: { + function x() { + var f = document.getElementById("fname"); + if (12345 < f.files[0].size) + return alert("alert"), f.focus(), !1; + } + } +} + +issue_1437: { + options = { + conditionals: false, + if_return: true, + sequences: true, + } + input: { + function x() { + if (a()) + return b(); + if (c()) + return d(); + else + e(); + f(); + } + } + expect: { + function x() { + if (a()) + return b(); + if (c()) + return d(); + else + e() + f(); + } + } +} + +issue_1437_conditionals: { + options = { + conditionals: true, + if_return: true, + sequences: true, + } + input: { + function x() { + if (a()) + return b(); + if (c()) + return d(); + else + e(); + f(); + } + } + expect: { + function x() { + return a() ? b() : c() ? d() : (e(), f(), void 0); + } + } +} + +issue_512: { + options = { + conditionals: true, + if_return: true, + sequences: true, + side_effects: true, + } + input: { + function a() { + if (b()) { + c(); + return; + } + throw e; + } + } + expect: { + function a() { + if (!b()) throw e; + c(); + } + } +} + +if_var_return_1: { + options = { + conditionals: true, + if_return: true, + join_vars: true, + sequences: true, + } + input: { + function f() { + var a; + return; + var b; + } + function g() { + var a; + if (u()) { + var b; + return v(); + var c; + } + var d; + if (w()) { + var e; + return x(); + var f; + } else { + var g; + y(); + var h; + } + var i; + z(); + var j; + } + } + expect: { + function f() { + var a, b; + } + function g() { + var a, b, c, d, e, f, g, h, i, j; + return u() ? v() : w() ? x() : (y(), z(), void 0); + } + } +} + +if_var_return_2: { + options = { + conditionals: true, + if_return: true, + sequences: true, + } + input: { + (function() { + var a = w(); + if (x()) + return y(); + z(); + })(); + } + expect: { + (function() { + var a = w(); + return x() ? y() : (z(), void 0); + })(); + } +} + +if_var_return_3: { + options = { + conditionals: true, + if_return: true, + sequences: true, + } + input: { + f(function() { + var a = w(); + if (x()) + return y(a); + z(); + }); + } + expect: { + f(function() { + var a = w(); + if (x()) + return y(a); + z(); + }); + } +} + +if_var_return_4: { + options = { + conditionals: true, + if_return: true, + sequences: true, + } + input: { + function f() { + if (u()) + return v(); + var a = w(); + if (x()) + return y(a); + z(); + } + } + expect: { + function f() { + var a; + return u() ? v() : (a = w(), x() ? y(a) : (z(), void 0)); + } + } +} + +if_var_return_5: { + options = { + conditionals: true, + if_return: true, + } + input: { + function f() { + if (w()) + return x(); + var a = y(); + return z(a); + } + } + expect: { + function f() { + var a; + return w() ? x() : (a = y(), z(a)); + } + } +} + +if_defns_return_1: { + options = { + conditionals: true, + if_return: true, + sequences: true, + side_effects: true, + } + input: { + function f() { + if (u()) + return v(); + function g() {} + if (w()) + return x(g); + var a = y(); + z(a); + } + } + expect: { + function f() { + var a; + return u() ? v() : w() ? x(g) : (a = y(), void z(a)); + function g() {} + } + } +} + +if_defns_return_2: { + options = { + conditionals: true, + if_return: true, + } + input: { + function f(a, b, c) { + if (v()) + return a(); + if (w()) + return b(); + if (x()) { + var d = c(); + return y(d); + } + return z(); + } + } + expect: { + function f(a, b, c) { + var d; + return v() ? a() : w() ? b() : x() ? (d = c(), y(d)) : z(); + } + } +} + +if_if_return_return: { + options = { + conditionals: true, + if_return: true, + } + input: { + function f(a, b) { + if (a) { + if (b) + return b; + return; + } + g(); + } + } + expect: { + function f(a, b) { + if (a) + return b || void 0; + g(); + } + } +} + +if_body_return_1: { + options = { + if_return: true, + } + input: { + var c = "PASS"; + function f(a, b) { + if (a) { + if (b) throw new Error(c); + return 42; + } + return true; + } + console.log(f(0, 0)); + console.log(f(0, 1)); + console.log(f(1, 0)); + try { + f(1, 1); + console.log("FAIL"); + } catch (e) { + console.log(e.message); + } + } + expect: { + var c = "PASS"; + function f(a, b) { + if (a) { + if (b) throw new Error(c); + return 42; + } + return true; + } + console.log(f(0, 0)); + console.log(f(0, 1)); + console.log(f(1, 0)); + try { + f(1, 1); + console.log("FAIL"); + } catch (e) { + console.log(e.message); + } + } + expect_stdout: [ + "true", + "true", + "42", + "PASS", + ] +} + +if_body_return_2: { + options = { + if_return: true, + } + input: { + var c = "PASS"; + function f(a, b) { + if (0 + a) { + if (b) throw new Error(c); + return 42; + } + return true; + } + console.log(f(0, 0)); + console.log(f(0, 1)); + console.log(f(1, 0)); + try { + f(1, 1); + console.log("FAIL"); + } catch (e) { + console.log(e.message); + } + } + expect: { + var c = "PASS"; + function f(a, b) { + if (0 + a) { + if (b) throw new Error(c); + return 42; + } + return true; + } + console.log(f(0, 0)); + console.log(f(0, 1)); + console.log(f(1, 0)); + try { + f(1, 1); + console.log("FAIL"); + } catch (e) { + console.log(e.message); + } + } + expect_stdout: [ + "true", + "true", + "42", + "PASS", + ] +} + +if_body_return_3: { + options = { + if_return: true, + } + input: { + var c = "PASS"; + function f(a, b) { + if (1 == a) { + if (b) throw new Error(c); + return 42; + } + return true; + } + console.log(f(0, 0)); + console.log(f(0, 1)); + console.log(f(1, 0)); + try { + f(1, 1); + console.log("FAIL"); + } catch (e) { + console.log(e.message); + } + } + expect: { + var c = "PASS"; + function f(a, b) { + if (1 != a) return true; + if (b) throw new Error(c); + return 42; + } + console.log(f(0, 0)); + console.log(f(0, 1)); + console.log(f(1, 0)); + try { + f(1, 1); + console.log("FAIL"); + } catch (e) { + console.log(e.message); + } + } + expect_stdout: [ + "true", + "true", + "42", + "PASS", + ] +} + +issue_3600_1: { + options = { + if_return: true, + inline: 3, + side_effects: true, + unused: true, + } + input: { + var c = 0; + (function() { + if ([ ][c++]); else return; + return void function() { + var b = --b, a = c = 42; + return c; + }(); + })(); + console.log(c); + } + expect: { + var c = 0; + (function() { + if ([][c++]) b = --b, c = 42; + var b; + })(); + console.log(c); + } + expect_stdout: "1" +} + +issue_3600_2: { + options = { + if_return: true, + inline: true, + side_effects: true, + unused: true, + } + input: { + var c = 0; + (function() { + if ([ ][c++]); else return; + return void function() { + var b = --b, a = c = 42; + return c; + }(); + })(); + console.log(c); + } + expect: { + var c = 0; + (function() { + if ([][c++]) + b = --b, + c = 42; + var b; + })(); + console.log(c); + } + expect_stdout: "1" +} + +iife_if_return_simple: { + options = { + conditionals: true, + if_return: true, + inline: true, + sequences: true, + side_effects: true, + } + input: { + (function() { + if (console) + return console.log("PASS"); + console.log("FAIL"); + })(); + } + expect: { + console ? console.log("PASS") : console.log("FAIL"); + } + expect_stdout: "PASS" +} + +nested_if_break: { + options = { + conditionals: true, + if_return: true, + side_effects: true, + } + input: { + for (var i = 0; i < 3; i++) + L1: if ("number" == typeof i) { + if (0 === i) break L1; + console.log(i); + } + } + expect: { + for (var i = 0; i < 3; i++) + L1: "number" == typeof i && 0 !== i && console.log(i); + } + expect_stdout: [ + "1", + "2", + ] +} + +nested_if_continue: { + options = { + conditionals: true, + if_return: true, + join_vars: true, + loops: true, + } + input: { + function f(n) { + var i = 0; + do { + if ("number" == typeof n) { + if (0 === n) { + console.log("even", i); + continue; + } + if (1 === n) { + console.log("odd", i); + continue; + } + i++; + } + } while (0 <= (n -= 2)); + } + f(37); + f(42); + } + expect: { + function f(n) { + for (var i = 0; + "number" == typeof n + && (0 === n + ? console.log("even", i) + : 1 === n + ? console.log("odd", i) + : i++), + 0 <= (n -= 2);); + } + f(37); + f(42); + } + expect_stdout: [ + "odd 18", + "even 21", + ] +} + +nested_if_return: { + options = { + conditionals: true, + if_return: true, + } + input: { + function f() { + if (A) { + if (B) + return B; + if (C) + return D; + if (E) + return F; + if (G) + return H; + if (I) { + if (J) + return K; + return; + } + if (L) { + if (M) + return; + return N; + } + } + } + } + expect: { + function f() { + if (A) + return B || (C ? D : E ? F : G ? H : I ? J ? K : void 0 : L && !M ? N : void 0); + } + } +} + +issue_866_1: { + options = { + conditionals: true, + if_return: true, + sequences: false, + }; + input: { + function f(a) { + if (a) + return ""; + console.log(a); + } + } + expect: { + function f(a) { + if (a) + return ""; + console.log(a); + } + } +} + +issue_866_2: { + options = { + conditionals: true, + if_return: true, + sequences: true, + } + input: { + (function() { + if (a) + if (b) + c; + else + return d; + })(); + } + expect: { + (function() { + if (a) { + if (!b) + return d; + c; + } + })(); + } +} + +identical_returns_1: { + options = { + conditionals: true, + if_return: true, + } + input: { + console.log(function() { + if (console.log("foo")) + return 42; + else + while (console.log("bar")); + return 42; + }()); + } + expect: { + console.log(function() { + if (!console.log("foo")) + while (console.log("bar")); + return 42; + }()); + } + expect_stdout: [ + "foo", + "bar", + "42", + ] +} + +identical_returns_2: { + options = { + conditionals: true, + if_return: true, + } + input: { + console.log(function() { + if (console.log("foo")) + while (console.log("FAIL")); + else + return "bar"; + return "bar"; + }()); + } + expect: { + console.log(function() { + if (console.log("foo")) + while (console.log("FAIL")); + return "bar"; + }()); + } + expect_stdout: [ + "foo", + "bar", + ] +} + +identical_returns_3: { + options = { + if_return: true, + } + input: { + function f(a) { + if (a) + return 42; + if (a) + return; + return 42; + } + if (f(console)) + console.log("PASS"); + } + expect: { + function f(a) { + if (a) + return 42; + if (a) + ; + else + return 42; + } + if (f(console)) + console.log("PASS"); + } + expect_stdout: "PASS" +} + +issue_4374: { + options = { + booleans: true, + conditionals: true, + if_return: true, + reduce_vars: true, + unused: true, + } + input: { + (function() { + console.log(f(console)); + function f(a) { + if (console) return 0; + if (a) return 1; + return 0; + } + })(); + } + expect: { + (function() { + console.log(function(a) { + return !console && a ? 1 : 0; + }(console)); + })(); + } + expect_stdout: "0" +} + +issue_5521: { + options = { + if_return: true, + } + input: { + console.log(function() { + if (console) + try { + return "FAIL"; + } finally { + return; + } + }()); + } + expect: { + console.log(function() { + if (console) + try { + return "FAIL"; + } finally { + return; + } + }()); + } + expect_stdout: "undefined" +} + +issue_5523: { + options = { + if_return: true, + } + input: { + console.log(function() { + if (console) + try { + FAIL; + } finally { + return; + } + }()); + } + expect: { + console.log(function() { + if (console) + try { + FAIL; + } finally { + return; + } + }()); + } + expect_stdout: "undefined" +} + +drop_catch: { + options = { + if_return: true, + } + input: { + function f() { + try { + throw 42; + } catch (e) { + return console.log("foo"), "bar"; + } finally { + console.log("baz"); + } + return "bar"; + } + console.log(f()); + } + expect: { + function f() { + try { + throw 42; + } catch (e) { + console.log("foo"); + } finally { + console.log("baz"); + } + return "bar"; + } + console.log(f()); + } + expect_stdout: [ + "foo", + "baz", + "bar", + ] +} + +retain_catch: { + options = { + if_return: true, + } + input: { + function f() { + try { + throw 42; + } catch (e) { + return console.log("foo"); + } finally { + console.log("bar"); + } + return console.log("foo"); + } + f(); + } + expect: { + function f() { + try { + throw 42; + } catch (e) { + return console.log("foo"); + } finally { + console.log("bar"); + } + return console.log("foo"); + } + f(); + } + expect_stdout: [ + "foo", + "bar", + ] +} + +retain_finally: { + options = { + if_return: true, + } + input: { + function f() { + try { + return console.log("foo"), FAIL; + } catch (e) { + return console.log("bar"), "FAIL"; + } finally { + return console.log("baz"), console.log("moo"); + } + return console.log("moo"); + } + console.log(f()); + } + expect: { + function f() { + try { + return console.log("foo"), FAIL; + } catch (e) { + return console.log("bar"), "FAIL"; + } finally { + return console.log("baz"), console.log("moo"); + } + return console.log("moo"); + } + console.log(f()); + } + expect_stdout: [ + "foo", + "bar", + "baz", + "moo", + "undefined", + ] +} + +drop_try: { + options = { + if_return: true, + } + input: { + function f() { + try { + return console.log("foo"), "bar"; + } finally { + console.log("baz"); + } + return "bar"; + } + console.log(f()); + } + expect: { + function f() { + try { + console.log("foo"); + } finally { + console.log("baz"); + } + return "bar"; + } + console.log(f()); + } + expect_stdout: [ + "foo", + "baz", + "bar", + ] +} + +retain_try: { + options = { + if_return: true, + } + input: { + function f() { + try { + return console.log("foo"); + } finally { + console.log("bar"); + } + return console.log("foo"); + } + f(); + } + expect: { + function f() { + try { + return console.log("foo"); + } finally { + console.log("bar"); + } + return console.log("foo"); + } + f(); + } + expect_stdout: [ + "foo", + "bar", + ] +} + +drop_try_catch: { + options = { + if_return: true, + } + input: { + function f(a) { + try { + if (a()) + return console.log("foo"), console.log("baz"); + } catch (e) { + return console.log("bar"), console.log("baz"); + } + return console.log("baz"); + } + f(function() { + return 42; + }); + f(function() {}); + f(); + } + expect: { + function f(a) { + try { + if (a()) + console.log("foo"); + } catch (e) { + console.log("bar"); + } + return console.log("baz"); + } + f(function() { + return 42; + }); + f(function() {}); + f(); + } + expect_stdout: [ + "foo", + "baz", + "baz", + "bar", + "baz", + ] +} + +empty_try: { + options = { + if_return: true, + reduce_vars: true, + unused: true, + } + input: { + console.log(function() { + return f; + function f() { + try {} finally {} + return "PASS"; + } + }()()); + } + expect: { + console.log(function() { + return function() { + try {} finally {} + return "PASS"; + }; + }()()); + } + expect_stdout: "PASS" +} + +sequence_void_1: { + options = { + if_return: true, + } + input: { + function f() { + { + if (console) + return console, void console.log("PASS"); + return; + } + } + f(); + } + expect: { + function f() { + if (console) + console, void console.log("PASS"); + } + f(); + } + expect_stdout: "PASS" +} + +sequence_void_2: { + options = { + if_return: true, + } + input: { + function f() { + { + if (console) + return console, void console.log("PASS"); + return; + } + FAIL; + } + f(); + } + expect: { + function f() { + if (console) + console, void console.log("PASS"); + else { + return; + FAIL; + } + } + f(); + } + expect_stdout: "PASS" +} + +tail_match: { + options = { + if_return: true, + } + input: { + function f(a) { + if (a) { + console.log("foo"); + return console.log("bar"); + } + while (console.log("baz")); + return console.log("moo"), console.log("bar"); + } + f(); + f(42); + } + expect: { + function f(a) { + if (a) + console.log("foo"); + else { + while (console.log("baz")); + console.log("moo"); + } + return console.log("bar"); + } + f(); + f(42); + } + expect_stdout: [ + "baz", + "moo", + "bar", + "foo", + "bar", + ] +} + +void_match: { + options = { + if_return: true, + } + input: { + function f(a) { + if (a) { + console.log("foo"); + return; + } + while (console.log("bar")); + return console.log("baz"), void console.log("moo"); + } + f(); + f(42); + } + expect: { + function f(a) { + if (a) + console.log("foo"); + else { + while (console.log("bar")); + console.log("baz"), + console.log("moo"); + } + } + f(); + f(42); + } + expect_stdout: [ + "bar", + "baz", + "moo", + "foo", + ] +} + +switch_break: { + options = { + conditionals: true, + if_return: true, + } + input: { + function f(a) { + switch (a) { + default: + if (console.log("foo")) + break; + while (console.log("bar")); + case 42: + if (console.log("baz")) + break; + while (console.log("moo")); + break; + case null: + if (console.log("moz")) + break; + } + } + f(); + f(42); + f(null); + } + expect: { + function f(a) { + switch (a) { + default: + if (console.log("foo")) + break; + while (console.log("bar")); + case 42: + if (!console.log("baz")) + while (console.log("moo")); + break; + case null: + console.log("moz"); + } + } + f(); + f(42); + f(null); + } + expect_stdout: [ + "foo", + "bar", + "baz", + "moo", + "baz", + "moo", + "moz", + ] +} + +switch_return_1: { + options = { + dead_code: true, + if_return: true, + } + input: { + function f(a) { + switch (a) { + case console.log("PASS"): + return; + break; + case 42: + FAIL; + } + } + f(); + } + expect: { + function f(a) { + switch (a) { + case console.log("PASS"): + return; + case 42: + FAIL; + } + } + f(); + } + expect_stdout: "PASS" +} + +switch_return_2: { + options = { + if_return: true, + } + input: { + function f(a) { + switch (a) { + case console.log("PASS"): + if (console) + return; + break; + case 42: + FAIL; + } + } + f(); + } + expect: { + function f(a) { + switch (a) { + case console.log("PASS"): + if (console); + break; + case 42: + FAIL; + } + } + f(); + } + expect_stdout: "PASS" +} + +switch_return_3: { + options = { + if_return: true, + side_effects: true, + } + input: { + function f(a) { + switch (a) { + case console.log("foo"): + if (console) + return void console.log("bar"); + break; + case 42: + FAIL; + } + } + f(); + } + expect: { + function f(a) { + switch (a) { + case console.log("foo"): + if (console) + console.log("bar"); + break; + case 42: + FAIL; + } + } + f(); + } + expect_stdout: [ + "foo", + "bar", + ] +} + +switch_return_4: { + options = { + conditionals: true, + if_return: true, + side_effects: true, + } + input: { + function f(a) { + switch (a) { + case console.log("foo"): + if (console) { + console.log("bar"); + return; + } + break; + case 42: + FAIL; + } + } + f(); + } + expect: { + function f(a) { + switch (a) { + case console.log("foo"): + console && console.log("bar"); + break; + case 42: + FAIL; + } + } + f(); + } + expect_stdout: [ + "foo", + "bar", + ] +} + +switch_return_5: { + options = { + dead_code: true, + if_return: true, + } + input: { + function f(a) { + switch (console.log("foo")) { + case console.log("bar"): + if (a) + return; + return; + break; + case null: + FAIL; + } + } + f(); + f(42); + } + expect: { + function f(a) { + switch (console.log("foo")) { + case console.log("bar"): + if (a); + return; + case null: + FAIL; + } + } + f(); + f(42); + } + expect_stdout: [ + "foo", + "bar", + "foo", + "bar", + ] +} + +merged_references_1: { + options = { + if_return: true, + reduce_vars: true, + unused: true, + } + input: { + var a, b = "PASS"; + console.log(function(c) { + if (c = b) + return a || c; + c = FAIL; + return a || c; + }()); + } + expect: { + var a, b = "PASS"; + console.log(function(c) { + if (c = b); + else + c = FAIL; + return a || c; + }()); + } + expect_stdout: "PASS" +} + +merged_references_2: { + options = { + if_return: true, + reduce_vars: true, + toplevel: true, + unused: true, + } + input: { + A = "PASS"; + var a; + console.log(function(b) { + if (a = b) + return console && a; + a = FAIL; + return console && a; + }(A)); + } + expect: { + A = "PASS"; + var a; + console.log(function(b) { + if (a = b); + else + a = FAIL; + return console && a; + }(A)); + } + expect_stdout: "PASS" +} + +issue_5583: { + options = { + conditionals: true, + if_return: true, + side_effects: true, + } + input: { + do { + switch (console) { + default: + if (!console.log("foo")) + continue; + break; + case console.log("bar"): + FAIL; + } + } while (console.log("baz")); + } + expect: { + do { + switch (console) { + default: + console.log("foo"); + break; + case console.log("bar"): + FAIL; + } + } while (console.log("baz")); + } + expect_stdout: [ + "bar", + "foo", + "baz", + ] +} + +issue_5584_1: { + options = { + conditionals: true, + if_return: true, + } + input: { + function f(a) { + switch (a) { + case 42: + if (!console.log("PASS")) + return; + return FAIL; + } + } + f(42); + } + expect: { + function f(a) { + switch (a) { + case 42: + if (console.log("PASS")) + return FAIL; + } + } + f(42); + } + expect_stdout: "PASS" +} + +issue_5584_2: { + options = { + if_return: true, + } + input: { + function f(a) { + switch (a) { + case console.log("PASS"): + if (console) + break; + return FAIL; + } + } + f(); + } + expect: { + function f(a) { + switch (a) { + case console.log("PASS"): + if (console) + break; + return FAIL; + } + } + f(); + } + expect_stdout: "PASS" +} + +issue_5584_3: { + options = { + if_return: true, + } + input: { + function f() { + switch (console.log("foo")) { + case console.log("bar"): + if (console) + break; + return; + } + console.log("baz"); + } + f(); + } + expect: { + function f() { + switch (console.log("foo")) { + case console.log("bar"): + if (console) + break; + return; + } + console.log("baz"); + } + f(); + } + expect_stdout: [ + "foo", + "bar", + "baz", + ] +} + +issue_5584_4: { + options = { + if_return: true, + } + input: { + function f(a) { + switch (console.log("foo")) { + case console.log("bar"): + if (a) + return; + break; + } + console.log("baz"); + } + f(); + } + expect: { + function f(a) { + switch (console.log("foo")) { + case console.log("bar"): + if (a) + return; + break; + } + console.log("baz"); + } + f(); + } + expect_stdout: [ + "foo", + "bar", + "baz", + ] +} + +issue_5586: { + options = { + if_return: true, + } + input: { + L: do { + switch (console.log("foo")) { + case console.log("bar"): + if (console) + break; + break L; + } + } while (console.log("baz")); + } + expect: { + L: do { + switch (console.log("foo")) { + case console.log("bar"): + if (console) + break; + break L; + } + } while (console.log("baz")); + } + expect_stdout: [ + "foo", + "bar", + "baz", + ] +} + +issue_5587_1: { + options = { + conditionals: true, + if_return: true, + } + input: { + function f(a) { + if (console) + return a ? void 0 : console.log("PASS"); + } + f(); + f(42); + } + expect: { + function f(a) { + return !console || a ? void 0 : console.log("PASS"); + } + f(); + f(42); + } + expect_stdout: "PASS" +} + +issue_5587_2: { + options = { + conditionals: true, + if_return: true, + } + input: { + function f(a) { + if (console) + return a ? console.log("PASS") : void 0; + } + f(); + f(42); + } + expect: { + function f(a) { + return console && a ? console.log("PASS") : void 0; + } + f(); + f(42); + } + expect_stdout: "PASS" +} + +issue_5589_1: { + options = { + if_return: true, + } + input: { + function f(a) { + switch (a) { + case 42: + if (!console.log("PASS")) + return; + return 0; + break; + case null: + FAIL; + } + } + f(42); + } + expect: { + function f(a) { + switch (a) { + case 42: + if (console.log("PASS")) + return 0; + break; + case null: + FAIL; + } + } + f(42); + } + expect_stdout: "PASS" +} + +issue_5589_2: { + options = { + if_return: true, + } + input: { + function f(a) { + switch (console.log("foo")) { + case console.log("bar"): + if (a) + return void console.log("baz"); + return; + } + } + f(); + f(42); + } + expect: { + function f(a) { + switch (console.log("foo")) { + case console.log("bar"): + if (a) + void console.log("baz"); + return; + } + } + f(); + f(42); + } + expect_stdout: [ + "foo", + "bar", + "foo", + "bar", + "baz", + ] +} + +issue_5589_3: { + options = { + if_return: true, + } + input: { + function f(a) { + do { + switch (console.log("foo")) { + case console.log("bar"): + if (a) + return void console.log("baz"); + continue; + } + } while (console.log("moo")); + } + f(); + f(42); + } + expect: { + function f(a) { + do { + switch (console.log("foo")) { + case console.log("bar"): + if (a) + return void console.log("baz"); + continue; + } + } while (console.log("moo")); + } + f(); + f(42); + } + expect_stdout: [ + "foo", + "bar", + "moo", + "foo", + "bar", + "baz", + ] +} + +issue_5592_1: { + options = { + if_return: true, + } + input: { + L: { + do { + switch (console.log("foo")) { + case console.log("bar"): + if (console) + break; + break L; + } + } while (console.log("baz")); + } + } + expect: { + L: do { + switch (console.log("foo")) { + case console.log("bar"): + if (console) + break; + break L; + } + } while (console.log("baz")); + } + expect_stdout: [ + "foo", + "bar", + "baz", + ] +} + +issue_5592_2: { + options = { + if_return: true, + } + input: { + L: { + do { + switch (console.log("foo")) { + case console.log("bar"): + if (!console) + break L; + break; + } + } while (console.log("baz")); + } + } + expect: { + L: do { + switch (console.log("foo")) { + case console.log("bar"): + if (console) + break; + break L; + } + } while (console.log("baz")); + } + expect_stdout: [ + "foo", + "bar", + "baz", + ] +} + +issue_5595: { + options = { + conditionals: true, + if_return: true, + } + input: { + function f(a) { + if (a) { + var b; + if (b++) + return "FAIL"; + } else + return "PASS"; + } + console.log(f()); + } + expect: { + function f(a) { + var b; + return a ? b++ ? "FAIL" : void 0 : "PASS"; + } + console.log(f()); + } + expect_stdout: "PASS" +} + +issue_5597: { + options = { + conditionals: true, + if_return: true, + unused: true, + } + input: { + function f(a) { + if (a) L: { + return; + var b; + } else + return "FAIL"; + } + console.log(f(42) || "PASS"); + } + expect: { + function f(a) { + if (!a) + return "FAIL"; + } + console.log(f(42) || "PASS"); + } + expect_stdout: "PASS" +} + +issue_5619_1: { + options = { + if_return: true, + } + input: { + console.log(function() { + if (console) + if (console) + return "PASS"; + var a = FAIL; + return "PASS"; + }()); + } + expect: { + console.log(function() { + if (console) + if (console) + return "PASS"; + var a = FAIL; + return "PASS"; + }()); + } + expect_stdout: "PASS" +} + +issue_5619_2: { + options = { + dead_code: true, + if_return: true, + loops: true, + } + input: { + console.log(function() { + if (console) + while (console) + return "PASS"; + var a = FAIL; + return "PASS"; + }()); + } + expect: { + console.log(function() { + if (console) { + if (console) + return "PASS"; + } + var a = FAIL; + return "PASS"; + }()); + } + expect_stdout: "PASS" +} + +issue_5649: { + options = { + if_return: true, + } + input: { + console.log(function() { + try { + throw new Error("FAIL"); + } catch (e) { + return "PASS"; + } + throw new Error("FAIL"); + }()); + } + expect: { + console.log(function() { + try { + throw new Error("FAIL"); + } catch (e) { + return "PASS"; + } + throw new Error("FAIL"); + }()); + } + expect_stdout: "PASS" +} + +issue_5688: { + options = { + conditionals: true, + if_return: true, + } + input: { + L: do { + switch (console) { + default: + if (console) + break; + if (FAIL_1) + ; + else + break L; + break; + case 42: + FAIL_2; + } + } while (console.log("PASS")); + } + expect: { + L: do { + switch (console) { + default: + if (console) + break; + if (FAIL_1) + break; + break L; + case 42: + FAIL_2; + } + } while (console.log("PASS")); + } + expect_stdout: "PASS" +} diff --git a/test/compress/imports.js b/test/compress/imports.js new file mode 100644 index 00000000000..b67ebbe8f7f --- /dev/null +++ b/test/compress/imports.js @@ -0,0 +1,298 @@ +nought: { + input: { + import "foo"; + } + expect_exact: 'import"foo";' +} + +default_only: { + input: { + import foo from "bar"; + } + expect_exact: 'import foo from"bar";' +} + +all_only: { + input: { + import * as foo from "bar"; + } + expect_exact: 'import*as foo from"bar";' +} + +keys_only: { + input: { + import { as as foo, bar, delete as baz } from "moo"; + } + expect_exact: 'import{as as foo,bar,delete as baz}from"moo";' +} + +default_all: { + input: { + import foo, * as bar from "baz"; + } + expect_exact: 'import foo,*as bar from"baz";' +} + +default_keys: { + input: { + import foo, { bar } from "baz"; + } + expect_exact: 'import foo,{bar}from"baz";' +} + +non_identifiers: { + beautify = { + quote_style: 3, + } + input: { + import { '42' as foo } from "bar"; + import { "foo" as bar } from 'baz'; + } + expect_exact: "import{'42'as foo}from\"bar\";import{foo as bar}from'baz';" +} + +dynamic: { + input: { + (async a => await import(a))("foo").then(bar); + } + expect_exact: '(async a=>await import(a))("foo").then(bar);' +} + +dynamic_nought: { + input: { + import(foo); + } + expect_exact: "import(foo);" +} + +import_meta_1: { + input: { + console.log(import.meta, import.meta.url); + } + expect_exact: "console.log(import.meta,import.meta.url);" +} + +import_meta_2: { + input: { + import.meta.url.split("/").forEach(function(part, index) { + console.log(index, part); + }); + } + expect_exact: 'import.meta.url.split("/").forEach(function(part,index){console.log(index,part)});' +} + +same_quotes: { + beautify = { + beautify: true, + quote_style: 3, + } + input: { + import 'foo'; + import "bar"; + } + expect_exact: [ + "import 'foo';", + "", + 'import "bar";', + ] +} + +drop_unused: { + options = { + imports: true, + toplevel: true, + unused: true, + } + input: { + import a, * as b from "foo"; + import { c } from "bar"; + import { d, _ as e } from "baz"; + console.log(d); + } + expect: { + import "foo"; + import "bar"; + import { d as d } from "baz"; + console.log(d); + } +} + +drop_side_effect_free: { + options = { + imports: true, + side_effects: true, + toplevel: true, + unused: true, + } + input: { + import foo from "bar"; + var a = foo; + } + expect: { + import "bar"; + } +} + +mangle: { + rename = false + mangle = { + toplevel: true, + } + input: { + import foo, { bar } from "baz"; + console.log(moo); + import * as moo from "moz"; + } + expect: { + import o, { bar as m } from "baz"; + console.log(r); + import * as r from "moz"; + } +} + +rename_mangle: { + rename = true + mangle = { + toplevel: true, + } + input: { + import foo, { bar } from "baz"; + console.log(moo); + import * as moo from "moz"; + } + expect: { + import o, { bar as m } from "baz"; + console.log(r); + import * as r from "moz"; + } +} + +mangle_export_import: { + mangle = { + toplevel: true, + } + input: { + export let o = A; + import { p as A } from "foo"; + } + expect: { + export let o = p; + import { p } from "foo"; + } +} + +mangle_import_export: { + mangle = { + toplevel: true, + } + input: { + import { p as A } from "foo"; + export let o = A; + } + expect: { + import { p } from "foo"; + export let o = p; + } +} + +keep_ref: { + options = { + reduce_vars: true, + toplevel: true, + unused: true, + } + input: { + import foo from "bar"; + foo(); + } + expect: { + import foo from "bar"; + foo(); + } +} + +forbid_merge: { + options = { + merge_vars: true, + toplevel: true, + } + input: { + import A from "foo"; + export default class extends A {} + var f = () => () => {}; + f(); + f(); + } + expect: { + import A from "foo"; + export default class extends A {} + var f = () => () => {}; + f(); + f(); + } +} + +issue_4708_1: { + options = { + imports: true, + toplevel: true, + unused: true, + } + input: { + var a; + import a from "foo"; + } + expect: { + var a; + import a from "foo"; + } +} + +issue_4708_2: { + options = { + imports: true, + reduce_vars: true, + toplevel: true, + unused: true, + } + input: { + var a; + console.log(a); + import a from "foo"; + } + expect: { + var a; + console.log(a); + import a from "foo"; + } +} + +pr_5550_1: { + input: { + if (console) + import("foo"); + else + import.meta.url.replace(/bar/g, console.log); + } + expect: { + if (console) + import("foo"); + else + import.meta.url.replace(/bar/g, console.log); + } +} + +pr_5550_2: { + input: { + L: { + import("foo"); + import.meta.url.replace(/bar/g, console.log); + } + } + expect: { + L: { + import("foo"); + import.meta.url.replace(/bar/g, console.log); + } + } +} diff --git a/test/compress/indentation.js b/test/compress/indentation.js new file mode 100644 index 00000000000..9eff2d7b903 --- /dev/null +++ b/test/compress/indentation.js @@ -0,0 +1,100 @@ +numeric: { + beautify = { + beautify: true, + indent_start: 1, + indent_level: 3, + } + input: { + switch (42) { + case null: + console.log("FAIL"); + } + console.log("PASS"); + } + expect_exact: [ + " switch (42) {", + " case null:", + ' console.log("FAIL");', + " }", + "", + ' console.log("PASS");', + ] + expect_stdout: "PASS" +} + +spaces: { + beautify = { + beautify: true, + indent_start: " ", + indent_level: " ", + } + input: { + switch (42) { + case null: + console.log("FAIL"); + } + console.log("PASS"); + } + expect_exact: [ + " switch (42) {", + " case null:", + ' console.log("FAIL");', + " }", + "", + ' console.log("PASS");', + ] + expect_stdout: "PASS" +} + +tabs: { + beautify = { + beautify: true, + indent_start: "\t", + indent_level: "\t", + } + input: { + switch (42) { + case null: + console.log("FAIL"); + } + console.log("PASS"); + } + expect_exact: [ + "\tswitch (42) {", + "\tcase null:", + '\t\tconsole.log("FAIL");', + "\t}", + "", + '\tconsole.log("PASS");', + ] + expect_stdout: "PASS" +} + +mixed: { + beautify = { + beautify: true, + indent_start: "\n", + indent_level: " \t", + } + input: { + switch (42) { + case null: + console.log("FAIL"); + } + console.log("PASS"); + } + expect_exact: [ + "", + "switch (42) {", + "", + " case null:", + "", + ' \tconsole.log("FAIL");', + "", + "}", + "", + "", + 'console.log("PASS");', + ] + expect_stdout: "PASS" +} diff --git a/test/compress/issue-1034.js b/test/compress/issue-1034.js new file mode 100644 index 00000000000..e1abad7c6ba --- /dev/null +++ b/test/compress/issue-1034.js @@ -0,0 +1,298 @@ +non_hoisted_function_after_return: { + options = { + booleans: true, + comparisons: true, + conditionals: true, + dead_code: true, + evaluate: true, + hoist_funs: false, + if_return: true, + join_vars: true, + keep_fargs: true, + loops: true, + side_effects: true, + unused: true, + } + input: { + function foo(x) { + if (x) { + return bar(); + not_called1(); + } else { + return baz(); + not_called2(); + } + function bar() { return 7; } + return not_reached; + function UnusedFunction() {} + function baz() { return 8; } + } + } + expect: { + function foo(x) { + return (x ? bar : baz)(); + function bar() { return 7 } + function baz() { return 8 } + } + } + expect_warnings: [ + "WARN: Dropping unreachable code [test/compress/issue-1034.js:4,16]", + "WARN: Dropping unreachable code [test/compress/issue-1034.js:7,16]", + "WARN: Dropping unreachable code [test/compress/issue-1034.js:10,12]", + "WARN: Dropping unused function UnusedFunction [test/compress/issue-1034.js:11,21]", + ] +} + +non_hoisted_function_after_return_2a: { + options = { + booleans: true, + collapse_vars: false, + comparisons: true, + conditionals: true, + dead_code: true, + evaluate: true, + hoist_funs: false, + if_return: true, + join_vars: true, + keep_fargs: true, + loops: true, + passes: 2, + side_effects: true, + unused: true, + } + input: { + function foo(x) { + if (x) { + return bar(1); + var a = not_called(1); + } else { + return bar(2); + var b = not_called(2); + } + var c = bar(3); + function bar(x) { return 7 - x; } + function nope() {} + return b || c; + } + } + expect: { + function foo(x) { + return bar(x ? 1 : 2); + function bar(x) { + return 7 - x; + } + } + } + expect_warnings: [ + "WARN: Dropping initialization in unreachable code [test/compress/issue-1034.js:4,16]", + "WARN: Dropping initialization in unreachable code [test/compress/issue-1034.js:7,16]", + "WARN: Dropping unused variable a [test/compress/issue-1034.js:4,20]", + "WARN: Dropping unused function nope [test/compress/issue-1034.js:11,21]", + "INFO: pass 0: last_count: Infinity, count: 35", + "WARN: Dropping initialization in unreachable code [test/compress/issue-1034.js:9,12]", + "WARN: Dropping unreachable code [test/compress/issue-1034.js:12,12]", + "INFO: Dropping unused variable b [test/compress/issue-1034.js:7,20]", + "INFO: Dropping unused variable c [test/compress/issue-1034.js:9,16]", + "INFO: pass 1: last_count: 35, count: 18", + ] +} + +non_hoisted_function_after_return_2b: { + options = { + booleans: true, + collapse_vars: false, + comparisons: true, + conditionals: true, + dead_code: true, + evaluate: true, + hoist_funs: false, + if_return: true, + join_vars: true, + keep_fargs: true, + loops: true, + side_effects: true, + unused: true, + } + input: { + function foo(x) { + if (x) { + return bar(1); + } else { + return bar(2); + var b; + } + var c = bar(3); + function bar(x) { + return 7 - x; + } + return b || c; + } + } + expect: { + function foo(x) { + return bar(x ? 1 : 2); + function bar(x) { return 7 - x; } + } + } + expect_warnings: [ + "WARN: Dropping initialization in unreachable code [test/compress/issue-1034.js:8,12]", + "WARN: Dropping unreachable code [test/compress/issue-1034.js:12,12]", + ] +} + +non_hoisted_function_after_return_strict: { + options = { + booleans: true, + comparisons: true, + conditionals: true, + dead_code: true, + evaluate: true, + hoist_funs: false, + if_return: true, + join_vars: true, + keep_fargs: true, + loops: true, + side_effects: true, + unused: true, + } + input: { + "use strict"; + function foo(x) { + if (x) { + return bar(); + not_called1(); + } else { + return baz(); + not_called2(); + } + function bar() { return 7; } + return not_reached; + function UnusedFunction() {} + function baz() { return 8; } + } + console.log(foo(0), foo(1)); + } + expect: { + "use strict"; + function foo(x) { + return (x ? bar : baz)(); + function bar() { return 7 } + function baz() { return 8 } + } + console.log(foo(0), foo(1)); + } + expect_stdout: "8 7" + expect_warnings: [ + "WARN: Dropping unreachable code [test/compress/issue-1034.js:5,16]", + "WARN: Dropping unreachable code [test/compress/issue-1034.js:8,16]", + "WARN: Dropping unreachable code [test/compress/issue-1034.js:11,12]", + "WARN: Dropping unused function UnusedFunction [test/compress/issue-1034.js:12,21]", + ] +} + +non_hoisted_function_after_return_2a_strict: { + options = { + booleans: true, + collapse_vars: false, + comparisons: true, + conditionals: true, + dead_code: true, + evaluate: true, + hoist_funs: false, + if_return: true, + join_vars: true, + keep_fargs: true, + loops: true, + passes: 2, + side_effects: true, + unused: true, + } + input: { + "use strict"; + function foo(x) { + if (x) { + return bar(1); + var a = not_called(1); + } else { + return bar(2); + var b = not_called(2); + } + var c = bar(3); + function bar(x) { return 7 - x; } + function nope() {} + return b || c; + } + console.log(foo(0), foo(1)); + } + expect: { + "use strict"; + function foo(x) { + return bar(x ? 1 : 2); + function bar(x) { + return 7 - x; + } + } + console.log(foo(0), foo(1)); + } + expect_stdout: "5 6" + expect_warnings: [ + "WARN: Dropping initialization in unreachable code [test/compress/issue-1034.js:5,16]", + "WARN: Dropping initialization in unreachable code [test/compress/issue-1034.js:8,16]", + "WARN: Dropping unused variable a [test/compress/issue-1034.js:5,20]", + "WARN: Dropping unused function nope [test/compress/issue-1034.js:12,21]", + "INFO: pass 0: last_count: Infinity, count: 46", + "WARN: Dropping initialization in unreachable code [test/compress/issue-1034.js:10,12]", + "WARN: Dropping unreachable code [test/compress/issue-1034.js:13,12]", + "INFO: Dropping unused variable b [test/compress/issue-1034.js:8,20]", + "INFO: Dropping unused variable c [test/compress/issue-1034.js:10,16]", + "INFO: pass 1: last_count: 46, count: 29", + ] +} + +non_hoisted_function_after_return_2b_strict: { + options = { + booleans: true, + collapse_vars: false, + comparisons: true, + conditionals: true, + dead_code: true, + evaluate: true, + hoist_funs: false, + if_return: true, + join_vars: true, + keep_fargs: true, + loops: true, + side_effects: true, + unused: true, + } + input: { + "use strict"; + function foo(x) { + if (x) { + return bar(1); + } else { + return bar(2); + var b; + } + var c = bar(3); + function bar(x) { + return 7 - x; + } + return b || c; + } + console.log(foo(0), foo(1)); + } + expect: { + "use strict"; + function foo(x) { + return bar(x ? 1 : 2); + function bar(x) { return 7 - x; } + } + console.log(foo(0), foo(1)); + } + expect_stdout: "5 6" + expect_warnings: [ + "WARN: Dropping initialization in unreachable code [test/compress/issue-1034.js:9,12]", + "WARN: Dropping unreachable code [test/compress/issue-1034.js:13,12]", + ] +} diff --git a/test/compress/issue-1041.js b/test/compress/issue-1041.js new file mode 100644 index 00000000000..5c8be9e3ee7 --- /dev/null +++ b/test/compress/issue-1041.js @@ -0,0 +1,30 @@ +const_pragma: { + options = { + evaluate: true, + reduce_funcs: true, + reduce_vars: true, + } + + input: { + /** @const */ var goog = goog || {}; + } + expect: { + var goog = goog || {}; + } +} + +// for completeness' sake +not_const: { + options = { + evaluate: true, + reduce_funcs: true, + reduce_vars: true, + } + + input: { + var goog = goog || {}; + } + expect: { + var goog = goog || {}; + } +} diff --git a/test/compress/issue-105.js b/test/compress/issue-105.js deleted file mode 100644 index ca17adbfd2d..00000000000 --- a/test/compress/issue-105.js +++ /dev/null @@ -1,25 +0,0 @@ -typeof_eq_undefined: { - options = { - comparisons: true - }; - input: { a = typeof b.c != "undefined" } - expect: { a = "undefined" != typeof b.c } -} - -typeof_eq_undefined_unsafe: { - options = { - comparisons: true, - unsafe: true - }; - input: { a = typeof b.c != "undefined" } - expect: { a = void 0 !== b.c } -} - -typeof_eq_undefined_unsafe2: { - options = { - comparisons: true, - unsafe: true - }; - input: { a = "undefined" != typeof b.c } - expect: { a = void 0 !== b.c } -} diff --git a/test/compress/issue-1052.js b/test/compress/issue-1052.js new file mode 100644 index 00000000000..8861f7fe808 --- /dev/null +++ b/test/compress/issue-1052.js @@ -0,0 +1,169 @@ +multiple_functions: { + options = { + hoist_funs: false, + if_return: true, + } + input: { + (function() { + if (!window) + return; + function f() {} + function g() {} + })(); + } + expect: { + (function() { + // NOTE: other compression steps will reduce this + // down to just `window`. + if (!window); + function f() {} + function g() {} + })(); + } +} + +single_function: { + options = { + hoist_funs: false, + if_return: true, + } + input: { + (function() { + if (!window) + return; + function f() {} + })(); + } + expect: { + (function() { + if (!window); + function f() {} + })(); + } +} + +deeply_nested: { + options = { + hoist_funs: false, + if_return: true, + } + input: { + (function() { + if (!window) + return; + function f() {} + function g() {} + if (!document) + return; + function h() {} + })(); + } + expect: { + (function() { + // NOTE: other compression steps will reduce this + // down to just `window`. + if (!window); + else if (!document); + function f() {} + function g() {} + function h() {} + })(); + } +} + +not_hoisted_when_already_nested: { + options = { + hoist_funs: false, + if_return: true, + } + input: { + (function() { + if (!window) + return; + if (foo) function f() {} + })(); + } + expect: { + (function() { + if (!window); + else if (foo) + function f() {} + })(); + } +} + +defun_if_return: { + options = { + hoist_funs: false, + if_return: true, + } + input: { + function e() { + function f() {} + if (!window) + return; + else + function g() {} + function h() {} + } + } + expect: { + function e() { + function f() {} + if (!window); + else + function g() {} + function h() {} + } + } +} + +defun_hoist_funs: { + options = { + hoist_funs: true, + if_return: true, + } + input: { + function e() { + function f() {} + if (!window) + return; + else + function g() {} + function h() {} + } + } + expect: { + function e() { + function f() {} + function g() {} + function h() {} + if (!window); + } + } +} + +defun_else_if_return: { + options = { + hoist_funs: false, + if_return: true, + } + input: { + function e() { + function f() {} + if (window) + function g() {} + else + return; + function h() {} + } + } + expect: { + function e() { + function f() {} + if (window) + function g() {} + function h() {} + } + } +} diff --git a/test/compress/issue-1105.js b/test/compress/issue-1105.js new file mode 100644 index 00000000000..fee6540a062 --- /dev/null +++ b/test/compress/issue-1105.js @@ -0,0 +1,309 @@ +with_in_global_scope: { + options = { + unused: true, + } + input: { + var o = 42; + with(o) { + var foo = 'something' + } + doSomething(o); + } + expect: { + var o=42; + with(o) + var foo = "something"; + doSomething(o); + } +} +with_in_function_scope: { + options = { + unused: true, + } + input: { + function foo() { + var o = 42; + with(o) { + var foo = "something" + } + doSomething(o); + } + } + expect: { + function foo() { + var o=42; + with(o) + var foo = "something"; + doSomething(o) + } + } +} +compress_with_with_in_other_scope: { + options = { + unused: true, + } + input: { + function foo() { + var o = 42; + with(o) { + var foo = "something" + } + doSomething(o); + } + function bar() { + var unused = 42; + return something(); + } + } + expect: { + function foo() { + var o = 42; + with(o) + var foo = "something"; + doSomething(o) + } + function bar() { + return something() + } + } +} +with_using_existing_variable_outside_scope: { + options = { + unused: true, + } + input: { + function f() { + var o = {}; + var unused = {}; // Doesn't get removed because upper scope uses with + function foo() { + with(o) { + var foo = "something" + } + doSomething(o); + } + foo() + } + } + expect: { + function f() { + var o = {}; + var unused = {}; + function foo() { + with(o) + var foo = "something"; + doSomething(o) + } + foo() + } + } +} +check_drop_unused_in_peer_function: { + options = { + unused: true, + } + input: { + function outer() { + var o = {}; + var unused = {}; // should be kept + function foo() { // should be kept + function not_in_use() { + var nested_unused = "foo"; // should be dropped + return 24; + } + var unused = {}; // should be kept + with (o) { + var foo = "something"; + } + doSomething(o); + } + function bar() { + var unused = {}; // should be dropped + doSomethingElse(); + } + foo(); + bar(); + } + } + expect: { + function outer() { + var o = {}; + var unused = {}; // should be kept + function foo() { // should be kept + function not_in_use() { + return 24; + } + var unused = {}; // should be kept + with (o) + var foo = "something"; + doSomething(o); + } + function bar() { + doSomethingElse(); + } + foo(); + bar(); + } + } +} + +Infinity_not_in_with_scope: { + options = { + unused: true, + } + input: { + var o = { Infinity: "FAIL" }; + var vInfinity = "Infinity"; + vInfinity = Infinity; + console.log(vInfinity); + } + expect: { + var o = { Infinity: "FAIL" }; + var vInfinity = "Infinity"; + vInfinity = 1/0; + console.log(vInfinity); + } + expect_stdout: "Infinity" +} + +Infinity_in_with_scope: { + options = { + unused: true, + } + input: { + var o = { Infinity: "PASS" }; + var vInfinity = "Infinity"; + with (o) { vInfinity = Infinity; } + console.log(vInfinity); + } + expect: { + var o = { Infinity: "PASS" }; + var vInfinity = "Infinity"; + with (o) vInfinity = Infinity; + console.log(vInfinity); + } + expect_stdout: "PASS" +} + +assorted_Infinity_NaN_undefined_in_with_scope: { + options = { + booleans: true, + comparisons: true, + conditionals: true, + dead_code: true, + evaluate: true, + hoist_funs: true, + if_return: true, + join_vars: true, + keep_fargs: true, + keep_infinity: false, + sequences: false, + side_effects: true, + unused: true, + } + input: { + var f = console.log; + var o = { + undefined : 3, + NaN : 4, + Infinity : 5, + }; + if (o) { + f(undefined, void 0); + f(NaN, 0/0); + f(Infinity, 1/0); + f(-Infinity, -(1/0)); + f(2 + 7 + undefined, 2 + 7 + void 0); + } + with (o) { + f(undefined, void 0); + f(NaN, 0/0); + f(Infinity, 1/0); + f(-Infinity, -(1/0)); + f(2 + 7 + undefined, 2 + 7 + void 0); + } + } + expect: { + var f = console.log, o = { + undefined : 3, + NaN : 4, + Infinity : 5 + }; + if (o) { + f(void 0, void 0); + f(NaN, NaN); + f(1/0, 1/0); + f(-1/0, -1/0); + f(NaN, NaN); + } + with (o) { + f(undefined, void 0); + f(NaN, 0/0); + f(Infinity, 1/0); + f(-Infinity, -1/0); + f(9 + undefined, 9 + void 0); + } + } + expect_stdout: true +} + +assorted_Infinity_NaN_undefined_in_with_scope_keep_infinity: { + options = { + booleans: true, + comparisons: true, + conditionals: true, + dead_code: true, + evaluate: true, + hoist_funs: true, + if_return: true, + join_vars: true, + keep_fargs: true, + keep_infinity: true, + sequences: false, + side_effects: true, + unused: true, + } + input: { + var f = console.log; + var o = { + undefined : 3, + NaN : 4, + Infinity : 5, + }; + if (o) { + f(undefined, void 0); + f(NaN, 0/0); + f(Infinity, 1/0); + f(-Infinity, -(1/0)); + f(2 + 7 + undefined, 2 + 7 + void 0); + } + with (o) { + f(undefined, void 0); + f(NaN, 0/0); + f(Infinity, 1/0); + f(-Infinity, -(1/0)); + f(2 + 7 + undefined, 2 + 7 + void 0); + } + } + expect: { + var f = console.log, o = { + undefined : 3, + NaN : 4, + Infinity : 5 + }; + if (o) { + f(void 0, void 0); + f(NaN, NaN); + f(Infinity, 1/0); + f(-Infinity, -1/0); + f(NaN, NaN); + } + with (o) { + f(undefined, void 0); + f(NaN, 0/0); + f(Infinity, 1/0); + f(-Infinity, -1/0); + f(9 + undefined, 9 + void 0); + } + } + expect_stdout: true +} diff --git a/test/compress/issue-12.js b/test/compress/issue-12.js index bf87d5c0150..e2bd384c7c3 100644 --- a/test/compress/issue-12.js +++ b/test/compress/issue-12.js @@ -1,11 +1,78 @@ keep_name_of_getter: { - options = { unused: true }; - input: { a = { get foo () {} } } - expect: { a = { get foo () {} } } + options = { + unused: true, + } + input: { + a = { + get foo() {}, + }; + } + expect: { + a = { + get foo() {}, + }; + } } keep_name_of_setter: { - options = { unused: true }; - input: { a = { set foo () {} } } - expect: { a = { set foo () {} } } + options = { + unused: true, + } + input: { + a = { + set foo(v) {}, + }; + } + expect: { + a = { + set foo(v) {}, + }; + } +} + +setter_with_operator_keys: { + input: { + var tokenCodes = { + get instanceof() { + return test0; + }, + set instanceof(value) { + test0 = value; + }, + set typeof(value) { + test1 = value; + }, + get typeof() { + return test1; + }, + set else(value) { + test2 = value; + }, + get else() { + return test2; + }, + }; + } + expect: { + var tokenCodes = { + get instanceof() { + return test0; + }, + set instanceof(value) { + test0 = value; + }, + set typeof(value) { + test1 = value; + }, + get typeof() { + return test1; + }, + set else(value) { + test2 = value; + }, + get else() { + return test2; + }, + }; + } } diff --git a/test/compress/issue-1202.js b/test/compress/issue-1202.js new file mode 100644 index 00000000000..1ad6be7b563 --- /dev/null +++ b/test/compress/issue-1202.js @@ -0,0 +1,51 @@ +mangle_keep_fnames_false: { + options = { + keep_fargs: true, + keep_fnames: true, + } + mangle = { + keep_fnames: false, + } + input: { + "use strict"; + function total() { + return function n(a, b, c) { + return a + b + c; + }; + } + } + expect: { + "use strict"; + function total() { + return function t(n, r, u) { + return n + r + u; + }; + } + } +} + +mangle_keep_fnames_true: { + options = { + keep_fargs: true, + keep_fnames: true, + } + mangle = { + keep_fnames: true, + } + input: { + "use strict"; + function total() { + return function n(a, b, c) { + return a + b + c; + }; + } + } + expect: { + "use strict"; + function total() { + return function n(t, r, u) { + return t + r + u; + }; + } + } +} diff --git a/test/compress/issue-1261.js b/test/compress/issue-1261.js new file mode 100644 index 00000000000..c6a2621c7d3 --- /dev/null +++ b/test/compress/issue-1261.js @@ -0,0 +1,186 @@ +pure_function_calls: { + options = { + annotations: true, + booleans: true, + comparisons: true, + conditionals: true, + evaluate: true, + if_return: true, + join_vars: true, + negate_iife: true, + side_effects: true, + unused: true, + } + input: { + // pure top-level IIFE will be dropped + // @__PURE__ - comment + (function() { + console.log("iife0"); + })(); + + // pure top-level IIFE assigned to unreferenced var will not be dropped + var iife1 = /*@__PURE__*/(function() { + console.log("iife1"); + function iife1() {} + return iife1; + })(); + + (function(){ + // pure IIFE in function scope assigned to unreferenced var will be dropped + var iife2 = /*#__PURE__*/(function() { + console.log("iife2"); + function iife2() {} + return iife2; + })(); + })(); + + // comment #__PURE__ comment + bar(), baz(), quux(); + a.b(), /* @__PURE__ */ c.d.e(), f.g(); + } + expect: { + var iife1 = function() { + console.log("iife1"); + function iife1() {} + return iife1; + }(); + + baz(), quux(); + a.b(), f.g(); + } + expect_warnings: [ + "WARN: Dropping __PURE__ call [test/compress/issue-1261.js:3,8]", + "WARN: Dropping side-effect-free statement [test/compress/issue-1261.js:3,8]", + "WARN: Dropping __PURE__ call [test/compress/issue-1261.js:16,37]", + "WARN: Dropping unused variable iife2 [test/compress/issue-1261.js:16,16]", + "WARN: Dropping side-effect-free statement [test/compress/issue-1261.js:14,8]", + "WARN: Dropping __PURE__ call [test/compress/issue-1261.js:24,8]", + "WARN: Dropping __PURE__ call [test/compress/issue-1261.js:25,31]", + ] +} + +pure_function_calls_toplevel: { + options = { + annotations: true, + booleans: true, + comparisons: true, + conditionals: true, + evaluate: true, + if_return: true, + join_vars: true, + negate_iife: true, + side_effects: true, + toplevel: true, + unused: true, + } + input: { + // pure top-level IIFE will be dropped + // @__PURE__ - comment + (function() { + console.log("iife0"); + })(); + + // pure top-level IIFE assigned to unreferenced var will be dropped + var iife1 = /*@__PURE__*/(function() { + console.log("iife1"); + function iife1() {} + return iife1; + })(); + + (function(){ + // pure IIFE in function scope assigned to unreferenced var will be dropped + var iife2 = /*#__PURE__*/(function() { + console.log("iife2"); + function iife2() {} + return iife2; + })(); + })(); + + // pure top-level calls will be dropped regardless of the leading comments position + var MyClass = /*#__PURE__*//*@class*/(function(){ + function MyClass() {} + MyClass.prototype.method = function() {}; + return MyClass; + })(); + + // comment #__PURE__ comment + bar(), baz(), quux(); + a.b(), /* @__PURE__ */ c.d.e(), f.g(); + } + expect: { + baz(), quux(); + a.b(), f.g(); + } + expect_warnings: [ + "WARN: Dropping __PURE__ call [test/compress/issue-1261.js:3,8]", + "WARN: Dropping side-effect-free statement [test/compress/issue-1261.js:3,8]", + "WARN: Dropping __PURE__ call [test/compress/issue-1261.js:16,37]", + "WARN: Dropping unused variable iife2 [test/compress/issue-1261.js:16,16]", + "WARN: Dropping side-effect-free statement [test/compress/issue-1261.js:14,8]", + "WARN: Dropping __PURE__ call [test/compress/issue-1261.js:31,8]", + "WARN: Dropping __PURE__ call [test/compress/issue-1261.js:32,31]", + "WARN: Dropping __PURE__ call [test/compress/issue-1261.js:8,33]", + "WARN: Dropping unused variable iife1 [test/compress/issue-1261.js:8,12]", + "WARN: Dropping __PURE__ call [test/compress/issue-1261.js:24,45]", + "WARN: Dropping unused variable MyClass [test/compress/issue-1261.js:24,12]", + ] +} + +should_warn: { + options = { + annotations: true, + booleans: true, + conditionals: true, + evaluate: true, + side_effects: true, + } + input: { + /* @__PURE__ */(function(){x})(), void/* @__PURE__ */(function(){y})(); + /* @__PURE__ */(function(){x})() || true ? foo() : bar(); + true || /* @__PURE__ */(function(){y})() ? foo() : bar(); + /* @__PURE__ */(function(){x})() && false ? foo() : bar(); + false && /* @__PURE__ */(function(){y})() ? foo() : bar(); + /* @__PURE__ */(function(){x})() + "foo" ? bar() : baz(); + "foo" + /* @__PURE__ */(function(){y})() ? bar() : baz(); + /* @__PURE__ */(function(){x})() ? foo() : foo(); + [/* @__PURE__ */(function(){x})()] ? foo() : bar(); + !{ foo: /* @__PURE__ */(function(){x})() } ? bar() : baz(); + } + expect: { + foo(); + foo(); + bar(); + bar(); + bar(); + bar(); + foo(); + foo(); + baz(); + } + expect_warnings: [ + "WARN: Dropping __PURE__ call [test/compress/issue-1261.js:1,61]", + "WARN: Dropping __PURE__ call [test/compress/issue-1261.js:1,23]", + "WARN: Dropping side-effect-free statement [test/compress/issue-1261.js:1,23]", + "WARN: Boolean || always true [test/compress/issue-1261.js:2,23]", + "WARN: Dropping __PURE__ call [test/compress/issue-1261.js:2,23]", + "WARN: Condition always true [test/compress/issue-1261.js:2,23]", + "WARN: Condition left of || always true [test/compress/issue-1261.js:3,8]", + "WARN: Condition always true [test/compress/issue-1261.js:3,8]", + "WARN: Boolean && always false [test/compress/issue-1261.js:4,23]", + "WARN: Dropping __PURE__ call [test/compress/issue-1261.js:4,23]", + "WARN: Condition always false [test/compress/issue-1261.js:4,23]", + "WARN: Condition left of && always false [test/compress/issue-1261.js:5,8]", + "WARN: Condition always false [test/compress/issue-1261.js:5,8]", + "WARN: + in boolean context always true [test/compress/issue-1261.js:6,23]", + "WARN: Dropping __PURE__ call [test/compress/issue-1261.js:6,23]", + "WARN: Condition always true [test/compress/issue-1261.js:6,23]", + "WARN: + in boolean context always true [test/compress/issue-1261.js:7,8]", + "WARN: Dropping __PURE__ call [test/compress/issue-1261.js:7,31]", + "WARN: Condition always true [test/compress/issue-1261.js:7,8]", + "WARN: Dropping __PURE__ call [test/compress/issue-1261.js:8,23]", + "WARN: Condition always true [test/compress/issue-1261.js:9,8]", + "WARN: Dropping __PURE__ call [test/compress/issue-1261.js:9,24]", + "WARN: Dropping __PURE__ call [test/compress/issue-1261.js:10,31]", + "WARN: Condition always false [test/compress/issue-1261.js:10,8]", + ] +} diff --git a/test/compress/issue-1275.js b/test/compress/issue-1275.js new file mode 100644 index 00000000000..683e336ddf6 --- /dev/null +++ b/test/compress/issue-1275.js @@ -0,0 +1,49 @@ +string_plus_optimization: { + options = { + booleans: true, + comparisons: true, + conditionals: true, + dead_code: true, + evaluate: true, + hoist_funs: true, + if_return: true, + join_vars: true, + side_effects: true, + unused: true, + } + input: { + function foo(anything) { + function throwing_function() { + throw "nope"; + } + try { + console.log('0' + throwing_function() ? "yes" : "no"); + } catch (ex) { + console.log(ex); + } + console.log('0' + anything ? "yes" : "no"); + console.log(anything + '0' ? "Yes" : "No"); + console.log('' + anything); + console.log(anything + ''); + } + foo(); + } + expect: { + function foo(anything) { + function throwing_function() { + throw "nope"; + } + try { + console.log((throwing_function(), "yes")); + } catch (ex) { + console.log(ex); + } + console.log("yes"); + console.log("Yes"); + console.log('' + anything); + console.log(anything + ''); + } + foo(); + } + expect_stdout: true +} diff --git a/test/compress/issue-1321.js b/test/compress/issue-1321.js new file mode 100644 index 00000000000..c76b5d786a0 --- /dev/null +++ b/test/compress/issue-1321.js @@ -0,0 +1,66 @@ +issue_1321_no_debug: { + mangle = { + properties: { + domprops: true, + keep_quoted: true, + }, + } + input: { + var x = {}; + x.foo = 1; + x["a"] = 2 * x.foo; + console.log(x.foo, x["a"]); + } + expect: { + var x = {}; + x.x = 1; + x["a"] = 2 * x.x; + console.log(x.x, x["a"]); + } + expect_stdout: true +} + +issue_1321_debug: { + mangle = { + properties: { + debug: "", + domprops: true, + keep_quoted: true, + }, + } + input: { + var x = {}; + x.foo = 1; + x["_$foo$_"] = 2 * x.foo; + console.log(x.foo, x["_$foo$_"]); + } + expect: { + var x = {}; + x.x = 1; + x["_$foo$_"] = 2 * x.x; + console.log(x.x, x["_$foo$_"]); + } + expect_stdout: true +} + +issue_1321_with_quoted: { + mangle = { + properties: { + domprops: true, + keep_quoted: false, + }, + } + input: { + var x = {}; + x.foo = 1; + x["a"] = 2 * x.foo; + console.log(x.foo, x["a"]); + } + expect: { + var x = {}; + x.x = 1; + x["o"] = 2 * x.x; + console.log(x.x, x["o"]); + } + expect_stdout: true +} diff --git a/test/compress/issue-143.js b/test/compress/issue-143.js index 4c79790b07f..150cdc4d3c7 100644 --- a/test/compress/issue-143.js +++ b/test/compress/issue-143.js @@ -1,5 +1,5 @@ /** - * There was an incorrect sort behaviour documented in issue #143: + * There was an incorrect sort behavior documented in issue #143: * (x = f(…)) <= x → x >= (x = f(…)) * * For example, let the equation be: @@ -12,37 +12,54 @@ * a >= (a = parseInt('100')) → 99 >= 100 → false */ -tranformation_sort_order_equal: { +transformation_sort_order_equal: { options = { comparisons: true, - }; - - input: { (a = parseInt('100')) == a } - expect: { (a = parseInt('100')) == a } + } + input: { + console.log((a = parseInt("100")) == a); + } + expect: { + console.log((a = parseInt("100")) == a); + } + expect_stdout: "true" } -tranformation_sort_order_unequal: { +transformation_sort_order_unequal: { options = { comparisons: true, - }; - - input: { (a = parseInt('100')) != a } - expect: { (a = parseInt('100')) != a } + } + input: { + console.log((a = parseInt("100")) != a); + } + expect: { + console.log((a = parseInt("100")) != a); + } + expect_stdout: "false" } -tranformation_sort_order_lesser_or_equal: { +transformation_sort_order_lesser_or_equal: { options = { comparisons: true, - }; - - input: { (a = parseInt('100')) <= a } - expect: { (a = parseInt('100')) <= a } + } + input: { + console.log((a = parseInt("100")) <= a); + } + expect: { + console.log((a = parseInt("100")) <= a); + } + expect_stdout: "true" } -tranformation_sort_order_greater_or_equal: { + +transformation_sort_order_greater_or_equal: { options = { comparisons: true, - }; - - input: { (a = parseInt('100')) >= a } - expect: { (a = parseInt('100')) >= a } -} \ No newline at end of file + } + input: { + console.log((a = parseInt("100")) >= a); + } + expect: { + console.log((a = parseInt("100")) >= a); + } + expect_stdout: "true" +} diff --git a/test/compress/issue-1431.js b/test/compress/issue-1431.js new file mode 100644 index 00000000000..2bd681e6e66 --- /dev/null +++ b/test/compress/issue-1431.js @@ -0,0 +1,151 @@ +level_zero: { + options = { + keep_fnames: true, + } + mangle = { + keep_fnames: true + } + input: { + function f(x) { + function n(a) { + return a * a; + } + return function() { + return x; + }; + } + } + expect: { + function f(r) { + function n(n) { + return n * n; + } + return function() { + return r; + }; + } + } +} + +level_one: { + options = { + keep_fnames: true, + } + mangle = { + keep_fnames: true + } + input: { + function f(x) { + return function() { + function n(a) { + return a * a; + } + return x(n); + }; + } + } + expect: { + function f(r) { + return function() { + function n(n) { + return n * n; + } + return r(n); + }; + } + } +} + +level_two: { + options = { + keep_fnames: true, + } + mangle = { + keep_fnames: true + } + input: { + function f(x) { + return function() { + function r(a) { + return a * a; + } + return function() { + function n(a) { + return a * a; + } + return x(n); + }; + }; + } + } + expect: { + function f(t) { + return function() { + function r(n) { + return n * n; + } + return function() { + function n(n) { + return n * n; + } + return t(n); + }; + }; + } + } +} + +level_three: { + options = { + keep_fnames: true, + } + mangle = { + keep_fnames: true + } + input: { + function f(x) { + return function() { + function r(a) { + return a * a; + } + return [ + function() { + function t(a) { + return a * a; + } + return t; + }, + function() { + function n(a) { + return a * a; + } + return x(n); + } + ]; + }; + } + } + expect: { + function f(t) { + return function() { + function r(n) { + return n * n; + } + return [ + function() { + function t(n) { + return n * n; + } + return t; + }, + function() { + function n(n) { + return n * n; + } + return t(n); + } + ]; + }; + } + } +} diff --git a/test/compress/issue-1443.js b/test/compress/issue-1443.js new file mode 100644 index 00000000000..18554ff69bf --- /dev/null +++ b/test/compress/issue-1443.js @@ -0,0 +1,61 @@ +// tests assume that variable `undefined` not redefined and has `void 0` as value + +unsafe_undefined: { + options = { + conditionals: true, + if_return: true, + unsafe_undefined: true, + } + mangle = {} + input: { + function f(undefined) { + return function() { + if (a) + return b; + if (c) + return d; + }; + } + } + expect: { + function f(n) { + return function() { + return a ? b : c ? d : n; + }; + } + } +} + +keep_fnames: { + options = { + conditionals: true, + if_return: true, + unsafe_undefined: true, + } + mangle = { + keep_fnames: true + } + input: { + function f(undefined) { + return function() { + function n(a) { + return a * a; + } + if (a) + return b; + if (c) + return d; + }; + } + } + expect: { + function f(r) { + return function() { + function n(n) { + return n * n; + } + return a ? b : c ? d : r; + }; + } + } +} diff --git a/test/compress/issue-1446.js b/test/compress/issue-1446.js new file mode 100644 index 00000000000..6a6033a0dc8 --- /dev/null +++ b/test/compress/issue-1446.js @@ -0,0 +1,75 @@ +typeof_eq_undefined: { + options = { + comparisons: true, + typeofs: true, + } + input: { + var a = typeof b != "undefined"; + b = typeof a != "undefined"; + var c = typeof d.e !== "undefined"; + var f = "undefined" === typeof g; + g = "undefined" === typeof f; + var h = "undefined" == typeof i.j; + } + expect: { + var a = "undefined" != typeof b; + b = void 0 !== a; + var c = void 0 !== d.e; + var f = "undefined" == typeof g; + g = void 0 === f; + var h = void 0 === i.j; + } +} + +typeof_eq_undefined_ie8: { + options = { + comparisons: true, + ie: true, + typeofs: true, + } + input: { + var a = typeof b != "undefined"; + b = typeof a != "undefined"; + var c = typeof d.e !== "undefined"; + var f = "undefined" === typeof g; + g = "undefined" === typeof f; + var h = "undefined" == typeof i.j; + } + expect: { + var a = "undefined" != typeof b; + b = void 0 !== a; + var c = "undefined" != typeof d.e; + var f = "undefined" == typeof g; + g = void 0 === f; + var h = "undefined" == typeof i.j; + } +} + +undefined_redefined: { + options = { + comparisons: true, + typeofs: true, + } + input: { + function f(undefined) { + var n = 1; + return typeof n == "undefined"; + } + } + expect_exact: "function f(undefined){var n=1;return void 0===n}" +} + +undefined_redefined_mangle: { + options = { + comparisons: true, + typeofs: true, + } + mangle = {} + input: { + function f(undefined) { + var n = 1; + return typeof n == "undefined"; + } + } + expect_exact: "function f(n){var r=1;return void 0===r}" +} diff --git a/test/compress/issue-1447.js b/test/compress/issue-1447.js new file mode 100644 index 00000000000..b5dbf035dd8 --- /dev/null +++ b/test/compress/issue-1447.js @@ -0,0 +1,48 @@ +else_with_empty_block: { + options = {} + input: { + if (x) + yes(); + else { + } + } + expect_exact: "if(x)yes();" +} + +else_with_empty_statement: { + options = {} + input: { + if (x) + yes(); + else + ; + } + expect_exact: "if(x)yes();" +} + +conditional_false_stray_else_in_loop: { + options = { + booleans: true, + comparisons: true, + conditionals: false, + dead_code: true, + evaluate: true, + hoist_vars: true, + if_return: true, + join_vars: true, + loops: true, + side_effects: true, + unused: true, + } + input: { + for (var i = 1; i <= 4; ++i) { + if (i <= 2) continue; + console.log(i); + } + } + expect_exact: "for(var i=1;i<=4;++i)if(i<=2);else console.log(i);" + expect_stdout: [ + "3", + "4", + ] +} diff --git a/test/compress/issue-1569.js b/test/compress/issue-1569.js new file mode 100644 index 00000000000..5f0bca34b88 --- /dev/null +++ b/test/compress/issue-1569.js @@ -0,0 +1,19 @@ +inner_reference: { + options = { + side_effects: true, + } + input: { + !function f(a) { + return a && f(a - 1) + a; + }(42); + !function g(a) { + return a; + }(42); + } + expect: { + !function f(a) { + return a && f(a - 1) + a; + }(42); + !void 0; + } +} diff --git a/test/compress/issue-1588.js b/test/compress/issue-1588.js new file mode 100644 index 00000000000..74f70ba288b --- /dev/null +++ b/test/compress/issue-1588.js @@ -0,0 +1,87 @@ +screw_ie8: { + options = { + ie: false, + } + mangle = { + ie: false, + } + input: { + try { throw "foo"; } catch (x) { console.log(x); } + } + expect_exact: 'try{throw"foo"}catch(o){console.log(o)}' + expect_stdout: [ + "foo" + ] +} + +support_ie8: { + options = { + ie: true, + } + mangle = { + ie: true, + } + input: { + try { throw "foo"; } catch (x) { console.log(x); } + } + expect_exact: 'try{throw"foo"}catch(x){console.log(x)}' + expect_stdout: "foo" +} + +safe_undefined: { + options = { + conditionals: true, + if_return: true, + unsafe: false, + } + mangle = {} + input: { + var a, c; + console.log(function(undefined) { + return function() { + if (a) + return b; + if (c) + return d; + }; + }(1)()); + } + expect: { + var a, c; + console.log(function(n) { + return function() { + return a ? b : c ? d : void 0; + }; + }(1)()); + } + expect_stdout: true +} + +unsafe_undefined: { + options = { + conditionals: true, + if_return: true, + unsafe_undefined: true, + } + mangle = {} + input: { + var a, c; + console.log(function(undefined) { + return function() { + if (a) + return b; + if (c) + return d; + }; + }()()); + } + expect: { + var a, c; + console.log(function(n) { + return function() { + return a ? b : c ? d : n; + }; + }()()); + } + expect_stdout: true +} diff --git a/test/compress/issue-1609.js b/test/compress/issue-1609.js new file mode 100644 index 00000000000..17fbb0e7839 --- /dev/null +++ b/test/compress/issue-1609.js @@ -0,0 +1,81 @@ +chained_evaluation_1: { + options = { + collapse_vars: true, + evaluate: true, + reduce_funcs: true, + reduce_vars: true, + unused: true, + } + input: { + (function() { + var a = 1; + (function() { + var b = a, c; + c = f(b); + c.bar = b; + })(); + })(); + } + expect: { + (function() { + (function() { + f(1).bar = 1; + })(); + })(); + } +} + +chained_evaluation_2: { + options = { + collapse_vars: true, + evaluate: true, + reduce_funcs: true, + reduce_vars: true, + unused: true, + } + input: { + (function() { + var a = "long piece of string"; + (function() { + var b = a, c; + c = f(b); + c.bar = b; + })(); + })(); + } + expect: { + (function() { + (function() { + var b = "long piece of string"; + f(b).bar = b; + })(); + })(); + } +} + +chained_evaluation_3: { + options = { + collapse_vars: true, + evaluate: 10, + reduce_funcs: true, + reduce_vars: true, + unused: true, + } + input: { + (function() { + var a = "long piece of string"; + (function() { + var b = a, c; + c = f(b); + c.bar = b; + })(); + })(); + } + expect: { + (function() { + (function() { + f("long piece of string").bar = "long piece of string"; + })(); + })(); + } +} diff --git a/test/compress/issue-1639.js b/test/compress/issue-1639.js new file mode 100644 index 00000000000..4bf74f3385d --- /dev/null +++ b/test/compress/issue-1639.js @@ -0,0 +1,84 @@ +issue_1639_1: { + options = { + booleans: true, + collapse_vars: true, + conditionals: true, + evaluate: true, + join_vars: true, + loops: true, + sequences: true, + side_effects: true, + } + input: { + var a = 100, b = 10; + var L1 = 5; + while (--L1 > 0) { + if ((--b), false) { + if (b) { + var ignore = 0; + } + } + } + console.log(a, b); + } + expect: { + for (var a = 100, b = 10, L1 = 5, ignore; --L1 > 0;) { + --b; + } + console.log(a, b); + } + expect_stdout: "100 6" +} + +issue_1639_2: { + options = { + booleans: true, + collapse_vars: true, + conditionals: true, + evaluate: true, + join_vars: true, + sequences: true, + side_effects: true, + } + input: { + var a = 100, b = 10; + function f19() { + if (++a, false) + if (a) + if (++a); + } + f19(); + console.log(a, b); + } + expect: { + var a = 100, b = 10; + function f19() { + ++a, 1; + } + f19(), + console.log(a, b); + } + expect_stdout: "101 10" +} + +issue_1639_3: { + options = { + booleans: true, + collapse_vars: true, + conditionals: true, + evaluate: true, + sequences: true, + side_effects: true, + } + input: { + var a = 100, b = 10; + a++ && false && a ? 0 : 0; + console.log(a, b); + } + expect: { + var a = 100, b = 10; + a++, + console.log(a, b); + } + expect_stdout: "101 10" +} diff --git a/test/compress/issue-1656.js b/test/compress/issue-1656.js new file mode 100644 index 00000000000..146ec609fc0 --- /dev/null +++ b/test/compress/issue-1656.js @@ -0,0 +1,41 @@ +f7: { + options = { + booleans: true, + collapse_vars: true, + comparisons: true, + conditionals: true, + dead_code: true, + drop_debugger: true, + evaluate: true, + hoist_funs: true, + if_return: true, + join_vars: true, + loops: true, + negate_iife: true, + passes: 3, + properties: true, + reduce_funcs: true, + reduce_vars: true, + sequences: true, + side_effects: true, + toplevel: true, + unused: true, + } + beautify = { + beautify: true, + } + input: { + var a = 100, b = 10; + function f22464() { + var brake146670 = 5; + while (((b = a) ? !a : ~a ? null : b += a) && --brake146670 > 0) { + } + } + f22464(); + console.log(a, b); + } + expect_exact: [ + "console.log(100, 100);", + ] + expect_stdout: "100 100" +} diff --git a/test/compress/issue-1673.js b/test/compress/issue-1673.js new file mode 100644 index 00000000000..88e1fa8d412 --- /dev/null +++ b/test/compress/issue-1673.js @@ -0,0 +1,166 @@ +side_effects_catch: { + options = { + reduce_funcs: true, + reduce_vars: true, + side_effects: true, + unused: true, + } + input: { + function f() { + function g() { + try { + throw 0; + } catch (e) { + console.log("PASS"); + } + } + g(); + } + f(); + } + expect: { + function f() { + (function() { + try { + throw 0; + } catch (e) { + console.log("PASS"); + } + })(); + } + f(); + } + expect_stdout: "PASS" +} + +side_effects_else: { + options = { + reduce_funcs: true, + reduce_vars: true, + side_effects: true, + unused: true, + } + input: { + function f(x) { + function g() { + if (x); + else console.log("PASS"); + } + g(); + } + f(0); + } + expect: { + function f(x) { + (function() { + if (x); + else console.log("PASS"); + })(); + } + f(0); + } + expect_stdout: "PASS" +} + +side_effects_finally: { + options = { + reduce_funcs: true, + reduce_vars: true, + side_effects: true, + unused: true, + } + input: { + function f() { + function g() { + try { + x(); + } catch (e) { + } finally { + console.log("PASS"); + } + } + g(); + } + f(); + } + expect: { + function f() { + (function() { + try { + x(); + } catch (e) { + } finally { + console.log("PASS"); + } + })(); + } + f(); + } + expect_stdout: "PASS" +} + +side_effects_label: { + options = { + reduce_funcs: true, + reduce_vars: true, + side_effects: true, + unused: true, + } + input: { + function f(x) { + function g() { + L: { + console.log("PASS"); + break L; + } + } + g(); + } + f(0); + } + expect: { + function f(x) { + (function() { + L: { + console.log("PASS"); + break L; + } + })(); + } + f(0); + } + expect_stdout: "PASS" +} + +side_effects_switch: { + options = { + reduce_funcs: true, + reduce_vars: true, + side_effects: true, + unused: true, + } + input: { + function f() { + function g() { + switch (0) { + default: + case console.log("PASS"): + } + } + g(); + } + f(); + } + expect: { + function f() { + (function() { + switch (0) { + default: + case console.log("PASS"): + } + })(); + } + f(); + } + expect_stdout: "PASS" +} diff --git a/test/compress/issue-1704.js b/test/compress/issue-1704.js new file mode 100644 index 00000000000..cb806a09da9 --- /dev/null +++ b/test/compress/issue-1704.js @@ -0,0 +1,447 @@ +mangle_catch: { + options = { + ie: false, + toplevel: false, + } + mangle = { + ie: false, + toplevel: false, + } + input: { + var a = "FAIL"; + try { + throw 1; + } catch (args) { + a = "PASS"; + } + console.log(a); + } + expect_exact: 'var a="FAIL";try{throw 1}catch(o){a="PASS"}console.log(a);' + expect_stdout: "PASS" +} + +mangle_catch_ie8: { + options = { + ie: true, + toplevel: false, + } + mangle = { + ie: true, + toplevel: false, + } + input: { + var a = "FAIL"; + try { + throw 1; + } catch (args) { + a = "PASS"; + } + console.log(a); + } + expect_exact: 'var a="FAIL";try{throw 1}catch(args){a="PASS"}console.log(a);' + expect_stdout: "PASS" +} + +mangle_catch_var: { + options = { + ie: false, + toplevel: false, + } + mangle = { + ie: false, + toplevel: false, + } + input: { + var a = "FAIL"; + try { + throw 1; + } catch (args) { + var a = "PASS"; + } + console.log(a); + } + expect_exact: 'var a="FAIL";try{throw 1}catch(o){var a="PASS"}console.log(a);' + expect_stdout: "PASS" +} + +mangle_catch_var_ie8: { + options = { + ie: true, + toplevel: false, + } + mangle = { + ie: true, + toplevel: false, + } + input: { + var a = "FAIL"; + try { + throw 1; + } catch (args) { + var a = "PASS"; + } + console.log(a); + } + expect_exact: 'var a="FAIL";try{throw 1}catch(args){var a="PASS"}console.log(a);' + expect_stdout: "PASS" +} + +mangle_catch_toplevel: { + options = { + ie: false, + toplevel: true, + } + mangle = { + ie: false, + toplevel: true, + } + input: { + var a = "FAIL"; + try { + throw 1; + } catch (args) { + a = "PASS"; + } + console.log(a); + } + expect_exact: 'var c="FAIL";try{throw 1}catch(o){c="PASS"}console.log(c);' + expect_stdout: "PASS" +} + +mangle_catch_ie8_toplevel: { + options = { + ie: true, + toplevel: true, + } + mangle = { + ie: true, + toplevel: true, + } + input: { + var a = "FAIL"; + try { + throw 1; + } catch (args) { + a = "PASS"; + } + console.log(a); + } + expect_exact: 'var o="FAIL";try{throw 1}catch(c){o="PASS"}console.log(o);' + expect_stdout: "PASS" +} + +mangle_catch_var_toplevel: { + options = { + ie: false, + toplevel: true, + } + mangle = { + ie: false, + toplevel: true, + } + input: { + var a = "FAIL"; + try { + throw 1; + } catch (args) { + var a = "PASS"; + } + console.log(a); + } + expect_exact: 'var r="FAIL";try{throw 1}catch(o){var r="PASS"}console.log(r);' + expect_stdout: "PASS" +} + +mangle_catch_var_ie8_toplevel: { + options = { + ie: true, + toplevel: true, + } + mangle = { + ie: true, + toplevel: true, + } + input: { + var a = "FAIL"; + try { + throw 1; + } catch (args) { + var a = "PASS"; + } + console.log(a); + } + expect_exact: 'var o="FAIL";try{throw 1}catch(r){var o="PASS"}console.log(o);' + expect_stdout: "PASS" +} + +mangle_catch_redef_1: { + options = { + ie: false, + toplevel: false, + } + mangle = { + ie: false, + toplevel: false, + } + input: { + var a = "PASS"; + try { + throw "FAIL1"; + } catch (a) { + var a = "FAIL2"; + } + console.log(a); + } + expect_exact: 'var a="PASS";try{throw"FAIL1"}catch(a){var a="FAIL2"}console.log(a);' + expect_stdout: "PASS" +} + +mangle_catch_redef_1_ie8: { + options = { + ie: true, + toplevel: false, + } + mangle = { + ie: true, + toplevel: false, + } + input: { + var a = "PASS"; + try { + throw "FAIL1"; + } catch (a) { + var a = "FAIL2"; + } + console.log(a); + } + expect_exact: 'var a="PASS";try{throw"FAIL1"}catch(a){var a="FAIL2"}console.log(a);' + expect_stdout: "PASS" +} + +mangle_catch_redef_1_toplevel: { + options = { + ie: false, + toplevel: true, + } + mangle = { + ie: false, + toplevel: true, + } + input: { + var a = "PASS"; + try { + throw "FAIL1"; + } catch (a) { + var a = "FAIL2"; + } + console.log(a); + } + expect_exact: 'var o="PASS";try{throw"FAIL1"}catch(o){var o="FAIL2"}console.log(o);' + expect_stdout: "PASS" +} + +mangle_catch_redef_1_ie8_toplevel: { + options = { + ie: true, + toplevel: true, + } + mangle = { + ie: true, + toplevel: true, + } + input: { + var a = "PASS"; + try { + throw "FAIL1"; + } catch (a) { + var a = "FAIL2"; + } + console.log(a); + } + expect_exact: 'var o="PASS";try{throw"FAIL1"}catch(o){var o="FAIL2"}console.log(o);' + expect_stdout: "PASS" +} + +mangle_catch_redef_2: { + options = { + ie: false, + toplevel: false, + } + mangle = { + ie: false, + toplevel: false, + } + input: { + try { + throw "FAIL1"; + } catch (a) { + var a = "FAIL2"; + } + console.log(a); + } + expect_exact: 'try{throw"FAIL1"}catch(a){var a="FAIL2"}console.log(a);' + expect_stdout: "undefined" +} + +mangle_catch_redef_2_ie8: { + options = { + ie: true, + toplevel: false, + } + mangle = { + ie: true, + toplevel: false, + } + input: { + try { + throw "FAIL1"; + } catch (a) { + var a = "FAIL2"; + } + console.log(a); + } + expect_exact: 'try{throw"FAIL1"}catch(a){var a="FAIL2"}console.log(a);' + expect_stdout: "undefined" +} + +mangle_catch_redef_2_toplevel: { + options = { + ie: false, + toplevel: true, + } + mangle = { + ie: false, + toplevel: true, + } + input: { + try { + throw "FAIL1"; + } catch (a) { + var a = "FAIL2"; + } + console.log(a); + } + expect_exact: 'try{throw"FAIL1"}catch(o){var o="FAIL2"}console.log(o);' + expect_stdout: "undefined" +} + +mangle_catch_redef_2_ie8_toplevel: { + options = { + ie: true, + toplevel: true, + } + mangle = { + ie: true, + toplevel: true, + } + input: { + try { + throw "FAIL1"; + } catch (a) { + var a = "FAIL2"; + } + console.log(a); + } + expect_exact: 'try{throw"FAIL1"}catch(o){var o="FAIL2"}console.log(o);' + expect_stdout: "undefined" +} + +mangle_catch_redef_3: { + mangle = { + ie: false, + toplevel: false, + } + input: { + var o = "PASS"; + try { + throw 0; + } catch (o) { + // prints "FAIL" if inlined on Node.js v4- + (function() { + function f() { + o = "FAIL"; + } + f(), f(); + })(); + } + console.log(o); + } + expect_exact: 'var o="PASS";try{throw 0}catch(o){(function(){function c(){o="FAIL"}c(),c()})()}console.log(o);' + expect_stdout: "PASS" + node_version: ">=6" +} + +mangle_catch_redef_3_toplevel: { + mangle = { + ie: false, + toplevel: true, + } + input: { + var o = "PASS"; + try { + throw 0; + } catch (o) { + // prints "FAIL" if inlined on Node.js v4- + (function() { + function f() { + o = "FAIL"; + } + f(), f(); + })(); + } + console.log(o); + } + expect_exact: 'var c="PASS";try{throw 0}catch(c){(function(){function o(){c="FAIL"}o(),o()})()}console.log(c);' + expect_stdout: "PASS" + node_version: ">=6" +} + +mangle_catch_redef_3_ie8: { + mangle = { + ie: true, + toplevel: false, + } + input: { + var o = "PASS"; + try { + throw 0; + } catch (o) { + // prints "FAIL" if inlined on Node.js v4- + (function() { + function f() { + o = "FAIL"; + } + f(), f(); + })(); + } + console.log(o); + } + expect_exact: 'var o="PASS";try{throw 0}catch(o){(function(){function c(){o="FAIL"}c(),c()})()}console.log(o);' + expect_stdout: "PASS" + node_version: ">=6" +} + +mangle_catch_redef_3_ie8_toplevel: { + mangle = { + ie: true, + toplevel: true, + } + input: { + var o = "PASS"; + try { + throw 0; + } catch (o) { + // prints "FAIL" if inlined on Node.js v4- + (function() { + function f() { + o = "FAIL"; + } + f(), f(); + })(); + } + console.log(o); + } + expect_exact: 'var c="PASS";try{throw 0}catch(c){(function(){function o(){c="FAIL"}o(),o()})()}console.log(c);' + expect_stdout: "PASS" + node_version: ">=6" +} diff --git a/test/compress/issue-1733.js b/test/compress/issue-1733.js new file mode 100644 index 00000000000..fc012ab87b0 --- /dev/null +++ b/test/compress/issue-1733.js @@ -0,0 +1,97 @@ +function_iife_catch: { + mangle = { + ie: false, + } + input: { + function f(n) { + !function() { + try { + throw 0; + } catch (n) { + var a = 1; + console.log(n, a); + } + }(); + } + f(); + } + expect_exact: "function f(o){!function(){try{throw 0}catch(o){var c=1;console.log(o,c)}}()}f();" + expect_stdout: "0 1" +} + +function_iife_catch_ie8: { + mangle = { + ie: true, + } + input: { + function f(n) { + !function() { + try { + throw 0; + } catch (n) { + var a = 1; + console.log(n, a); + } + }(); + } + f(); + } + expect_exact: "function f(c){!function(){try{throw 0}catch(c){var o=1;console.log(c,o)}}()}f();" + expect_stdout: "0 1" +} + +function_catch_catch: { + mangle = { + ie: false, + } + input: { + var o = 0; + function f() { + try { + throw 1; + } catch (c) { + try { + throw 2; + } catch (o) { + var o = 3; + console.log(o); + } + } + console.log(o); + } + f(); + } + expect_exact: "var o=0;function f(){try{throw 1}catch(o){try{throw 2}catch(c){var c=3;console.log(c)}}console.log(c)}f();" + expect_stdout: [ + "3", + "undefined", + ] +} + +function_catch_catch_ie8: { + mangle = { + ie: true, + } + input: { + var o = 0; + function f() { + try { + throw 1; + } catch (c) { + try { + throw 2; + } catch (o) { + var o = 3; + console.log(o); + } + } + console.log(o); + } + f(); + } + expect_exact: "var o=0;function f(){try{throw 1}catch(c){try{throw 2}catch(o){var o=3;console.log(o)}}console.log(o)}f();" + expect_stdout: [ + "3", + "undefined", + ] +} diff --git a/test/compress/issue-1750.js b/test/compress/issue-1750.js new file mode 100644 index 00000000000..970cea12066 --- /dev/null +++ b/test/compress/issue-1750.js @@ -0,0 +1,54 @@ +case_1: { + options = { + dead_code: true, + evaluate: true, + switches: true, + } + input: { + var a = 0, b = 1; + switch (true) { + case a || true: + default: + b = 2; + case true: + } + console.log(a, b); + } + expect: { + var a = 0, b = 1; + switch (true) { + case a || true: + b = 2; + } + console.log(a, b); + } + expect_stdout: "0 2" +} + +case_2: { + options = { + dead_code: true, + evaluate: true, + switches: true, + } + input: { + var a = 0, b = 1; + switch (0) { + default: + b = 2; + case a: + a = 3; + case 0: + } + console.log(a, b); + } + expect: { + var a = 0, b = 1; + switch (0) { + case a: + a = 3; + } + console.log(a, b); + } + expect_stdout: "3 1" +} diff --git a/test/compress/issue-1770.js b/test/compress/issue-1770.js new file mode 100644 index 00000000000..cc01edfbee0 --- /dev/null +++ b/test/compress/issue-1770.js @@ -0,0 +1,263 @@ +mangle_props: { + mangle = { + properties: true, + } + input: { + var obj = { + undefined: 1, + NaN: 2, + Infinity: 3, + "-Infinity": 4, + null: 5, + }; + console.log( + obj[void 0], + obj[undefined], + obj["undefined"], + obj[0/0], + obj[NaN], + obj["NaN"], + obj[1/0], + obj[Infinity], + obj["Infinity"], + obj[-1/0], + obj[-Infinity], + obj["-Infinity"], + obj[null], + obj["null"] + ); + } + expect: { + var obj = { + undefined: 1, + NaN: 2, + Infinity: 3, + "-Infinity": 4, + null: 5, + }; + console.log( + obj[void 0], + obj[void 0], + obj["undefined"], + obj[0/0], + obj[NaN], + obj["NaN"], + obj[1/0], + obj[1/0], + obj["Infinity"], + obj[-1/0], + obj[-(1/0)], + obj["-Infinity"], + obj[null], + obj["null"] + ); + } + expect_stdout: "1 1 1 2 2 2 3 3 3 4 4 4 5 5" + expect_warnings: [ + "INFO: Preserving reserved property undefined", + "INFO: Preserving reserved property NaN", + "INFO: Preserving reserved property Infinity", + "INFO: Preserving reserved property -Infinity", + "INFO: Preserving reserved property null", + "INFO: Preserving reserved property log", + ] +} + +numeric_literal: { + mangle = { + properties: { + domprops: true, + }, + } + beautify = { + beautify: true, + } + input: { + var obj = { + 0: 0, + "-0": 1, + 42: 2, + "42": 3, + 0x25: 4, + "0x25": 5, + 1E42: 6, + "1E42": 7, + "1e+42": 8, + }; + console.log(obj[-0], obj[-""], obj["-0"]); + console.log(obj[42], obj["42"]); + console.log(obj[0x25], obj["0x25"], obj[37], obj["37"]); + console.log(obj[1E42], obj["1E42"], obj["1e+42"]); + } + expect_exact: [ + 'var obj = {', + ' 0: 0,', + ' "-0": 1,', + ' 42: 2,', + ' 42: 3,', + ' 37: 4,', + ' o: 5,', + ' 1e42: 6,', + ' b: 7,', + ' 1e42: 8', + '};', + '', + 'console.log(obj[-0], obj[-""], obj["-0"]);', + '', + 'console.log(obj[42], obj["42"]);', + '', + 'console.log(obj[37], obj["o"], obj[37], obj["37"]);', + '', + 'console.log(obj[1e42], obj["b"], obj["1e+42"]);', + ] + expect_stdout: [ + "0 0 1", + "3 3", + "4 5 4 4", + "8 7 8", + ] + expect_warnings: [ + "INFO: Mapping property 0x25 to o", + "INFO: Mapping property 1E42 to b", + "INFO: Preserving reserved property log", + ] +} + +identifier: { + mangle = { + properties: { + builtins: true, + domprops: true, + }, + } + input: { + var obj = { + abstract: 1, + boolean: 2, + byte: 3, + char: 4, + class: 5, + double: 6, + enum: 7, + export: 8, + extends: 9, + final: 10, + float: 11, + goto: 12, + implements: 13, + import: 14, + int: 15, + interface: 16, + let: 17, + long: 18, + native: 19, + package: 20, + private: 21, + protected: 22, + public: 23, + short: 24, + static: 25, + super: 26, + synchronized: 27, + this: 28, + throws: 29, + transient: 30, + volatile: 31, + yield: 32, + false: 33, + null: 34, + true: 35, + break: 36, + case: 37, + catch: 38, + const: 39, + continue: 40, + debugger: 41, + default: 42, + delete: 43, + do: 44, + else: 45, + finally: 46, + for: 47, + function: 48, + if: 49, + in: 50, + instanceof: 51, + new: 52, + return: 53, + switch: 54, + throw: 55, + try: 56, + typeof: 57, + var: 58, + void: 59, + while: 60, + with: 61, + }; + } + expect: { + var obj = { + e: 1, + t: 2, + n: 3, + a: 4, + i: 5, + o: 6, + r: 7, + l: 8, + s: 9, + c: 10, + f: 11, + u: 12, + d: 13, + h: 14, + p: 15, + b: 16, + v: 17, + w: 18, + y: 19, + g: 20, + m: 21, + k: 22, + x: 23, + j: 24, + z: 25, + q: 26, + A: 27, + B: 28, + C: 29, + D: 30, + E: 31, + F: 32, + G: 33, + H: 34, + I: 35, + J: 36, + K: 37, + L: 38, + M: 39, + N: 40, + O: 41, + P: 42, + Q: 43, + R: 44, + S: 45, + T: 46, + U: 47, + V: 48, + W: 49, + X: 50, + Y: 51, + Z: 52, + $: 53, + _: 54, + ee: 55, + te: 56, + ne: 57, + ae: 58, + ie: 59, + oe: 60, + re: 61, + }; + } +} diff --git a/test/compress/issue-1787.js b/test/compress/issue-1787.js new file mode 100644 index 00000000000..fcef61489a7 --- /dev/null +++ b/test/compress/issue-1787.js @@ -0,0 +1,17 @@ +unary_prefix: { + options = { + evaluate: true, + inline: true, + reduce_funcs: true, + reduce_vars: true, + unused: true, + } + input: { + console.log(function() { + var x = -(2 / 3); + return x; + }()); + } + expect_exact: "console.log(-2/3);" + expect_stdout: true +} diff --git a/test/compress/issue-1833.js b/test/compress/issue-1833.js new file mode 100644 index 00000000000..41777ddbbec --- /dev/null +++ b/test/compress/issue-1833.js @@ -0,0 +1,138 @@ +iife_for: { + options = { + negate_iife: true, + reduce_funcs: true, + reduce_vars: true, + toplevel: true, + unused: true, + } + input: { + function f() { + function g() { + L: for (;;) break L; + } + g(); + } + f(); + } + expect: { + !function() { + !function() { + L: for (;;) break L; + }(); + }(); + } +} + +iife_for_in: { + options = { + negate_iife: true, + reduce_funcs: true, + reduce_vars: true, + toplevel: true, + unused: true, + } + input: { + function f() { + function g() { + L: for (var a in x) break L; + } + g(); + } + f(); + } + expect: { + !function() { + !function() { + L: for (var a in x) break L; + }(); + }(); + } +} + +iife_do: { + options = { + negate_iife: true, + reduce_funcs: true, + reduce_vars: true, + toplevel: true, + unused: true, + } + input: { + function f() { + function g() { + L: do { + break L; + } while (1); + } + g(); + } + f(); + } + expect: { + !function() { + !function() { + L: do { + break L; + } while (1); + }(); + }(); + } +} + +iife_while: { + options = { + negate_iife: true, + reduce_funcs: true, + reduce_vars: true, + toplevel: true, + unused: true, + } + input: { + function f() { + function g() { + L: while (1) break L; + } + g(); + } + f(); + } + expect: { + !function() { + !function() { + L: while (1) break L; + }(); + }(); + } +} + +label_do: { + options = { + evaluate: true, + loops: true, + } + input: { + L: do { + continue L; + } while (0); + } + expect: { + L: do { + continue L; + } while (0); + } +} + +label_while: { + options = { + dead_code: true, + evaluate: true, + loops: true, + } + input: { + function f() { + L: while (0) continue L; + } + } + expect_exact: "function f(){L:0}" +} diff --git a/test/compress/issue-1943.js b/test/compress/issue-1943.js new file mode 100644 index 00000000000..69bb9e64064 --- /dev/null +++ b/test/compress/issue-1943.js @@ -0,0 +1,31 @@ +operator: { + input: { + a. //comment + typeof + } + expect_exact: "a.typeof;" +} + +name: { + input: { + a. //comment + b + } + expect_exact: "a.b;" +} + +keyword: { + input: { + a. //comment + default + } + expect_exact: "a.default;" +} + +atom: { + input: { + a. //comment + true + } + expect_exact: "a.true;" +} diff --git a/test/compress/issue-208.js b/test/compress/issue-208.js new file mode 100644 index 00000000000..3f732755eb3 --- /dev/null +++ b/test/compress/issue-208.js @@ -0,0 +1,74 @@ +do_not_update_lhs: { + options = { + global_defs: { + DEBUG: 0, + }, + } + input: { + DEBUG++; + DEBUG += 1; + DEBUG = 1; + } + expect: { + DEBUG++; + DEBUG += 1; + DEBUG = 1; + } +} + +do_update_rhs: { + options = { + global_defs: { + DEBUG: 0, + }, + } + input: { + MY_DEBUG = DEBUG; + MY_DEBUG += DEBUG; + } + expect: { + MY_DEBUG = 0; + MY_DEBUG += 0; + } +} + +mixed: { + options = { + evaluate: true, + global_defs: { + DEBUG: 0, + ENV: 1, + FOO: 2, + }, + } + input: { + var ENV = 3; + var FOO = 4; + f(ENV * 10); + --FOO; + DEBUG = 1; + DEBUG++; + DEBUG += 1; + f(DEBUG); + x = DEBUG; + } + expect: { + var ENV = 3; + var FOO = 4; + f(10); + --FOO; + DEBUG = 1; + DEBUG++; + DEBUG += 1; + f(0); + x = 0; + } + expect_warnings: [ + "WARN: global_defs ENV redefined [test/compress/issue-208.js:1,12]", + "WARN: global_defs FOO redefined [test/compress/issue-208.js:2,12]", + "WARN: global_defs FOO redefined [test/compress/issue-208.js:4,10]", + "WARN: global_defs DEBUG redefined [test/compress/issue-208.js:5,8]", + "WARN: global_defs DEBUG redefined [test/compress/issue-208.js:6,8]", + "WARN: global_defs DEBUG redefined [test/compress/issue-208.js:7,8]", + ] +} diff --git a/test/compress/issue-22.js b/test/compress/issue-22.js index a8b7bc60037..1cbc7602eb7 100644 --- a/test/compress/issue-22.js +++ b/test/compress/issue-22.js @@ -1,5 +1,7 @@ return_with_no_value_in_if_body: { - options = { conditionals: true }; + options = { + conditionals: true, + } input: { function foo(bar) { if (bar) { diff --git a/test/compress/issue-2652.js b/test/compress/issue-2652.js new file mode 100644 index 00000000000..d3159742961 --- /dev/null +++ b/test/compress/issue-2652.js @@ -0,0 +1,33 @@ +insert_semicolon: { + beautify = { + beautify: true, + comments: "all", + } + input: { + var a + /* foo */ var b + } + expect_exact: [ + "var a", + "/* foo */;", + "", + "var b;", + ] +} + +unary_postfix: { + beautify = { + beautify: true, + comments: "all", + } + input: { + a + /* foo */++b + } + expect_exact: [ + "a", + "/* foo */;", + "", + "++b;", + ] +} diff --git a/test/compress/issue-267.js b/test/compress/issue-267.js index 7233d9f185c..4f478d25ebf 100644 --- a/test/compress/issue-267.js +++ b/test/compress/issue-267.js @@ -1,5 +1,7 @@ issue_267: { - options = { comparisons: true }; + options = { + comparisons: true, + } input: { x = a % b / b * c * 2; x = a % b * 2 diff --git a/test/compress/issue-269.js b/test/compress/issue-269.js index 1d41dea61b7..3e0c44373ae 100644 --- a/test/compress/issue-269.js +++ b/test/compress/issue-269.js @@ -1,66 +1,111 @@ issue_269_1: { - options = {unsafe: true}; - input: { - f( - String(x), - Number(x), - Boolean(x), + options = { + unsafe: true, + } + input: { + var x = {}; + console.log( + String(x), + Number(x), + Boolean(x), - String(), - Number(), - Boolean() - ); - } - expect: { - f( - x + '', +x, !!x, - '', 0, false - ); - } + String(), + Number(), + Boolean() + ); + } + expect: { + var x = {}; + console.log( + "" + x, +("" + x), !!x, + "", 0, false + ); + } + expect_stdout: true } issue_269_dangers: { - options = {unsafe: true}; - input: { - f( - String(x, x), - Number(x, x), - Boolean(x, x) - ); - } - expect: { - f(String(x, x), Number(x, x), Boolean(x, x)); - } + options = { + unsafe: true, + } + input: { + var x = {}; + console.log( + String(x, x), + Number(x, x), + Boolean(x, x) + ); + } + expect: { + var x = {}; + console.log(String(x, x), Number(x, x), Boolean(x, x)); + } + expect_stdout: true } issue_269_in_scope: { - options = {unsafe: true}; - input: { - var String, Number, Boolean; - f( - String(x), - Number(x, x), - Boolean(x) - ); - } - expect: { - var String, Number, Boolean; - f(String(x), Number(x, x), Boolean(x)); - } + options = { + unsafe: true, + } + input: { + var String, Number, Boolean; + var x = {}; + console.log( + String(x), + Number(x, x), + Boolean(x) + ); + } + expect: { + var String, Number, Boolean; + var x = {}; + console.log(String(x), Number(x, x), Boolean(x)); + } + expect_stdout: true } strings_concat: { - options = {unsafe: true}; - input: { - f( - String(x + 'str'), - String('str' + x) - ); - } - expect: { - f( - x + 'str', - 'str' + x - ); - } + options = { + strings: true, + unsafe: true, + } + input: { + var x = {}; + console.log( + String(x + "str"), + String("str" + x) + ); + } + expect: { + var x = {}; + console.log( + x + "str", + "str" + x + ); + } + expect_stdout: true +} + +regexp: { + options = { + evaluate: true, + unsafe: true, + } + input: { + RegExp("foo"); + RegExp("bar", "ig"); + RegExp(foo); + RegExp("bar", ig); + RegExp("should", "fail"); + } + expect: { + /foo/; + /bar/ig; + RegExp(foo); + RegExp("bar", ig); + RegExp("should", "fail"); + } + expect_warnings: [ + 'WARN: Error converting RegExp("should","fail") [test/compress/issue-269.js:5,8]', + ] } diff --git a/test/compress/issue-2719.js b/test/compress/issue-2719.js new file mode 100644 index 00000000000..f11d00774ab --- /dev/null +++ b/test/compress/issue-2719.js @@ -0,0 +1,32 @@ +warn: { + options = { + evaluate: true, + inline: true, + passes: 2, + properties: true, + reduce_funcs: true, + reduce_vars: true, + toplevel: true, + unused: true, + } + input: { + function f() { + return g(); + } + function g() { + return g["call" + "er"].arguments; + } + // 3 + console.log(f(1, 2, 3).length); + } + expect: { + // TypeError: Cannot read property 'arguments' of null + console.log(function g() { + return g.caller.arguments; + }().length); + } + expect_warnings: [ + "WARN: Function.prototype.arguments not supported [test/compress/issue-2719.js:5,19]", + "WARN: Function.prototype.caller not supported [test/compress/issue-2719.js:5,19]", + ] +} diff --git a/test/compress/issue-281.js b/test/compress/issue-281.js new file mode 100644 index 00000000000..649eb255c4b --- /dev/null +++ b/test/compress/issue-281.js @@ -0,0 +1,512 @@ +collapse_vars_constants: { + options = { + collapse_vars: true, + evaluate: true, + inline: true, + reduce_funcs: true, + reduce_vars: true, + unused: true, + } + input: { + function f1(x) { + var a = 4, b = x.prop, c = 5, d = sideeffect1(), e = sideeffect2(); + return b + (function() { return d - a * e - c; })(); + } + function f2(x) { + var a = 4, b = x.prop, c = 5, not_used = sideeffect1(), e = sideeffect2(); + return b + (function() { return -a * e - c; })(); + } + } + expect: { + function f1(x) { + var b = x.prop, d = sideeffect1(), e = sideeffect2(); + return b + (d - 4 * e - 5); + } + function f2(x) { + var b = x.prop; + sideeffect1(); + return b + (-4 * sideeffect2() - 5); + } + } +} + +modified: { + options = { + collapse_vars: true, + inline: true, + unused: true, + } + input: { + function f5(b) { + var a = function() { + return b; + }(); + return b++ + a; + } + console.log(f5(1)); + } + expect: { + function f5(b) { + var a = b; + return b++ + a; + } + console.log(f5(1)); + } + expect_stdout: "2" +} + +ref_scope: { + options = { + collapse_vars: true, + inline: true, + unused: true, + } + input: { + console.log(function() { + var a = 1, b = 2, c = 3; + var a = c++, b = b /= a; + return function() { + return a; + }() + b; + }()); + } + expect: { + console.log(function() { + var a = 1, b = 2, c = 3; + b = b /= a = c++; + return a + b; + }()); + } + expect_stdout: true +} + +safe_undefined: { + options = { + conditionals: true, + if_return: true, + inline: true, + unsafe: false, + unused: true, + } + mangle = {} + input: { + var a, c; + console.log(function(undefined) { + return function() { + if (a) + return b; + if (c) + return d; + }; + }(1)()); + } + expect: { + var a, c; + console.log(a ? b : c ? d : void 0); + } + expect_stdout: true +} + +negate_iife_3: { + expression = true + options = { + conditionals: true, + expression: true, + inline: true, + negate_iife: true, + } + input: { + (function(){ return t })() ? console.log(true) : console.log(false); + } + expect: { + t ? console.log(true) : console.log(false); + } +} + +negate_iife_3_off: { + expression = true + options = { + conditionals: true, + expression: true, + inline: true, + negate_iife: false, + } + input: { + (function(){ return t })() ? console.log(true) : console.log(false); + } + expect: { + t ? console.log(true) : console.log(false); + } +} + +negate_iife_4: { + options = { + conditionals: true, + expression: true, + inline: true, + negate_iife: true, + sequences: true, + } + input: { + (function(){ return t })() ? console.log(true) : console.log(false); + (function(){ + console.log("something"); + })(); + } + expect: { + t ? console.log(true) : console.log(false), void console.log("something"); + } +} + +negate_iife_5: { + options = { + conditionals: true, + expression: true, + inline: true, + negate_iife: true, + sequences: true, + } + input: { + if ((function(){ return t })()) { + foo(true); + } else { + bar(false); + } + (function(){ + console.log("something"); + })(); + } + expect: { + t ? foo(true) : bar(false), void console.log("something"); + } +} + +negate_iife_5_off: { + options = { + conditionals: true, + expression: true, + inline: true, + negate_iife: false, + sequences: true, + } + input: { + if ((function(){ return t })()) { + foo(true); + } else { + bar(false); + } + (function(){ + console.log("something"); + })(); + } + expect: { + t ? foo(true) : bar(false), void console.log("something"); + } +} + +issue_1254_negate_iife_true: { + expression = true + options = { + expression: true, + inline: true, + negate_iife: true, + } + input: { + (function() { + return function() { + console.log('test') + }; + })()(); + } + expect_exact: 'void console.log("test")' + expect_stdout: true +} + +issue_1254_negate_iife_nested: { + expression = true + options = { + expression: true, + inline: true, + negate_iife: true, + } + input: { + (function() { + return function() { + console.log('test') + }; + })()()()()(); + } + expect_exact: '(void console.log("test"))()()()' +} + +negate_iife_issue_1073: { + options = { + conditionals: true, + evaluate: true, + inline: true, + negate_iife: true, + reduce_funcs: true, + reduce_vars: true, + sequences: true, + unused: true, + } + input: { + new (function(a) { + return function Foo() { + this.x = a; + console.log(this); + }; + }(7))(); + } + expect: { + new function() { + this.x = 7, + console.log(this); + }(); + } + expect_stdout: true +} + +issue_1288_side_effects: { + options = { + conditionals: true, + evaluate: true, + inline: true, + negate_iife: true, + reduce_funcs: true, + reduce_vars: true, + side_effects: true, + unused: true, + } + input: { + if (w) ; + else { + (function f() {})(); + } + if (!x) { + (function() { + x = {}; + })(); + } + if (y) + (function() {})(); + else + (function(z) { + return z; + })(0); + } + expect: { + w; + x || (x = {}); + y; + } +} + +inner_var_for_in_1: { + options = { + evaluate: true, + inline: true, + reduce_funcs: true, + reduce_vars: true, + } + input: { + function f() { + var a = 1, b = 2; + for (b in (function() { + return x(a, b, c); + })()) { + var c = 3, d = 4; + x(a, b, c, d); + } + x(a, b, c, d); + } + } + expect: { + function f() { + var a = 1, b = 2; + for (b in x(1, b, c)) { + var c = 3, d = 4; + x(1, b, c, d); + } + x(1, b, c, d); + } + } +} + +issue_1595_3: { + options = { + evaluate: true, + inline: true, + passes: 2, + reduce_funcs: true, + reduce_vars: true, + unused: true, + } + input: { + (function f(a) { + return g(a + 1); + })(2); + } + expect: { + g(3); + } +} + +issue_1758: { + options = { + inline: true, + sequences: true, + side_effects: true, + } + input: { + console.log(function(c) { + var undefined = 42; + return function() { + c--; + c--, c.toString(); + return; + }(); + }()); + } + expect: { + console.log(function(c) { + var undefined = 42; + return c--, c--, void c.toString(); + }()); + } + expect_stdout: "undefined" +} +wrap_iife: { + options = { + inline: true, + negate_iife: false, + } + beautify = { + wrap_iife: true, + } + input: { + (function() { + return function() { + console.log('test') + }; + })()(); + } + expect_exact: 'void console.log("test");' +} + +wrap_iife_in_expression: { + options = { + inline: true, + negate_iife: false, + } + beautify = { + wrap_iife: true, + } + input: { + foo = (function() { + return bar(); + })(); + } + expect_exact: 'foo=bar();' +} + +wrap_iife_in_return_call: { + options = { + inline: true, + negate_iife: false, + } + beautify = { + wrap_iife: true, + } + input: { + (function() { + return (function() { + console.log('test') + })(); + })()(); + } + expect_exact: '(void console.log("test"))();' +} + +pure_annotation_1: { + options = { + annotations: true, + inline: true, + side_effects: true, + } + input: { + /*@__PURE__*/(function() { + console.log("hello"); + }()); + } + expect_exact: "" +} + +pure_annotation_2: { + options = { + annotations: true, + collapse_vars: true, + inline: true, + side_effects: true, + } + input: { + /*@__PURE__*/(function(n) { + console.log("hello", n); + }(42)); + } + expect_exact: "" +} + +drop_fargs: { + options = { + collapse_vars: true, + inline: true, + keep_fargs: false, + side_effects: true, + unused: true, + } + input: { + var a = 1; + !function(a_1) { + a++; + }(a++ + (a && console.log(a))); + console.log(a); + } + expect: { + var a = 1; + ++a && console.log(a), + a++; + console.log(a); + } + expect_stdout: [ + "2", + "3", + ] +} + +keep_fargs: { + options = { + collapse_vars: true, + inline: true, + keep_fargs: true, + side_effects: true, + unused: true, + } + input: { + var a = 1; + !function(a_1) { + a++; + }(a++ + (a && console.log(a))); + console.log(a); + } + expect: { + var a = 1; + ++a && console.log(a), + a++; + console.log(a); + } + expect_stdout: [ + "2", + "3", + ] +} diff --git a/test/compress/issue-2871.js b/test/compress/issue-2871.js new file mode 100644 index 00000000000..43c8352b282 --- /dev/null +++ b/test/compress/issue-2871.js @@ -0,0 +1,37 @@ +comparison_with_undefined: { + options = { + comparisons: true, + } + input: { + a == undefined; + a != undefined; + a === undefined; + a !== undefined; + + undefined == a; + undefined != a; + undefined === a; + undefined !== a; + + void 0 == a; + void 0 != a; + void 0 === a; + void 0 !== a; + } + expect: { + null == a; + null != a; + void 0 === a; + void 0 !== a; + + null == a; + null != a; + void 0 === a; + void 0 !== a; + + null == a; + null != a; + void 0 === a; + void 0 !== a; + } +} diff --git a/test/compress/issue-2989.js b/test/compress/issue-2989.js new file mode 100644 index 00000000000..c9066921f25 --- /dev/null +++ b/test/compress/issue-2989.js @@ -0,0 +1,21 @@ +inline_script_off: { + beautify = { + inline_script: false, + } + input: { + console.log(""); + } + expect_exact: 'console.log("");' + expect_stdout: "" +} + +inline_script_on: { + beautify = { + inline_script: true, + } + input: { + console.log(""); + } + expect_exact: 'console.log("<\\/sCrIpT>");' + expect_stdout: "" +} diff --git a/test/compress/issue-368.js b/test/compress/issue-368.js new file mode 100644 index 00000000000..04751c6b887 --- /dev/null +++ b/test/compress/issue-368.js @@ -0,0 +1,55 @@ +collapse: { + options = { + collapse_vars: true, + sequences: true, + side_effects: true, + unused: true, + } + input: { + function f1() { + var a; + a = typeof b === 'function' ? b() : b; + return a !== undefined && c(); + } + function f2(b) { + var a; + b = c(); + a = typeof b === 'function' ? b() : b; + return 'string' == typeof a && d(); + } + function f3(c) { + var a; + a = b(a / 2); + if (a < 0) { + a++; + ++c; + return c / 2; + } + } + function f4(c) { + var a; + a = b(a / 2); + if (a < 0) { + a++; + c++; + return c / 2; + } + } + } + expect: { + function f1() { + return void 0 !== ('function' === typeof b ? b() : b) && c(); + } + function f2(b) { + return 'string' == typeof ('function' === typeof (b = c()) ? b() : b) && d(); + } + function f3(c) { + var a; + if ((a = b(a / 2)) < 0) return a++, ++c / 2; + } + function f4(c) { + var a; + if ((a = b(a / 2)) < 0) return a++, ++c / 2; + } + } +} diff --git a/test/compress/issue-3768.js b/test/compress/issue-3768.js new file mode 100644 index 00000000000..c467fa6494b --- /dev/null +++ b/test/compress/issue-3768.js @@ -0,0 +1,128 @@ +mangle: { + mangle = { + toplevel: true, + } + input: { + var e = eval, x = 42; + (function() { + console.log(e("typeof x")); + })(); + } + expect: { + var e = eval, x = 42; + (function() { + console.log(e("typeof x")); + })(); + } + expect_stdout: true +} + +compress: { + options = { + collapse_vars: true, + inline: true, + unused: true, + } + input: { + console.log(function() { + var a = 42; + return eval("typeof a"); + }(), function(e) { + var a = null; + return e("typeof a"); + }(eval), function(eval) { + var a = false; + return eval("typeof a"); + }(eval), function(f) { + var a = "STRING"; + var eval = f; + return eval("typeof a"); + }(eval), function(g) { + var a = eval; + function eval() { + return g; + } + return eval()("typeof a"); + }(eval)); + } + expect: { + console.log(function() { + var a = 42; + return eval("typeof a"); + }(), (0, eval)("typeof a"), function(eval) { + var a = false; + return eval("typeof a"); + }(eval), function(f) { + var a = "STRING"; + var eval = f; + return eval("typeof a"); + }(eval), function(g) { + var a = eval; + function eval() { + return g; + } + return eval()("typeof a"); + }(eval)); + } + expect_stdout: "number undefined boolean string undefined" +} + +call_arg_1: { + mangle = { + toplevel: true, + } + input: { + var z = "foo"; + (function() { + var z = false; + (function(e) { + var z = 42; + e("console.log(typeof z)"); + })(eval); + })(); + } + expect: { + var z = "foo"; + (function() { + var o = false; + (function(o) { + var a = 42; + o("console.log(typeof z)"); + })(eval); + })(); + } + expect_stdout: true +} + +call_arg_2: { + mangle = { + toplevel: true, + } + input: { + function eval() { + console.log("PASS"); + } + var z = "foo"; + (function() { + var z = false; + (function(e) { + var z = 42; + e("console.log(typeof z)"); + })(eval); + })(); + } + expect: { + function n() { + console.log("PASS"); + } + var o = "foo"; + (function() { + var o = false; + (function(o) { + var n = 42; + o("console.log(typeof z)"); + })(n); + })(); + } + expect_stdout: "PASS" +} diff --git a/test/compress/issue-44.js b/test/compress/issue-44.js index 7a972f9eaf0..958e525e6ca 100644 --- a/test/compress/issue-44.js +++ b/test/compress/issue-44.js @@ -1,5 +1,7 @@ issue_44_valid_ast_1: { - options = { unused: true }; + options = { + unused: true, + } input: { function a(b) { for (var i = 0, e = b.qoo(); ; i++) {} @@ -14,7 +16,9 @@ issue_44_valid_ast_1: { } issue_44_valid_ast_2: { - options = { unused: true }; + options = { + unused: true, + } input: { function a(b) { if (foo) for (var i = 0, e = b.qoo(); ; i++) {} diff --git a/test/compress/issue-5614.js b/test/compress/issue-5614.js new file mode 100644 index 00000000000..bdf6bfe17aa --- /dev/null +++ b/test/compress/issue-5614.js @@ -0,0 +1,195 @@ +record_update: { + options = { + loops: true, + passes: 3, + pure_getters: "strict", + reduce_vars: true, + side_effects: true, + toplevel: true, + unused: true, + } + input: { + var value = { a: 42, b: "PASS" }; + var unused = _Utils_update(value, { b: "FAIL" }); + function _Utils_update(oldRecord, updatedFields) { + var newRecord = {}; + for (var key in oldRecord) + newRecord[key] = oldRecord[key]; + for (var key in updatedFields) + newRecord[key] = updatedFields[key]; + return newRecord; + } + } + expect: {} +} + +currying: { + options = { + inline: true, + passes: 2, + pure_getters: "strict", + reduce_funcs: true, + reduce_vars: true, + sequences: true, + side_effects: true, + toplevel: true, + unused: true, + } + input: { + function F(arity, fun, wrapper) { + wrapper.a = arity; + wrapper.f = fun; + return wrapper; + } + function F2(fun) { + return F(2, fun, function(a) { + return function(b) { + return fun(a, b); + }; + }); + } + function _Utils_eq(x, y) { + var pair, stack = [], isEqual = _Utils_eqHelp(x, y, 0, stack); + while (isEqual && (pair = stack.pop())) + isEqual = _Utils_eqHelp(pair.a, pair.b, 0, stack); + return isEqual; + } + var _Utils_equal = F2(_Utils_eq); + } + expect: {} +} + +conditional_property_write: { + options = { + pure_getters: "strict", + reduce_vars: true, + unused: true, + } + input: { + function f(a) { + var o = {}; + if (a) + o.p = console.log("foo"); + else + o.q = console.log("bar"); + o.r = console.log("baz"); + } + f(42); + f(null); + } + expect: { + function f(a) { + if (a) + console.log("foo"); + else + console.log("bar"); + console.log("baz"); + } + f(42); + f(null); + } + expect_stdout: [ + "foo", + "baz", + "bar", + "baz", + ] +} + +reassign_1: { + options = { + reduce_vars: true, + toplevel: true, + unused: true, + } + input: { + var a = "PASS", b = "FAIL"; + (b = a).toString(); + console.log(b); + } + expect: { + var b = "FAIL"; + (b = "PASS").toString(); + console.log(b); + } + expect_stdout: "PASS" +} + +reassign_2: { + options = { + evaluate: true, + reduce_vars: true, + toplevel: true, + } + input: { + var a = "PASS"; + if (false) { + a = null + 0; + a(); + } + console.log(a); + } + expect: { + var a = "PASS"; + if (false) { + a = 0; + a(); + } + console.log(a); + } + expect_stdout: "PASS" +} + +reassign_3: { + options = { + evaluate: true, + reduce_vars: true, + toplevel: true, + } + input: { + var a = 0; + (a = a || "PASS").toString(); + console.log(a); + } + expect: { + var a = 0; + (a = (0, "PASS")).toString(); + console.log(a); + } + expect_stdout: "PASS" +} + +retain_instance_write: { + options = { + pure_getters: true, + reduce_vars: true, + unused: true, + } + input: { + function f(a) { + return a; + } + function g() { + var o = {}; + var b = new f(o); + if (console) + b.p = "PASS"; + return o; + } + console.log(g().p); + } + expect: { + function f(a) { + return a; + } + function g() { + var o = {}; + var b = new f(o); + if (console) + b.p = "PASS"; + return o; + } + console.log(g().p); + } + expect_stdout: "PASS" +} diff --git a/test/compress/issue-59.js b/test/compress/issue-59.js index 82b388067d0..4e2411fd077 100644 --- a/test/compress/issue-59.js +++ b/test/compress/issue-59.js @@ -1,8 +1,8 @@ keep_continue: { options = { dead_code: true, - evaluate: true - }; + evaluate: true, + } input: { while (a) { if (b) { diff --git a/test/compress/issue-597.js b/test/compress/issue-597.js index 3c651a4c647..c988e9c1169 100644 --- a/test/compress/issue-597.js +++ b/test/compress/issue-597.js @@ -1,17 +1,17 @@ NaN_and_Infinity_must_have_parens: { - options = {}; + options = {} input: { Infinity.toString(); NaN.toString(); } expect: { (1/0).toString(); - (0/0).toString(); + NaN.toString(); } } NaN_and_Infinity_should_not_be_replaced_when_they_are_redefined: { - options = {}; + options = {} input: { var Infinity, NaN; Infinity.toString(); @@ -23,3 +23,135 @@ NaN_and_Infinity_should_not_be_replaced_when_they_are_redefined: { NaN.toString(); } } + +NaN_and_Infinity_must_have_parens_evaluate: { + options = { + evaluate: true, + } + input: { + (123456789 / 0).toString(); + (+"foo").toString(); + } + expect: { + (1/0).toString(); + NaN.toString(); + } +} + +NaN_and_Infinity_should_not_be_replaced_when_they_are_redefined_evaluate: { + options = { + evaluate: true, + } + input: { + var Infinity, NaN; + (123456789 / 0).toString(); + (+"foo").toString(); + } + expect: { + var Infinity, NaN; + (1/0).toString(); + (0/0).toString(); + } +} + +beautify_off_1: { + options = { + evaluate: true, + } + beautify = { + beautify: false, + } + input: { + var NaN; + console.log( + null, + undefined, + Infinity, + NaN, + Infinity * undefined, + Infinity.toString(), + NaN.toString(), + (Infinity * undefined).toString() + ); + } + expect_exact: "var NaN;console.log(null,void 0,1/0,NaN,0/0,(1/0).toString(),NaN.toString(),(0/0).toString());" + expect_stdout: true +} + +beautify_off_2: { + options = { + evaluate: true, + } + beautify = { + beautify: false, + } + input: { + console.log( + null.toString(), + undefined.toString() + ); + } + expect_exact: "console.log(null.toString(),(void 0).toString());" +} + +beautify_on_1: { + options = { + evaluate: true, + } + beautify = { + beautify: true, + } + input: { + var NaN; + console.log( + null, + undefined, + Infinity, + NaN, + Infinity * undefined, + Infinity.toString(), + NaN.toString(), + (Infinity * undefined).toString() + ); + } + expect_exact: [ + "var NaN;", + "", + "console.log(null, void 0, 1 / 0, NaN, 0 / 0, (1 / 0).toString(), NaN.toString(), (0 / 0).toString());", + ] + expect_stdout: true +} + +beautify_on_2: { + options = { + evaluate: true, + } + beautify = { + beautify: true, + } + input: { + console.log( + null.toString(), + undefined.toString() + ); + } + expect_exact: "console.log(null.toString(), (void 0).toString());" +} + +issue_1724: { + input: { + var a = 0; + ++a % Infinity | Infinity ? a++ : 0; + console.log(a); + } + expect_exact: "var a=0;++a%(1/0)|1/0?a++:0;console.log(a);" + expect_stdout: "2" +} + +issue_1725: { + input: { + ([].length === 0) % Infinity ? console.log("PASS") : console.log("FAIL"); + } + expect_exact: '(0===[].length)%(1/0)?console.log("PASS"):console.log("FAIL");' + expect_stdout: "PASS" +} diff --git a/test/compress/issue-611.js b/test/compress/issue-611.js index 6f2c65d2df4..f1f2bf9c5ad 100644 --- a/test/compress/issue-611.js +++ b/test/compress/issue-611.js @@ -1,8 +1,8 @@ issue_611: { options = { - sequences: true, - side_effects: true - }; + sequences: true, + side_effects: true, + } input: { define(function() { function fn() {} diff --git a/test/compress/issue-637.js b/test/compress/issue-637.js new file mode 100644 index 00000000000..e39c69fae11 --- /dev/null +++ b/test/compress/issue-637.js @@ -0,0 +1,21 @@ +wrongly_optimized: { + options = { + booleans: true, + conditionals: true, + evaluate: true, + } + input: { + function func() { + foo(); + } + if (func() || true) { + bar(); + } + } + expect: { + function func() { + foo(); + } + func(), 1, bar(); + } +} diff --git a/test/compress/issue-640.js b/test/compress/issue-640.js new file mode 100644 index 00000000000..8c893de8b09 --- /dev/null +++ b/test/compress/issue-640.js @@ -0,0 +1,357 @@ +cond_5: { + options = { + conditionals: true, + expression: true, + } + input: { + if (some_condition()) { + if (some_other_condition()) { + do_something(); + } else { + alternate(); + } + } else { + alternate(); + } + + if (some_condition()) { + if (some_other_condition()) { + do_something(); + } + } + } + expect: { + (some_condition() && some_other_condition() ? do_something : alternate)(); + if (some_condition() && some_other_condition()) do_something(); + } +} + +dead_code_const_annotation_regex: { + options = { + booleans: true, + conditionals: true, + dead_code: true, + evaluate: true, + expression: true, + loops: true, + } + input: { + var unused; + // @constraint this shouldn't be a constant + var CONST_FOO_ANN = false; + if (CONST_FOO_ANN) { + console.log("reachable"); + } + } + expect: { + var unused; + var CONST_FOO_ANN = !1; + if (CONST_FOO_ANN) console.log('reachable'); + } + expect_stdout: true +} + +drop_console_2: { + options = { + drop_console: true, + expression: true, + } + input: { + console.log('foo'); + console.log.apply(console, arguments); + } + expect: { + // with regular compression these will be stripped out as well + void 0; + void 0; + } +} + +drop_value: { + expression = true + options = { + expression: true, + side_effects: true, + } + input: { + (1, [2, foo()], 3, {a:1, b:bar()}); + } + expect: { + foo(), {a:1, b:bar()}; + } +} + +wrongly_optimized: { + options = { + booleans: true, + conditionals: true, + dead_code: true, + evaluate: true, + expression: true, + } + input: { + function func() { + foo(); + } + if (func() || true) { + bar(); + } + } + expect: { + function func() { + foo(); + } + func(), 1; + bar(); + } +} + +negate_iife_1: { + expression = true + options = { + expression: true, + negate_iife: true, + } + input: { + (function(){ stuff() })(); + } + expect: { + (function(){ stuff() })(); + } +} + +negate_iife_3: { + expression = true + options = { + conditionals: true, + expression: true, + negate_iife: true, + } + input: { + (function(){ return t })() ? console.log(true) : console.log(false); + } + expect: { + (function(){ return t })() ? console.log(true) : console.log(false); + } +} + +negate_iife_3_off: { + expression = true + options = { + conditionals: true, + expression: true, + negate_iife: false, + } + input: { + (function(){ return t })() ? console.log(true) : console.log(false); + } + expect: { + (function(){ return t })() ? console.log(true) : console.log(false); + } +} + +negate_iife_4: { + options = { + conditionals: true, + expression: true, + negate_iife: true, + sequences: true, + } + input: { + (function(){ return t })() ? console.log(true) : console.log(false); + (function(){ + console.log("something"); + })(); + } + expect: { + !function(){ return t }() ? console.log(false) : console.log(true), function(){ + console.log("something"); + }(); + } +} + +negate_iife_5: { + options = { + conditionals: true, + expression: true, + negate_iife: true, + sequences: true, + } + input: { + if ((function(){ return t })()) { + foo(true); + } else { + bar(false); + } + (function(){ + console.log("something"); + })(); + } + expect: { + !function(){ return t }() ? bar(false) : foo(true), function(){ + console.log("something"); + }(); + } +} + +negate_iife_5_off: { + options = { + conditionals: true, + expression: true, + negate_iife: false, + sequences: true, + } + input: { + if ((function(){ return t })()) { + foo(true); + } else { + bar(false); + } + (function(){ + console.log("something"); + })(); + } + expect: { + !function(){ return t }() ? bar(false) : foo(true), function(){ + console.log("something"); + }(); + } +} + +issue_1254_negate_iife_true: { + expression = true + options = { + expression: true, + negate_iife: true, + } + input: { + (function() { + return function() { + console.log('test') + }; + })()(); + } + expect_exact: 'function(){return function(){console.log("test")}}()()' + expect_stdout: true +} + +issue_1254_negate_iife_nested: { + expression = true + options = { + expression: true, + negate_iife: true, + } + input: { + (function() { + return function() { + console.log('test') + }; + })()()()()(); + } + expect_exact: 'function(){return function(){console.log("test")}}()()()()()' + expect_stdout: true +} + +conditional: { + options = { + expression: true, + pure_funcs: [ + "pure" + ], + side_effects: true, + } + input: { + pure(1 | a() ? 2 & b() : 7 ^ c()); + pure(1 | a() ? 2 & b() : 5); + pure(1 | a() ? 4 : 7 ^ c()); + pure(1 | a() ? 4 : 5); + pure(3 ? 2 & b() : 7 ^ c()); + pure(3 ? 2 & b() : 5); + pure(3 ? 4 : 7 ^ c()); + pure(3 ? 4 : 5); + } + expect: { + 1 | a() ? b() : c(); + 1 | a() && b(); + 1 | a() || c(); + a(); + 3 ? b() : c(); + 3 && b(); + 3 || c(); + pure(3 ? 4 : 5); + } +} + +limit_1: { + options = { + expression: true, + sequences: 3, + } + input: { + a; + b; + c; + d; + e; + f; + g; + h; + i; + j; + k; + } + expect: { + // Turned into a single return statement + // so it can no longer be split into lines + a,b,c,d,e,f,g,h,i,j,k; + } +} + +iife: { + options = { + expression: true, + sequences: true, + } + input: { + x = 42; + (function a() {})(); + !function b() {}(); + ~function c() {}(); + +function d() {}(); + -function e() {}(); + void function f() {}(); + typeof function g() {}(); + } + expect: { + x = 42, + function a() {}(), + !function b() {}(), + ~function c() {}(), + +function d() {}(), + -function e() {}(), + void function f() {}(), + typeof function g() {}(); + } +} + +iife_drop_side_effect_free: { + options = { + expression: true, + sequences: true, + side_effects: true, + } + input: { + x = 42; + (function a() {})(); + !function b() {}(); + ~function c() {}(); + +function d() {}(); + -function e() {}(); + void function f() {}(); + typeof function g() {}(); + } + expect: { + x = 42, + typeof void 0; + } +} diff --git a/test/compress/issue-747.js b/test/compress/issue-747.js new file mode 100644 index 00000000000..8de43d62f0b --- /dev/null +++ b/test/compress/issue-747.js @@ -0,0 +1,57 @@ +dont_reuse_prop: { + mangle = { + properties: { + domprops: true, + regex: /asd/, + }, + } + input: { + "aaaaaaaaaabbbbb"; + var obj = {}; + obj.a = 123; + obj.asd = 256; + console.log(obj.a); + } + expect: { + "aaaaaaaaaabbbbb"; + var obj = {}; + obj.a = 123; + obj.b = 256; + console.log(obj.a); + } + expect_stdout: "123" + expect_warnings: [ + "INFO: Preserving excluded property a", + "INFO: Mapping property asd to b", + "INFO: Preserving reserved property log", + ] +} + +unmangleable_props_should_always_be_reserved: { + mangle = { + properties: { + domprops: true, + regex: /asd/, + }, + } + input: { + "aaaaaaaaaabbbbb"; + var obj = {}; + obj.asd = 256; + obj.a = 123; + console.log(obj.a); + } + expect: { + "aaaaaaaaaabbbbb"; + var obj = {}; + obj.b = 256; + obj.a = 123; + console.log(obj.a); + } + expect_stdout: "123" + expect_warnings: [ + "INFO: Preserving excluded property a", + "INFO: Mapping property asd to b", + "INFO: Preserving reserved property log", + ] +} diff --git a/test/compress/issue-751.js b/test/compress/issue-751.js new file mode 100644 index 00000000000..234eee35724 --- /dev/null +++ b/test/compress/issue-751.js @@ -0,0 +1,29 @@ +negate_booleans_1: { + options = { + comparisons: true, + } + input: { + var a = !a || !b || !c || !d || !e || !f; + } + expect: { + var a = !(a && b && c && d && e && f); + } +} + +negate_booleans_2: { + options = { + comparisons: true, + } + input: { + var match = !x && // should not touch this one + (!z || c) && + (!k || d) && + the_stuff(); + } + expect: { + var match = !x && + (!z || c) && + (!k || d) && + the_stuff(); + } +} diff --git a/test/compress/issue-782.js b/test/compress/issue-782.js new file mode 100644 index 00000000000..34baf5ff395 --- /dev/null +++ b/test/compress/issue-782.js @@ -0,0 +1,51 @@ +remove_sequence: { + options = { + side_effects: true, + } + input: { + (0, 1, eval)(); + (0, 1, logThis)(); + (0, 1, _decorators.logThis)(); + } + expect: { + (0, eval)(); + logThis(); + (0, _decorators.logThis)(); + } +} + +remove_redundant_sequence_items: { + options = { + side_effects: true, + } + input: { + "use strict"; + (0, 1, eval)(); + (0, 1, logThis)(); + (0, 1, _decorators.logThis)(); + } + expect: { + "use strict"; + (0, eval)(); + logThis(); + (0, _decorators.logThis)(); + } +} + +dont_remove_this_binding_sequence: { + options = { + side_effects: true, + } + input: { + "use strict"; + (0, eval)(); + (0, logThis)(); + (0, _decorators.logThis)(); + } + expect: { + "use strict"; + (0, eval)(); + logThis(); + (0, _decorators.logThis)(); + } +} diff --git a/test/compress/issue-892.js b/test/compress/issue-892.js new file mode 100644 index 00000000000..53744a312c8 --- /dev/null +++ b/test/compress/issue-892.js @@ -0,0 +1,31 @@ +dont_mangle_arguments: { + options = { + booleans: true, + comparisons: true, + conditionals: true, + dead_code: true, + drop_debugger: true, + evaluate: true, + hoist_funs: true, + hoist_vars: true, + if_return: true, + join_vars: true, + keep_fargs: true, + keep_fnames: false, + loops: true, + negate_iife: false, + properties: true, + sequences: true, + side_effects: true, + unused: true, + } + mangle = {} + input: { + (function(){ + var arguments = arguments, not_arguments = 9; + console.log(not_arguments, arguments); + })(5, 6, 7); + } + expect_exact: "(function(){var arguments,o=9;console.log(o,arguments)})(5,6,7);" + expect_stdout: true +} diff --git a/test/compress/issue-913.js b/test/compress/issue-913.js new file mode 100644 index 00000000000..db1127cf291 --- /dev/null +++ b/test/compress/issue-913.js @@ -0,0 +1,21 @@ +keep_var_for_in: { + options = { + hoist_vars: true, + join_vars: true, + unused: true, + } + input: { + (function(obj) { + var foo = 5; + for (var i in obj) + return foo; + })(); + } + expect: { + (function(obj) { + var i, foo = 5; + for (i in obj) + return foo; + })(); + } +} diff --git a/test/compress/issue-973.js b/test/compress/issue-973.js new file mode 100644 index 00000000000..93071107075 --- /dev/null +++ b/test/compress/issue-973.js @@ -0,0 +1,163 @@ +this_binding_conditionals: { + options = { + conditionals: true, + evaluate: true, + side_effects: true, + } + input: { + "use strict"; + (1 && a)(); + (0 || a)(); + (0 || 1 && a)(); + (1 ? a : 0)(); + + (1 && a.b)(); + (0 || a.b)(); + (0 || 1 && a.b)(); + (1 ? a.b : 0)(); + + (1 && a[b])(); + (0 || a[b])(); + (0 || 1 && a[b])(); + (1 ? a[b] : 0)(); + + (1 && eval)(); + (0 || eval)(); + (0 || 1 && eval)(); + (1 ? eval : 0)(); + } + expect: { + "use strict"; + a(); + a(); + a(); + a(); + + (0, a.b)(); + (0, a.b)(); + (0, a.b)(); + (0, a.b)(); + + (0, a[b])(); + (0, a[b])(); + (0, a[b])(); + (0, a[b])(); + + (0, eval)(); + (0, eval)(); + (0, eval)(); + (0, eval)(); + } +} + +this_binding_collapse_vars: { + options = { + collapse_vars: true, + unused: true, + } + input: { + function f() { + "use strict"; + var c = a; c(); + var d = a.b; d(); + var e = eval; e(); + } + } + expect: { + function f() { + "use strict"; + a(); + (0, a.b)(); + (0, eval)(); + } + } +} + +this_binding_side_effects: { + options = { + side_effects: true, + } + input: { + (function(foo) { + (0, foo)(); + (0, foo.bar)(); + (0, eval)("console.log(foo);"); + }()); + (function(foo) { + "use strict"; + (0, foo)(); + (0, foo.bar)(); + (0, eval)("console.log(foo);"); + }()); + (function(foo) { + var eval = console; + (0, foo)(); + (0, foo.bar)(); + (0, eval)("console.log(foo);"); + }()); + } + expect: { + (function(foo) { + foo(); + (0, foo.bar)(); + (0, eval)("console.log(foo);"); + }()); + (function(foo) { + "use strict"; + foo(); + (0, foo.bar)(); + (0, eval)("console.log(foo);"); + }()); + (function(foo) { + var eval = console; + foo(); + (0, foo.bar)(); + eval("console.log(foo);"); + }()); + } +} + +this_binding_sequences: { + options = { + sequences: true, + side_effects: true, + } + input: { + console.log(typeof function() { + return eval("this"); + }()); + console.log(typeof function() { + "use strict"; + return eval("this"); + }()); + console.log(typeof function() { + return (0, eval)("this"); + }()); + console.log(typeof function() { + "use strict"; + return (0, eval)("this"); + }()); + } + expect: { + console.log(typeof function() { + return eval("this"); + }()), + console.log(typeof function() { + "use strict"; + return eval("this"); + }()), + console.log(typeof function() { + return (0, eval)("this"); + }()), + console.log(typeof function() { + "use strict"; + return (0, eval)("this"); + }()); + } + expect_stdout: [ + "object", + "undefined", + "object", + "object", + ] +} diff --git a/test/compress/issue-976.js b/test/compress/issue-976.js new file mode 100644 index 00000000000..435335018a9 --- /dev/null +++ b/test/compress/issue-976.js @@ -0,0 +1,143 @@ +eval_collapse_vars: { + options = { + booleans: true, + collapse_vars: true, + comparisons: true, + conditionals: true, + dead_code: true, + evaluate: true, + hoist_funs: true, + if_return: true, + join_vars: true, + keep_fargs: true, + loops: true, + properties: true, + sequences: false, + side_effects: true, + unused: true, + } + input: { + function f1() { + var e = 7; + var s = "abcdef"; + var i = 2; + var eval = console.log.bind(console); + var x = s.charAt(i++); + var y = s.charAt(i++); + var z = s.charAt(i++); + eval(x, y, z, e); + } + function p1() { var a = foo(), b = bar(), eval = baz(); return a + b + eval; } + function p2() { var a = foo(), b = bar(), eval = baz; return a + b + eval(); } + (function f2(eval) { + var a = 2; + console.log(a - 5); + eval("console.log(a);"); + })(eval); + } + expect: { + function f1() { + var e = 7, + s = "abcdef", + i = 2, + eval = console.log.bind(console), + x = s.charAt(i++), + y = s.charAt(i++), + z = s.charAt(i++); + eval(x, y, z, e); + } + function p1() { return foo() + bar() + baz(); } + function p2() { var a = foo(), b = bar(), eval = baz; return a + b + eval(); } + (function f2(eval) { + var a = 2; + console.log(a - 5); + eval("console.log(a);"); + })(eval); + } + expect_stdout: true +} + +eval_unused: { + options = { + keep_fargs: false, + unused: true, + } + input: { + function o(k) { + return { c: 14 }[k]; + } + console.log(function f1(a, eval, c, d, e) { + return a("c") + eval; + }(o, 28, true)); + console.log(function f2(a, b, c, d, e) { + return a + eval("c"); + }(14, true, 28)); + console.log(function f3(a, eval, c, d, e) { + return a + eval("c"); + }(28, o, true)); + } + expect: { + function o(k) { + return { c: 14 }[k]; + } + console.log(function(a, eval) { + return a("c") + eval; + }(o, 28)); + console.log(function f2(a, b, c, d, e) { + return a + eval("c"); + }(14, true, 28)); + console.log(function f3(a, eval, c, d, e) { + return a + eval("c"); + }(28, o, true)); + } + expect_stdout: [ + "42", + "42", + "42", + ] +} + +eval_mangle: { + mangle = {} + beautify = { + beautify: true, + } + input: { + function o(k) { + return { cc: 14 }[k + "c"]; + } + console.log(function f1(a, eval, c, d, e) { + return a("c") + eval; + }(o, 28, true)); + console.log(function f2(a, b, c, d, e) { + return a + eval("c"); + }(14, true, 28)); + console.log(function f3(a, eval, c, d, e) { + return a + eval("c"); + }(28, o, true)); + } + expect_exact: [ + "function o(o) {", + " return {", + " cc: 14", + ' }[o + "c"];', + "}", + "", + "console.log(function o(c, e, n, r, t) {", + ' return c("c") + e;', + "}(o, 28, true));", + "", + "console.log(function f2(a, b, c, d, e) {", + ' return a + eval("c");', + "}(14, true, 28));", + "", + "console.log(function f3(a, eval, c, d, e) {", + ' return a + eval("c");', + "}(28, o, true));", + ] + expect_stdout: [ + "42", + "42", + "42", + ] +} diff --git a/test/compress/issue-979.js b/test/compress/issue-979.js new file mode 100644 index 00000000000..d752ff9fdb8 --- /dev/null +++ b/test/compress/issue-979.js @@ -0,0 +1,102 @@ +reported: { + options = { + booleans: true, + comparisons: true, + conditionals: true, + dead_code: true, + evaluate: true, + hoist_funs: true, + if_return: true, + join_vars: true, + keep_fargs: true, + loops: true, + properties: true, + sequences: true, + side_effects: true, + unused: true, + } + input: { + function f1() { + if (a == 1 || b == 2) + foo(); + } + function f2() { + if (!(a == 1 || b == 2)); + else + foo(); + } + } + expect: { + function f1() { + 1 != a && 2 != b || foo(); + } + function f2() { + 1 != a && 2 != b || foo(); + } + } +} + +test_negated_is_best: { + options = { + booleans: true, + comparisons: true, + conditionals: true, + dead_code: true, + evaluate: true, + hoist_funs: true, + if_return: true, + join_vars: true, + keep_fargs: true, + loops: true, + properties: true, + sequences: true, + side_effects: true, + unused: true, + } + input: { + function f3() { + if (a == 1 | b == 2) + foo(); + } + function f4() { + if (!(a == 1 | b == 2)); + else + foo(); + } + function f5() { + if (a == 1 && b == 2) + foo(); + } + function f6() { + if (!(a == 1 && b == 2)); + else + foo(); + } + function f7() { + if (a == 1 || b == 2) + foo(); + else + return bar(); + } + } + expect: { + function f3() { + 1 == a | 2 == b && foo(); + } + function f4() { + 1 == a | 2 == b && foo(); + } + function f5() { + 1 == a && 2 == b && foo(); + } + function f6() { + 1 == a && 2 == b && foo(); + } + function f7() { + if (1 != a && 2 != b) + return bar(); + foo(); + } + } +} + diff --git a/test/compress/join_vars.js b/test/compress/join_vars.js new file mode 100644 index 00000000000..8e4445ec3a5 --- /dev/null +++ b/test/compress/join_vars.js @@ -0,0 +1,1396 @@ +join_vars_assign: { + options = { + join_vars: true, + unused: true, + } + input: { + var y, x; + x = Object("PAS"); + y = Object("S"); + console.log(x + y); + } + expect: { + var x = Object("PAS"), y = Object("S"); + console.log(x + y); + } + expect_stdout: "PASS" +} + +join_object_assignments_1: { + options = { + evaluate: true, + join_vars: true, + } + input: { + console.log(function() { + var x = { + a: 1, + c: (console.log("c"), "C"), + }; + x.b = 2; + x[3] = function() { + console.log(x); + }, + x["a"] = /foo/, + x.bar = x; + return x; + }()); + } + expect: { + console.log(function() { + var x = { + a: 1, + c: (console.log("c"), "C"), + b: 2, + 3: function() { + console.log(x); + }, + a: /foo/, + }; + x.bar = x; + return x; + }()); + } + expect_stdout: true +} + +join_object_assignments_2: { + options = { + evaluate: true, + hoist_props: true, + join_vars: true, + passes: 3, + reduce_vars: true, + toplevel: true, + unused: true, + } + input: { + var o = { + foo: 1, + }; + o.bar = 2; + o.baz = 3; + console.log(o.foo, o.bar + o.bar, o.foo * o.bar * o.baz); + } + expect: { + console.log(1, 4, 6); + } + expect_stdout: "1 4 6" +} + +join_object_assignments_3: { + options = { + evaluate: true, + join_vars: true, + } + input: { + console.log(function() { + var o = { + a: "PASS", + }, a = o.a; + o.a = "FAIL"; + return a; + }()); + } + expect: { + console.log(function() { + var o = { + a: "PASS", + }, a = o.a; + o.a = "FAIL"; + return a; + }()); + } + expect_stdout: "PASS" +} + +join_object_assignments_4: { + options = { + join_vars: true, + sequences: true, + } + input: { + var o; + console.log(o); + o = {}; + o.a = "foo"; + console.log(o.b); + o.b = "bar"; + console.log(o.a); + } + expect: { + var o; + console.log(o), + o = { + a: "foo", + }, + console.log(o.b), + o.b = "bar", + console.log(o.a); + } + expect_stdout: [ + "undefined", + "undefined", + "foo", + ] +} + +join_object_assignments_return_1: { + options = { + join_vars: true, + } + input: { + console.log(function() { + var o = { + p: 3 + }; + return o.q = "foo"; + }()); + } + expect: { + console.log(function() { + var o = { + p: 3, + q: "foo" + }; + return o.q; + }()); + } + expect_stdout: "foo" +} + +join_object_assignments_return_2: { + options = { + join_vars: true, + } + input: { + console.log(function() { + var o = { + p: 3 + }; + return o.q = /foo/, + o.r = "bar"; + }()); + } + expect: { + console.log(function() { + var o = { + p: 3, + q: /foo/, + r: "bar" + }; + return o.r; + }()); + } + expect_stdout: "bar" +} + +join_object_assignments_return_3: { + options = { + join_vars: true, + } + input: { + console.log(function() { + var o = { + p: 3 + }; + return o.q = "foo", + o.p += "", + console.log(o.q), + o.p; + }()); + } + expect: { + console.log(function() { + var o = { + p: 3, + q: "foo" + }; + return o.p += "", + console.log(o.q), + o.p; + }()); + } + expect_stdout: [ + "foo", + "3", + ] +} + +join_object_assignments_for: { + options = { + join_vars: true, + } + input: { + console.log(function() { + var o = { + p: 3 + }; + for (o.q = "foo"; console.log(o.q);); + return o.p; + }()); + } + expect: { + console.log(function() { + for (var o = { + p: 3, + q: "foo" + }; console.log(o.q);); + return o.p; + }()); + } + expect_stdout: [ + "foo", + "3", + ] +} + +join_object_assignments_if: { + options = { + join_vars: true, + } + input: { + console.log(function() { + var o = {}; + if (o.a = "PASS") return o.a; + }()) + } + expect: { + console.log(function() { + var o = { a: "PASS" }; + if (o.a) return o.a; + }()); + } + expect_stdout: "PASS" +} + +join_object_assignments_forin: { + options = { + join_vars: true, + } + input: { + console.log(function() { + var o = {}; + for (var a in o.a = "PASS", o) + return o[a]; + }()) + } + expect: { + console.log(function() { + var o = { a: "PASS" }, a; + for (a in o) + return o[a]; + }()); + } + expect_stdout: "PASS" +} + +join_object_assignments_negative: { + options = { + evaluate: true, + join_vars: true, + properties: true, + } + input: { + var o = {}; + o[0] = 0; + o[-0] = 1; + o[-1] = 2; + console.log(o[0], o[-0], o[-1]); + } + expect: { + var o = { + 0: 0, + 0: 1, + "-1": 2 + }; + console.log(o[0], o[-0], o[-1]); + } + expect_stdout: "1 1 2" +} + +join_object_assignments_NaN_1: { + options = { + join_vars: true, + } + input: { + var o = {}; + o[NaN] = 1; + o[0/0] = 2; + console.log(o[NaN], o[NaN]); + } + expect: { + var o = {}; + o[NaN] = 1; + o[0/0] = 2; + console.log(o[NaN], o[NaN]); + } + expect_stdout: "2 2" +} + +join_object_assignments_NaN_2: { + options = { + evaluate: true, + join_vars: true, + properties: true, + } + input: { + var o = {}; + o[NaN] = 1; + o[0/0] = 2; + console.log(o[NaN], o[NaN]); + } + expect: { + var o = { + NaN: 1, + NaN: 2 + }; + console.log(o.NaN, o.NaN); + } + expect_stdout: "2 2" +} + +join_object_assignments_null_0: { + options = { + join_vars: true, + } + input: { + var o = {}; + o[null] = 1; + console.log(o[null]); + } + expect: { + var o = {}; + o[null] = 1; + console.log(o[null]); + } + expect_stdout: "1" +} + +join_object_assignments_null_1: { + options = { + evaluate: true, + join_vars: true, + properties: true, + } + input: { + var o = {}; + o[null] = 1; + console.log(o[null]); + } + expect: { + var o = { + null: 1 + }; + console.log(o.null); + } + expect_stdout: "1" +} + +join_object_assignments_void_0: { + options = { + evaluate: true, + join_vars: true, + } + input: { + var o = {}; + o[void 0] = 1; + console.log(o[void 0]); + } + expect: { + var o = { + undefined: 1 + }; + console.log(o[void 0]); + } + expect_stdout: "1" +} + +join_object_assignments_undefined_1: { + options = { + join_vars: true, + } + input: { + var o = {}; + o[undefined] = 1; + console.log(o[undefined]); + } + expect: { + var o = {}; + o[void 0] = 1; + console.log(o[void 0]); + } + expect_stdout: "1" +} + +join_object_assignments_undefined_2: { + options = { + evaluate: true, + join_vars: true, + properties: true, + } + input: { + var o = {}; + o[undefined] = 1; + console.log(o[undefined]); + } + expect: { + var o = { + undefined : 1 + }; + console.log(o[void 0]); + } + expect_stdout: "1" +} + +join_object_assignments_Infinity: { + options = { + evaluate: true, + join_vars: true, + properties: true, + } + input: { + var o = {}; + o[Infinity] = 1; + o[1/0] = 2; + o[-Infinity] = 3; + o[-1/0] = 4; + console.log(o[Infinity], o[1/0], o[-Infinity], o[-1/0]); + } + expect: { + var o = { + Infinity: 1, + Infinity: 2, + "-Infinity": 3, + "-Infinity": 4 + }; + console.log(o[1/0], o[1/0], o[-1/0], o[-1/0]); + } + expect_stdout: "2 2 4 4" +} + +join_object_assignments_regex: { + options = { + evaluate: true, + join_vars: true, + properties: true, + } + input: { + var o = {}; + o[/rx/] = 1; + console.log(o[/rx/]); + } + expect: { + var o = { + "/rx/": 1 + }; + console.log(o[/rx/]); + } + expect_stdout: "1" +} + +chained_assignments: { + options = { + join_vars: true, + } + input: { + var a, b = a = {}; + b.p = "PASS"; + console.log(a.p); + } + expect: { + var a, b = a = { + p: "PASS", + }; + console.log(a.p); + } + expect_stdout: "PASS" +} + +folded_assignments_1: { + options = { + evaluate: true, + join_vars: true, + } + input: { + var a = {}; + a[a.PASS = 42] = "PASS"; + console.log(a[42], a.PASS); + } + expect: { + var a = { + PASS: 42, + 42: "PASS", + }; + console.log(a[42], a.PASS); + } + expect_stdout: "PASS 42" +} + +folded_assignments_2: { + options = { + evaluate: true, + join_vars: true, + } + input: { + "use strict"; + var a = {}; + a[42] = "FAIL"; + a[a.PASS = 42] = "PASS"; + console.log(a[42], a.PASS); + } + expect: { + "use strict"; + var a = { + 42: "FAIL", + PASS: 42, + }; + a[42] = "PASS"; + console.log(a[42], a.PASS); + } + expect_stdout: "PASS 42" +} + +inlined_assignments: { + options = { + join_vars: true, + unused: true, + } + input: { + var a; + (a = {}).p = "PASS"; + console.log(a.p); + } + expect: { + var a = { + p: "PASS", + }; + console.log(a.p); + } + expect_stdout: "PASS" +} + +inline_for: { + options = { + inline: true, + join_vars: true, + reduce_vars: true, + toplevel: true, + unused: true, + } + input: { + var a = function() { + for (; console.log("PASS");); + }; + a(); + } + expect: { + for (; console.log("PASS");); + } + expect_stdout: "PASS" +} + +inline_var: { + options = { + inline: true, + join_vars: true, + reduce_vars: true, + toplevel: true, + unused: true, + } + input: { + A = "PASS"; + var a = function() { + var b = A; + for (b in console.log(b)); + }; + a(); + } + expect: { + A = "PASS"; + var b = A; + for (b in console.log(b)); + } + expect_stdout: "PASS" +} + +typescript_enum: { + rename = true + options = { + assignments: true, + collapse_vars: true, + evaluate: true, + hoist_props: true, + inline: true, + join_vars: true, + passes: 4, + reduce_vars: true, + sequences: true, + side_effects: true, + toplevel: true, + unused: true, + } + input: { + var Enum; + (function (Enum) { + Enum[Enum.PASS = 42] = "PASS"; + })(Enum || (Enum = {})); + console.log(Enum[42], Enum.PASS); + } + expect: { + console.log("PASS", 42); + } + expect_stdout: "PASS 42" +} + +issue_2816: { + options = { + join_vars: true, + } + input: { + "use strict"; + var o = { + a: 1 + }; + o.b = 2; + o.a = 3; + o.c = 4; + console.log(o.a, o.b, o.c); + } + expect: { + "use strict"; + var o = { + a: 1, + b: 2 + }; + o.a = 3; + o.c = 4; + console.log(o.a, o.b, o.c); + } + expect_stdout: "3 2 4" +} + +issue_2893_1: { + options = { + join_vars: true, + } + input: { + var o = { + get a() { + return "PASS"; + }, + }; + o.a = "FAIL"; + console.log(o.a); + } + expect: { + var o = { + get a() { + return "PASS"; + }, + }; + o.a = "FAIL"; + console.log(o.a); + } + expect_stdout: "PASS" +} + +issue_2893_2: { + options = { + join_vars: true, + } + input: { + var o = { + set a(v) { + this.b = v; + }, + b: "FAIL", + }; + o.a = "PASS"; + console.log(o.b); + } + expect: { + var o = { + set a(v) { + this.b = v; + }, + b: "FAIL", + }; + o.a = "PASS"; + console.log(o.b); + } + expect_stdout: "PASS" +} + +join_expr: { + options = { + evaluate: true, + join_vars: true, + } + input: { + var c = "FAIL"; + (function() { + var a = 0; + switch ((a = {}) && (a.b = 0)) { + case 0: + c = "PASS"; + } + })(); + console.log(c); + } + expect: { + var c = "FAIL"; + (function() { + var a = 0, a = { b: 0 }; + switch (a.b) { + case 0: + c = "PASS"; + } + })(); + console.log(c); + } + expect_stdout: "PASS" +} + +issue_3786: { + options = { + join_vars: true, + } + input: { + try { + var a = b; + b = 0; + console.log("FAIL"); + } catch (e) { + console.log("PASS"); + } + } + expect: { + try { + var a = b; + b = 0; + console.log("FAIL"); + } catch (e) { + console.log("PASS"); + } + } + expect_stdout: "PASS" +} + +issue_3788: { + options = { + inline: true, + join_vars: true, + reduce_vars: true, + sequences: true, + side_effects: true, + toplevel: true, + unused: true, + } + input: { + var a = "FAIL"; + function f() { + function g() { + function h() { + a = 42; + a = "PASS"; + return "PASS"; + } + var b = h(); + console.log(b); + } + g(); + } + f(); + console.log(a); + } + expect: { + var b, a = "FAIL"; + a = 42, + a = "PASS", + b = "PASS", + console.log(b), + console.log(a); + } + expect_stdout: [ + "PASS", + "PASS", + ] +} + +issue_3789_1: { + options = { + join_vars: true, + } + input: { + try { + c; + console.log("FAIL"); + } catch (e) { + console.log("PASS"); + } + try {} catch (c) { + var a; + c = 0; + } + } + expect: { + try { + c; + console.log("FAIL"); + } catch (e) { + console.log("PASS"); + } + try {} catch (c) { + var a; + c = 0; + } + } + expect_stdout: "PASS" +} + +issue_3789_2: { + options = { + join_vars: true, + } + input: { + try { + c; + console.log("FAIL"); + } catch (e) { + console.log("PASS"); + } + try {} catch (c) { + try {} catch (c) { + var a; + c = 0; + } + } + } + expect: { + try { + c; + console.log("FAIL"); + } catch (e) { + console.log("PASS"); + } + try {} catch (c) { + try {} catch (c) { + var a; + c = 0; + } + } + } + expect_stdout: "PASS" +} + +issue_3791_1: { + options = { + collapse_vars: true, + join_vars: true, + toplevel: true, + } + input: { + var a = "PASS"; + switch (a) { + case console: + } + var a = a; + console.log(a); + } + expect: { + var a; + switch (a = "PASS") { + case console: + } + var a = a; + console.log(a); + } + expect_stdout: "PASS" +} + +issue_3791_2: { + options = { + collapse_vars: true, + join_vars: true, + } + input: { + function f(a) { + var b; + return b = a || g; + function g() { + return b; + } + } + console.log(typeof f()()); + } + expect: { + function f(a) { + var b; + return b = a || g; + function g() { + return b; + } + } + console.log(typeof f()()); + } + expect_stdout: "function" +} + +issue_3795: { + options = { + booleans: true, + collapse_vars: true, + dead_code: true, + evaluate: true, + join_vars: true, + keep_fargs: false, + loops: true, + passes: 2, + reduce_vars: true, + toplevel: true, + unused: true, + } + input: { + var a = "FAIL"; + function f(b, c) { + for (var i = 5; c && i; --i) return -1; + a = "PASS"; + } + var d = f(a = 42, d); + console.log(a, d); + } + expect: { + var a = "FAIL", d = function() { + if (void 0) return -1; + a = "PASS"; + }(a = 42); + console.log(a, d); + } + expect_stdout: "PASS undefined" +} + +if_body: { + options = { + join_vars: true, + } + input: { + var a; + if (x) + var b; + else + var c; + } + expect: { + var a, b, c; + if (x); + else; + } +} + +if_switch: { + options = { + join_vars: true, + } + input: { + var a; + if (x) switch (y) { + case 1: + var b; + default: + var c; + } + } + expect: { + var a, b, c; + if (x) switch (y) { + case 1: + default: + } + } +} + +loop_body_1: { + options = { + join_vars: true, + } + input: { + var a; + for (;x;) + var b; + } + expect: { + for (var a, b; x;); + } +} + +loop_body_2: { + options = { + join_vars: true, + } + input: { + for (var a; x;) + var b; + } + expect: { + for (var a, b; x;); + } +} + +loop_body_3: { + options = { + join_vars: true, + } + input: { + var a; + for (var b; x;) + var c; + } + expect: { + for (var a, b, c; x;); + } +} + +conditional_assignments_1: { + options = { + conditionals: true, + join_vars: true, + sequences: true, + } + input: { + function f(b, c, d) { + var a = b; + if (c) a = d; + return a; + } + function g(b, c, d) { + var a = b; + if (c); else a = d; + return a; + } + console.log(f("FAIL", 1, "PASS"), g("PASS", 1, "FAIL")); + } + expect: { + function f(b, c, d) { + var a = c ? d : b; + return a; + } + function g(b, c, d) { + var a = c ? b : d; + return a; + } + console.log(f("FAIL", 1, "PASS"), g("PASS", 1, "FAIL")); + } + expect_stdout: "PASS PASS" +} + +conditional_assignments_2: { + options = { + conditionals: true, + join_vars: true, + sequences: true, + } + input: { + function f1(c, d) { + var a = b; + if (c) a = d; + return a; + } + function f2(b, d) { + var a = b; + if (c) a = d; + return a; + } + function f3(b, c) { + var a = b; + if (c) a = d; + return a; + } + } + expect: { + function f1(c, d) { + var a = b; + return c && (a = d), a; + } + function f2(b, d) { + var a = b; + return c && (a = d), a; + } + function f3(b, c) { + var a = b; + return c && (a = d), a; + } + } +} + +conditional_assignments_3: { + options = { + conditionals: true, + sequences: true, + } + input: { + console.log(function(b) { + var a = "PASS"; + if (b) a = a; + return a; + }(0, 1)); + } + expect: { + console.log(function(b) { + var a = "PASS"; + return b && (a = a), a; + }(0, 1)); + } + expect_stdout: "PASS" +} + +issue_3856_1: { + options = { + booleans: true, + conditionals: true, + if_return: true, + join_vars: true, + sequences: true, + } + input: { + console.log(function() { + (function() { + var a; + if (!a) { + a = 0; + for (var b; !console;); + return 0; + } + if (a) return 1; + })(); + }()); + } + expect: { + console.log(function() { + (function() { + var a, b; + if (a) a; + else { + a = 0; + for (; !console;); + } + })(); + }()); + } + expect_stdout: "undefined" +} + +issue_3856_2: { + options = { + booleans: true, + conditionals: true, + if_return: true, + join_vars: true, + passes: 2, + sequences: true, + side_effects: true, + } + input: { + console.log(function() { + (function() { + var a; + if (!a) { + a = 0; + for (var b; !console;); + return 0; + } + if (a) return 1; + })(); + }()); + } + expect: { + console.log(function() { + (function() { + var a, b; + if (!a) + for (a = 0; !console;); + })(); + }()); + } + expect_stdout: "undefined" +} + +issue_3916_1: { + options = { + join_vars: true, + } + input: { + var o = {}; + o.p = "PASS"; + o.__proto__ = 42; + o.q = "FAIL"; + o.__proto__ = { + p: "FAIL", + q: "PASS", + }; + o.__proto__ = "foo"; + console.log(typeof o.__proto__, o.p, delete o.q, o.q); + } + expect: { + var o = { + p: "PASS", + __proto__: 42, + }; + o.q = "FAIL"; + o.__proto__ = { + p: "FAIL", + q: "PASS", + }; + o.__proto__ = "foo"; + console.log(typeof o.__proto__, o.p, delete o.q, o.q); + } + expect_stdout: "object PASS true PASS" +} + +issue_3916_2: { + options = { + join_vars: true, + } + input: { + var log = console.log, o = {}; + o.p = "FAIL 1"; + o.__proto__ = { + get p() { + return "FAIL 2"; + }, + set p(u) { + log("FAIL 3"); + }, + set q(v) { + log("PASS 1"); + }, + get q() { + return "PASS 3"; + }, + }; + o.p = "PASS 2"; + o.q = "FAIL 4"; + log(o.p); + log(o.q); + } + expect: { + var log = console.log, o = { + p: "FAIL 1", + __proto__: { + get p() { + return "FAIL 2"; + }, + set p(u) { + log("FAIL 3"); + }, + set q(v) { + log("PASS 1"); + }, + get q() { + return "PASS 3"; + }, + }, + }; + o.p = "PASS 2"; + o.q = "FAIL 4"; + log(o.p); + log(o.q); + } + expect_stdout: [ + "PASS 1", + "PASS 2", + "PASS 3", + ] +} + +assign_var: { + options = { + join_vars: true, + } + input: { + b = "foo"; + var a = [ , "bar" ]; + console.log(b); + for (var b in a) + console.log(b, a[b]); + } + expect: { + var b = "foo", a = [ , "bar" ], b; + console.log(b); + for (b in a) + console.log(b, a[b]); + } + expect_stdout: [ + "foo", + "1 bar", + ] +} + +assign_for_var: { + options = { + join_vars: true, + } + input: { + i = "foo", + a = new Array(i, "bar"); + for (var i = 2; --i >= 0;) { + console.log(a[i]); + for (var a in i); + } + } + expect: { + for (var i = "foo", a = new Array(i, "bar"), i = 2; --i >= 0;) { + console.log(a[i]); + for (var a in i); + } + } + expect_stdout: [ + "bar", + "foo", + ] +} + +assign_sequence_var: { + options = { + join_vars: true, + } + input: { + var a = 0, b = 1; + console.log(a), + a++, + b = 2; + var c = 3; + console.log(a, b, c); + } + expect: { + var a = 0, b = 1, c = (console.log(a), a++, b = 2, 3); + console.log(a, b, c); + } + expect_stdout: [ + "0", + "1 2 3", + ] +} + +issue_5175: { + options = { + join_vars: true, + } + input: { + function log(f) { + console.log(f(), A.p); + } + log(function() { + return (A = {}).p = "PASS"; + }); + } + expect: { + function log(f) { + console.log(f(), A.p); + } + log(function() { + return (A = {}).p = "PASS"; + }); + } + expect_stdout: "PASS PASS" +} diff --git a/test/compress/keep_fargs.js b/test/compress/keep_fargs.js new file mode 100644 index 00000000000..a8e34cca580 --- /dev/null +++ b/test/compress/keep_fargs.js @@ -0,0 +1,1425 @@ +keep_fargs_false: { + options = { + keep_fargs: false, + unused: true, + } + input: { + console.log(function f(a) { + return f.length; + }(), function g(b) { + return g; + }().length); + function h(c) { + return h.length; + } + function i(d) { + return i; + } + function j(e) {} + console.log(h(), i().length, j.length); + } + expect: { + console.log(function f(a) { + return f.length; + }(), function g(b) { + return g; + }().length); + function h(c) { + return h.length; + } + function i(d) { + return i; + } + function j(e) {} + console.log(h(), i().length, j.length); + } + expect_stdout: [ + "1 1", + "1 1 1", + ] +} + +keep_fargs_true: { + options = { + keep_fargs: true, + unused: true, + } + input: { + console.log(function f(a) { + return f.length; + }(), function g(b) { + return g; + }().length); + function h(c) { + return h.length; + } + function i(d) { + return i; + } + function j(e) {} + console.log(h(), i().length, j.length); + } + expect: { + console.log(function f(a) { + return f.length; + }(), function g(b) { + return g; + }().length); + function h(c) { + return h.length; + } + function i(d) { + return i; + } + function j(e) {} + console.log(h(), i().length, j.length); + } + expect_stdout: [ + "1 1", + "1 1 1", + ] +} + +replace_index_strict: { + options = { + arguments: true, + evaluate: true, + keep_fargs: false, + properties: true, + reduce_vars: true, + } + input: { + "use strict"; + (function() { + console.log(arguments[1], arguments["1"], arguments["foo"]); + })("bar", 42); + (function(a, b) { + console.log(arguments[1], arguments["1"], arguments["foo"]); + })("bar", 42); + } + expect: { + "use strict"; + (function(argument_0, argument_1) { + console.log(argument_1, argument_1, arguments.foo); + })("bar", 42); + (function(a, b) { + console.log(b, b, arguments.foo); + })("bar", 42); + } + expect_stdout: [ + "42 42 undefined", + "42 42 undefined", + ] +} + +issue_1858: { + options = { + collapse_vars: true, + keep_fargs: false, + pure_getters: true, + reduce_vars: true, + unused: true, + } + input: { + console.log(function(x) { + var a = {}, b = a.b = x; + return a.b + b; + }(1)); + } + expect: { + console.log(function() { + var a = {}, b = a.b = 1; + return a.b + b; + }()); + } + expect_stdout: "2" +} + +issue_2187_2: { + options = { + collapse_vars: true, + keep_fargs: false, + unused: true, + } + input: { + var b = 1; + console.log(function(a) { + return a && ++b; + }(b--)); + } + expect: { + var b = 1; + console.log(function() { + return b-- && ++b; + }()); + } + expect_stdout: "1" +} + +issue_2203_2: { + options = { + collapse_vars: true, + keep_fargs: false, + unused: true, + } + input: { + a = "PASS"; + console.log({ + a: "FAIL", + b: function() { + return function(c) { + return c.a; + }((String, (Object, function() { + return this; + }()))); + } + }.b()); + } + expect: { + a = "PASS"; + console.log({ + a: "FAIL", + b: function() { + return function() { + return (Object, function() { + return this; + }()).a; + }(String); + } + }.b()); + } + expect_stdout: "PASS" +} + +issue_2298: { + options = { + collapse_vars: true, + keep_fargs: false, + passes: 2, + reduce_funcs: true, + reduce_vars: true, + unused: true, + } + input: { + !function() { + function f() { + var a = undefined; + var undefined = a++; + try { + !function g(b) { + b[1] = "foo"; + }(); + console.log("FAIL"); + } catch (e) { + console.log("PASS"); + } + } + f(); + }(); + } + expect: { + !function() { + (function() { + try { + !function() { + (void 0)[1] = "foo"; + }(); + console.log("FAIL"); + } catch (e) { + console.log("PASS"); + } + })(); + }(); + } + expect_stdout: "PASS" +} + +issue_2319_1: { + options = { + collapse_vars: true, + keep_fargs: false, + unused: true, + } + input: { + console.log(function(a) { + return a; + }(!function() { + return this; + }())); + } + expect: { + console.log(function() { + return !function() { + return this; + }(); + }()); + } + expect_stdout: "false" +} + +issue_2319_2: { + options = { + collapse_vars: true, + keep_fargs: false, + unused: true, + } + input: { + console.log(function(a) { + "use strict"; + return a; + }(!function() { + return this; + }())); + } + expect: { + console.log(function(a) { + "use strict"; + return a; + }(!function() { + return this; + }())); + } + expect_stdout: "false" +} + +issue_2319_3: { + options = { + collapse_vars: true, + keep_fargs: false, + unused: true, + } + input: { + "use strict"; + console.log(function(a) { + return a; + }(!function() { + return this; + }())); + } + expect: { + "use strict"; + console.log(function() { + return !function() { + return this; + }(); + }()); + } + expect_stdout: "true" +} + +issue_2425_1: { + options = { + collapse_vars: true, + keep_fargs: false, + unused: true, + } + input: { + var a = 8; + (function(b) { + b.toString(); + })(--a, a |= 10); + console.log(a); + } + expect: { + var a = 8; + (function(b) { + b.toString(); + })(--a, a |= 10); + console.log(a); + } + expect_stdout: "15" +} + +issue_2425_2: { + options = { + collapse_vars: true, + keep_fargs: false, + unused: true, + } + input: { + var a = 8; + (function(b, c) { + b.toString(); + })(--a, a |= 10); + console.log(a); + } + expect: { + var a = 8; + (function(b) { + b.toString(); + })(--a, a |= 10); + console.log(a); + } + expect_stdout: "15" +} + +issue_2425_3: { + options = { + collapse_vars: true, + keep_fargs: false, + unused: true, + } + input: { + var a = 8; + (function(b, b) { + b.toString(); + })(--a, a |= 10); + console.log(a); + } + expect: { + var a = 8; + (function() { + (a |= 10).toString(); + })(--a); + console.log(a); + } + expect_stdout: "15" +} + +issue_2436_13: { + options = { + collapse_vars: true, + keep_fargs: false, + passes: 2, + reduce_vars: true, + unused: true, + } + input: { + var a = "PASS"; + (function() { + function f(b) { + (function g(b) { + var b = b && (b.null = "FAIL"); + })(a); + } + f(); + })(); + console.log(a); + } + expect: { + var a = "PASS"; + (function() { + (function() { + (function() { + a && (a.null = "FAIL"); + })(); + })(); + })(); + console.log(a); + } + expect_stdout: "PASS" +} + +issue_2506: { + options = { + collapse_vars: true, + keep_fargs: false, + passes: 2, + reduce_vars: true, + unused: true, + } + input: { + var c = 0; + function f0(bar) { + function f1(Infinity_2) { + function f13(NaN) { + if (false <= NaN & this >> 1 >= 0) { + c++; + } + } + var b_2 = f13(NaN, c++); + } + var bar = f1(-3, -1); + } + f0(false); + console.log(c); + } + expect: { + var c = 0; + function f0(bar) { + (function() { + (function() { + if (false <= 0/0 & this >> 1 >= 0) + c++; + })(c++); + })(); + } + f0(false); + console.log(c); + } + expect_stdout: "1" +} + +issue_2226_1: { + options = { + keep_fargs: false, + side_effects: true, + unused: true, + } + input: { + function f1() { + var a = b; + a += c; + } + function f2(a) { + a <<= b; + } + function f3(a) { + --a; + } + function f4() { + var a = b; + return a *= c; + } + function f5(a) { + x(a /= b); + } + } + expect: { + function f1() { + b; + c; + } + function f2(a) { + b; + } + function f3(a) { + 0; + } + function f4() { + var a = b; + return a *= c; + } + function f5(a) { + x(a /= b); + } + } +} + +issue_2226_2: { + options = { + collapse_vars: true, + keep_fargs: false, + sequences: true, + side_effects: true, + unused: true, + } + input: { + console.log(function(a, b) { + a += b; + return a; + }(1, 2)); + } + expect: { + console.log(function(a) { + return a += 2; + }(1)); + } + expect_stdout: "3" +} + +issue_2226_3: { + options = { + collapse_vars: true, + keep_fargs: false, + side_effects: true, + unused: true, + } + input: { + console.log(function(a, b) { + a += b; + return a; + }(1, 2)); + } + expect: { + console.log(function(a) { + return a += 2; + }(1)); + } + expect_stdout: "3" +} + +issue_3192: { + options = { + keep_fargs: false, + unused: true, + } + input: { + (function(a) { + console.log(a = "foo", arguments[0]); + })("bar"); + (function(a) { + "use strict"; + console.log(a = "foo", arguments[0]); + })("bar"); + } + expect: { + (function(a) { + console.log(a = "foo", arguments[0]); + })("bar"); + (function() { + "use strict"; + console.log("foo", arguments[0]); + })("bar"); + } + expect_stdout: [ + "foo foo", + "foo bar", + ] +} + +if_increment: { + options = { + evaluate: true, + keep_fargs: false, + reduce_vars: true, + unused: true, + } + input: { + console.log(function(a) { + if (console) + return ++a; + }(0)); + } + expect: { + console.log(function() { + if (console) + return 1; + }()); + } + expect_stdout: "1" +} + +try_increment: { + options = { + evaluate: true, + keep_fargs: false, + reduce_vars: true, + unused: true, + } + input: { + console.log(function(a) { + try { + return ++a; + } catch (e) {} + }(0)); + } + expect: { + console.log(function() { + try { + return 1; + } catch (e) {} + }()); + } + expect_stdout: "1" +} + +issue_2630_3: { + options = { + inline: true, + keep_fargs: false, + reduce_vars: true, + unused: true, + } + input: { + var x = 2, a = 1; + (function() { + function f1(a) { + f2(); + --x >= 0 && f1({}); + } + f1(a++); + function f2() { + a++; + } + })(); + console.log(a); + } + expect: { + var x = 2, a = 1; + (function() { + (function f1() { + f2(); + --x >= 0 && f1(); + })(a++); + function f2() { + a++; + } + })(); + console.log(a); + } + expect_stdout: "5" +} + +issue_3364: { + options = { + functions: true, + keep_fargs: false, + reduce_vars: true, + toplevel: true, + unused: true, + } + mangle = {} + input: { + var s = 2, a = 100, b = 10, c = 0; + function f(p, e, r) { + try { + for (var i = 1; i-- > 0;) + var a = function(x) { + function g(y) { + y && y[a++]; + } + var x = g(--s >= 0 && f(c++)); + for (var j = 1; --j > 0;); + }(); + } catch (e) { + try { + return; + } catch (z) { + for (var k = 1; --k > 0;) { + for (var l = 1; l > 0; --l) { + var n = function() {}; + for (var k in n) + var o = (n, k); + } + } + } + } + } + var r = f(); + console.log(c); + } + expect: { + var s = 2, c = 0; + (function o() { + try { + for (var r = 1; r-- > 0;) + var n = function() { + (function(r) { + r && r[n++]; + })(--s >= 0 && o(c++)); + for (var r = 1; --r > 0;); + }(); + } catch (r) { + try { + return; + } catch (r) { + for (var a = 1; --a > 0;) + for (var f = 1; f > 0; --f) { + function t() {} + for (var a in t); + } + } + } + })(); + console.log(c); + } + expect_stdout: "2" +} + +defun_label: { + options = { + keep_fargs: false, + passes: 2, + reduce_funcs: true, + reduce_vars: true, + unused: true, + } + input: { + !function() { + function f(a) { + L: { + if (a) break L; + return 1; + } + } + console.log(f(2)); + }(); + } + expect: { + !function() { + console.log(function() { + L: { + if (2) break L; + return 1; + } + }()); + }(); + } + expect_stdout: true +} + +iife_func_side_effects: { + options = { + keep_fargs: false, + reduce_funcs: true, + reduce_vars: true, + unused: true, + } + input: { + function x() { + console.log("x"); + } + function y() { + console.log("y"); + } + function z() { + console.log("z"); + } + (function(a, b, c) { + function y() { + console.log("FAIL"); + } + return y + b(); + })(x(), function() { + return y(); + }, z()); + } + expect: { + function x() { + console.log("x"); + } + function y() { + console.log("y"); + } + function z() { + console.log("z"); + } + (function(b) { + return function() { + console.log("FAIL"); + } + b(); + })((x(), function() { + return y(); + }), z()); + } + expect_stdout: [ + "x", + "z", + "y", + ] +} + +issue_1595_1: { + options = { + evaluate: true, + keep_fargs: false, + reduce_funcs: true, + reduce_vars: true, + unused: true, + } + input: { + (function f(a) { + return f(a + 1); + })(2); + } + expect: { + (function f(a) { + return f(a + 1); + })(2); + } +} + +issue_1595_2: { + options = { + evaluate: true, + keep_fargs: false, + reduce_funcs: true, + reduce_vars: true, + unused: true, + } + input: { + (function f(a) { + return g(a + 1); + })(2); + } + expect: { + (function(a) { + return g(a + 1); + })(2); + } +} + +issue_1595_3: { + options = { + evaluate: true, + keep_fargs: false, + passes: 2, + reduce_funcs: true, + reduce_vars: true, + unused: true, + } + input: { + (function f(a) { + return g(a + 1); + })(2); + } + expect: { + (function() { + return g(3); + })(); + } +} + +issue_1595_4: { + options = { + evaluate: true, + keep_fargs: false, + reduce_funcs: true, + reduce_vars: true, + unused: true, + } + input: { + (function iife(a, b, c) { + console.log(a, b, c); + if (a) iife(a - 1, b, c); + })(3, 4, 5); + } + expect: { + (function iife(a, b, c) { + console.log(a, b, c); + if (a) iife(a - 1, b, c); + })(3, 4, 5); + } + expect_stdout: true +} + +duplicate_lambda_defun_name_1: { + options = { + keep_fargs: false, + reduce_vars: true, + } + input: { + console.log(function f(a) { + function f() {} + return f.length; + }()); + } + expect: { + console.log(function f(a) { + function f() {} + return f.length; + }()); + } + expect_stdout: "0" +} + +duplicate_lambda_defun_name_2: { + options = { + keep_fargs: false, + passes: 2, + reduce_vars: true, + unused: true, + } + input: { + console.log(function f(a) { + function f() {} + return f.length; + }()); + } + expect: { + console.log(function() { + return function() {}.length; + }()); + } + expect_stdout: "0" +} + +function_argument_mangle: { + mangle = { + keep_fargs: true, + toplevel: true, + } + input: { + A = "PASS"; + var a = A; + (function(o) { + console.log(a); + })("FAIL"); + } + expect: { + A = "PASS"; + var n = A; + (function(o) { + console.log(n); + })("FAIL"); + } + expect_stdout: "PASS" +} + +function_name_mangle: { + options = { + keep_fargs: false, + keep_fnames: true, + reduce_vars: true, + unused: true, + } + mangle = {} + input: { + (function() { + function foo(bar) {} + console.log(typeof foo); + })(); + } + expect_exact: "(function(){console.log(typeof function o(){})})();" + expect_stdout: "function" +} + +function_name_mangle_ie8: { + options = { + keep_fargs: false, + keep_fnames: true, + reduce_vars: true, + unused: true, + } + mangle = { + ie: true, + toplevel: true, + } + input: { + (function() { + function foo(bar) {} + console.log(typeof foo); + })(); + } + expect_exact: "(function(){console.log(typeof function o(){})})();" + expect_stdout: "function" +} + +issue_3420_1: { + options = { + keep_fargs: false, + unused: true, + } + input: { + console.log(function() { + return function(a, b, c, d) { + return a + b; + }; + }().length); + } + expect: { + console.log(function() { + return function(a, b, c, d) { + return a + b; + }; + }().length); + } + expect_stdout: "4" +} + +issue_3420_2: { + options = { + inline: true, + keep_fargs: false, + unused: true, + } + input: { + console.log(function() { + return function(a, b, c, d) { + return a + b; + }; + }().length); + } + expect: { + console.log(function(a, b, c, d) { + return a + b; + }.length); + } + expect_stdout: "4" +} + +issue_3420_3: { + options = { + inline: true, + keep_fargs: false, + reduce_vars: true, + unused: true, + } + input: { + console.log(function() { + function f(a, b, c, d) { + return a + b; + } + return f; + }().length); + } + expect: { + console.log(function(a, b, c, d) { + return a + b; + }.length); + } + expect_stdout: "4" +} + +issue_3423_1: { + options = { + keep_fargs: false, + unused: true, + } + input: { + function f(g) { + console.log(g.length); + } + f(function(a) {}); + } + expect: { + function f(g) { + console.log(g.length); + } + f(function(a) {}); + } + expect_stdout: "1" +} + +issue_3423_2: { + options = { + keep_fargs: false, + unused: true, + } + input: { + new function(a) { + console.log(this.constructor.length); + }(); + } + expect: { + new function(a) { + console.log(this.constructor.length); + }(); + } + expect_stdout: "1" +} + +collapse_vars_repeated: { + options = { + booleans: true, + collapse_vars: true, + comparisons: true, + conditionals: true, + dead_code: true, + evaluate: true, + hoist_funs: true, + if_return: true, + join_vars: true, + keep_fargs: false, + loops: true, + properties: true, + reduce_funcs: true, + reduce_vars: true, + sequences: true, + side_effects: true, + unused: true, + } + input: { + function f1() { + var dummy = 3, a = 5, unused = 2, a = 1, a = 3; + return -a; + } + function f2(x) { + var a = 3, a = x; + return a; + } + (function(x) { + var a = "GOOD" + x, e = "BAD", k = "!", e = a; + console.log(e + k); + })("!"), + (function(x) { + var a = "GOOD" + x, e = "BAD" + x, k = "!", e = a; + console.log(e + k); + })("!"); + } + expect: { + function f1() { + return -3; + } + function f2(x) { + return x; + } + (function() { + console.log("GOOD!!"); + })(), + (function() { + console.log("GOOD!!"); + })(); + } + expect_stdout: true +} + +chained_3: { + options = { + collapse_vars: true, + keep_fargs: false, + unused: true, + } + input: { + console.log(function(a, b) { + var c = a, c = b; + b++; + return c; + }(1, 2)); + } + expect: { + console.log(function(b) { + var c = 1, c = b; + b++; + return c; + }(2)); + } + expect_stdout: "2" +} + +replace_all_var_scope: { + rename = true + options = { + collapse_vars: true, + keep_fargs: false, + unused: true, + } + mangle = {} + input: { + var a = 100, b = 10; + (function(r, a) { + switch (~a) { + case (b += a): + case a++: + } + })(--b, a); + console.log(a, b); + } + expect: { + var a = 100, b = 10; + (function(c) { + switch (~a) { + case (b += a): + case c++: + } + })((--b, a)); + console.log(a, b); + } + expect_stdout: "100 109" +} + +issue_1583: { + options = { + keep_fargs: false, + passes: 2, + reduce_funcs: true, + reduce_vars: true, + unused: true, + } + input: { + function m(t) { + (function(e) { + t = e(); + })(function() { + return (function(a) { + return a; + })(function(a) {}); + }); + } + } + expect: { + function m(t) { + (function() { + (function() { + return (function() { + return function(a) {}; + })(); + })(); + })(); + } + } +} + +issues_3267_1: { + options = { + collapse_vars: true, + conditionals: true, + dead_code: true, + evaluate: true, + inline: true, + keep_fargs: false, + negate_iife: true, + reduce_vars: true, + sequences: true, + side_effects: true, + unused: true, + } + input: { + (function(x) { + x(); + })(function() { + (function(i) { + if (i) + return console.log("PASS"); + throw "FAIL"; + })(Object()); + }); + } + expect: { + !function() { + if (Object()) + return console.log("PASS"); + throw "FAIL"; + }(); + } + expect_stdout: "PASS" +} + +trailing_argument_side_effects: { + options = { + keep_fargs: false, + unused: true, + } + input: { + function f() { + return "FAIL"; + } + console.log(function(a, b) { + return b || "PASS"; + }(f())); + } + expect: { + function f() { + return "FAIL"; + } + console.log(function(b) { + return b || "PASS"; + }(void f())); + } + expect_stdout: "PASS" +} + +recursive_iife_1: { + options = { + keep_fargs: false, + reduce_vars: true, + unused: true, + } + input: { + console.log(function f(a, b) { + return b || f("FAIL", "PASS"); + }()); + } + expect: { + console.log(function f(a, b) { + return b || f(0, "PASS"); + }()); + } + expect_stdout: "PASS" +} + +recursive_iife_2: { + options = { + keep_fargs: false, + reduce_vars: true, + unused: true, + } + input: { + console.log(function f(a, b) { + return b || f("FAIL", "PASS"); + }(null, 0)); + } + expect: { + console.log(function f(a, b) { + return b || f(0, "PASS"); + }(0, 0)); + } + expect_stdout: "PASS" +} + +recursive_iife_3: { + options = { + keep_fargs: false, + reduce_vars: true, + unused: true, + } + input: { + var a = 1, c = "PASS"; + (function() { + function f(b, d, e) { + a-- && f(null, 42, 0); + e && (c = "FAIL"); + d && d.p; + } + var a_1 = f(); + })(); + console.log(c); + } + expect: { + var a = 1, c = "PASS"; + (function() { + (function f(b, d, e) { + a-- && f(0, 42, 0); + e && (c = "FAIL"); + d && d.p; + })(); + })(); + console.log(c); + } + expect_stdout: "PASS" +} + +issue_3619: { + options = { + keep_fargs: false, + unused: true, + } + input: { + var a = 1, b = "FAIL"; + (function f(c, d) { + function g() { + d && (b = "PASS", 0 <= --a && g()); + 0 <= --a && f(0, "function"); + } + g(); + })(); + console.log(b); + } + expect: { + var a = 1, b = "FAIL"; + (function f(c, d) { + function g() { + d && (b = "PASS", 0 <= --a && g()); + 0 <= --a && f(0, "function"); + } + g(); + })(); + console.log(b); + } + expect_stdout: "PASS" +} + +issue_4353_1: { + options = { + keep_fargs: false, + reduce_vars: true, + unused: true, + } + input: { + console.log(function f(a) {}.length); + } + expect: { + console.log(function(a) {}.length); + } + expect_stdout: "1" +} + +issue_4353_2: { + options = { + keep_fargs: false, + reduce_vars: true, + unused: true, + } + input: { + (function f(a) { + while (console.log("PASS")); + })(); + } + expect: { + (function() { + while (console.log("PASS")); + })(); + } + expect_stdout: "PASS" +} diff --git a/test/compress/labels.js b/test/compress/labels.js index 044b7a7eabc..c455f3d1de1 100644 --- a/test/compress/labels.js +++ b/test/compress/labels.js @@ -1,5 +1,10 @@ labels_1: { - options = { if_return: true, conditionals: true, dead_code: true }; + options = { + conditionals: true, + dead_code: true, + if_return: true, + unused: true, + } input: { out: { if (foo) break out; @@ -9,10 +14,16 @@ labels_1: { expect: { foo || console.log("bar"); } + expect_stdout: true } labels_2: { - options = { if_return: true, conditionals: true, dead_code: true }; + options = { + conditionals: true, + dead_code: true, + if_return: true, + unused: true, + } input: { out: { if (foo) print("stuff"); @@ -29,7 +40,11 @@ labels_2: { } labels_3: { - options = { if_return: true, conditionals: true, dead_code: true }; + options = { + conditionals: true, + dead_code: true, + if_return: true, + } input: { for (var i = 0; i < 5; ++i) { if (i < 3) continue; @@ -40,10 +55,16 @@ labels_3: { for (var i = 0; i < 5; ++i) i < 3 || console.log(i); } + expect_stdout: true } labels_4: { - options = { if_return: true, conditionals: true, dead_code: true }; + options = { + conditionals: true, + dead_code: true, + if_return: true, + unused: true, + } input: { out: for (var i = 0; i < 5; ++i) { if (i < 3) continue out; @@ -54,11 +75,17 @@ labels_4: { for (var i = 0; i < 5; ++i) i < 3 || console.log(i); } + expect_stdout: true } labels_5: { - options = { if_return: true, conditionals: true, dead_code: true }; - // should keep the break-s in the following + options = { + conditionals: true, + dead_code: true, + if_return: true, + unused: true, + } + // should keep `break`s below input: { while (foo) { if (bar) break; @@ -74,14 +101,18 @@ labels_5: { if (bar) break; console.log("foo"); } - out: while (foo) { - if (bar) break out; + while (foo) { + if (bar) break; console.log("foo"); } } } labels_6: { + options = { + dead_code: true, + unused: true, + } input: { out: break out; }; @@ -89,7 +120,11 @@ labels_6: { } labels_7: { - options = { if_return: true, conditionals: true, dead_code: true }; + options = { + conditionals: true, + dead_code: true, + if_return: true, + } input: { while (foo) { x(); @@ -106,7 +141,11 @@ labels_7: { } labels_8: { - options = { if_return: true, conditionals: true, dead_code: true }; + options = { + conditionals: true, + dead_code: true, + if_return: true, + } input: { while (foo) { x(); @@ -124,7 +163,12 @@ labels_8: { } labels_9: { - options = { if_return: true, conditionals: true, dead_code: true }; + options = { + conditionals: true, + dead_code: true, + if_return: true, + unused: true, + } input: { out: while (foo) { x(); @@ -143,21 +187,247 @@ labels_9: { } labels_10: { - options = { if_return: true, conditionals: true, dead_code: true }; + options = { + conditionals: true, + dead_code: true, + if_return: true, + unused: true, + } input: { - out: while (foo) { - x(); - y(); + out: while (42) { + console.log("PASS"); break out; - z(); - k(); + console.log("FAIL"); } }; expect: { - out: while (foo) { - x(); - y(); - break out; + while (42) { + console.log("PASS"); + break; + } + } + expect_stdout: "PASS" +} + +labels_11: { + options = { + conditionals: true, + if_return: true, + unused: true, + } + input: { + L: if (console.log("PASS")) + break L; + } + expect: { + console.log("PASS"); + } + expect_stdout: "PASS" +} + +labels_12: { + options = { + conditionals: true, + dead_code: true, + if_return: true, + } + input: { + L: try { + if (console.log("foo")) + break L; + throw "bar"; + } catch (e) { + console.log(e); + break L; + } finally { + if (console.log("baz")) + break L; + } + } + expect: { + L: try { + if (!console.log("foo")) + throw "bar"; + } catch (e) { + console.log(e); + } finally { + if (console.log("baz")) + break L; + } + } + expect_stdout: [ + "foo", + "bar", + "baz", + ] +} + +issue_4466_1: { + mangle = { + v8: false, + } + input: { + A: if (console.log("PASS")) + B:; + else + C:; + } + expect: { + e: if (console.log("PASS")) + l:; + else + l:; + } + expect_stdout: "PASS" + node_version: ">=12" +} + +issue_4466_1_v8: { + mangle = { + v8: true, + } + input: { + A: if (console.log("PASS")) + B:; + else + C:; + } + expect: { + e: if (console.log("PASS")) + l:; + else + o:; + } + expect_stdout: "PASS" + node_version: ">=12" +} + +issue_4466_2: { + mangle = { + toplevel: false, + v8: false, + } + input: { + if (console.log("PASS")) + A:; + else + B:; + } + expect: { + if (console.log("PASS")) + e:; + else + e:; + } + expect_stdout: "PASS" +} + +issue_4466_2_v8: { + mangle = { + toplevel: false, + v8: true, + } + input: { + if (console.log("PASS")) + A:; + else + B:; + } + expect: { + if (console.log("PASS")) + e:; + else + l:; + } + expect_stdout: "PASS" +} + +issue_4466_2_toplevel: { + mangle = { + toplevel: true, + v8: false, + } + input: { + if (console.log("PASS")) + A:; + else + B:; + } + expect: { + if (console.log("PASS")) + e:; + else + e:; + } + expect_stdout: "PASS" +} + +issue_4466_2_toplevel_v8: { + mangle = { + toplevel: true, + v8: true, + } + input: { + if (console.log("PASS")) + A:; + else + B:; + } + expect: { + if (console.log("PASS")) + e:; + else + e:; + } + expect_stdout: "PASS" +} + +issue_5522: { + options = { + dead_code: true, + } + input: { + console.log(function() { + L: try { + return "FAIL"; + } finally { + break L; + } + return "PASS"; + }()); + } + expect: { + console.log(function() { + L: try { + return "FAIL"; + } finally { + break L; + } + return "PASS"; + }()); + } + expect_stdout: "PASS" +} + +issue_5524: { + options = { + dead_code: true, + } + input: { + L: try { + FAIL; + } finally { + break L; + } + console.log("PASS"); + } + expect: { + L: try { + FAIL; + } finally { + break L; } + console.log("PASS"); } + expect_stdout: "PASS" } diff --git a/test/compress/let.js b/test/compress/let.js new file mode 100644 index 00000000000..ba6c4bc74ae --- /dev/null +++ b/test/compress/let.js @@ -0,0 +1,2539 @@ +retain_block_1: { + options = {} + input: { + "use strict"; + { + let a = "FAIL"; + } + var a = "PASS"; + console.log(a); + } + expect: { + "use strict"; + { + let a = "FAIL"; + } + var a = "PASS"; + console.log(a); + } + expect_stdout: "PASS" + node_version: ">=4" +} + +retain_block_2: { + options = { + toplevel: true, + unused: true, + } + input: { + "use strict"; + { + var a; + let a; + } + } + expect: { + "use strict"; + { + var a; + let a; + } + } + expect_stdout: true + node_version: ">=4" +} + +retain_block_2_mangle: { + rename = true + mangle = { + toplevel: true, + } + input: { + "use strict"; + { + var a; + let a; + } + } + expect: { + "use strict"; + { + var t; + let t; + } + } +} + +retain_block_3: { + options = { + toplevel: true, + unused: true, + } + input: { + "use strict"; + { + let a; + var a; + } + } + expect: { + "use strict"; + { + let a; + var a; + } + } + expect_stdout: true + node_version: ">=4" +} + +retain_block_3_mangle: { + rename = true + mangle = { + toplevel: true, + } + input: { + "use strict"; + { + let a; + var a; + } + } + expect: { + "use strict"; + { + let t; + var t; + } + } +} + +retain_assignment: { + options = { + dead_code: true, + reduce_vars: true, + } + input: { + "use strict"; + function f() { + return a = 0; + let a; + } + try { + f(); + } catch (e) { + console.log("PASS"); + } + } + expect: { + "use strict"; + function f() { + return a = 0; + let a; + } + try { + f(); + } catch (e) { + console.log("PASS"); + } + } + expect_stdout: "PASS" + node_version: ">=4" +} + +retain_catch: { + options = { + dead_code: true, + } + input: { + "use strict"; + try {} catch (a) { + let a = "aa"; + } + } + expect: { + "use strict"; + try {} catch (a) { + let a = "aa"; + } + } + expect_stdout: true + node_version: ">=4" +} + +if_dead_branch: { + options = { + conditionals: true, + dead_code: true, + evaluate: true, + } + input: { + "use strict"; + console.log(function() { + if (0) { + let a = 0; + } + return typeof a; + }()); + } + expect: { + "use strict"; + console.log(function() { + 0; + { + let a; + } + return typeof a; + }()); + } + expect_stdout: "undefined" + node_version: ">=4" +} + +retain_tail_1: { + options = { + conditionals: true, + } + input: { + "use strict"; + function f(a) { + var b = "foo"; + if (a) { + let b = "bar"; + while (console.log("baz")); + console.log(b); + } else { + while (console.log("moo")); + console.log(b); + } + } + f(); + f(42); + } + expect: { + "use strict"; + function f(a) { + var b = "foo"; + if (a) { + let b = "bar"; + while (console.log("baz")); + console.log(b); + } else { + while (console.log("moo")); + console.log(b); + } + } + f(); + f(42); + } + expect_stdout: [ + "moo", + "foo", + "baz", + "bar", + ] + node_version: ">=4" +} + +retain_tail_2: { + options = { + conditionals: true, + } + input: { + "use strict"; + function f(a) { + var b = "foo"; + if (a) { + while (console.log("bar")); + console.log(b); + } else { + let b = "baz"; + while (console.log("moo")); + console.log(b); + } + } + f(); + f(42); + } + expect: { + "use strict"; + function f(a) { + var b = "foo"; + if (a) { + while (console.log("bar")); + console.log(b); + } else { + let b = "baz"; + while (console.log("moo")); + console.log(b); + } + } + f(); + f(42); + } + expect_stdout: [ + "moo", + "baz", + "bar", + "foo", + ] + node_version: ">=4" +} + +merge_vars_1: { + options = { + merge_vars: true, + toplevel: true, + } + input: { + "use strict"; + let a = console; + console.log(typeof a); + var b = typeof a; + console.log(b); + } + expect: { + "use strict"; + let a = console; + console.log(typeof a); + var b = typeof a; + console.log(b); + } + expect_stdout: [ + "object", + "object", + ] + node_version: ">=4" +} + +merge_vars_2: { + options = { + inline: true, + merge_vars: true, + toplevel: true, + } + input: { + "use strict"; + var a = 0; + (function() { + var b = function f() { + let c = a && f; + c.var += 0; + }(); + console.log(b); + })(1 && --a); + } + expect: { + "use strict"; + var a = 0; + 1 && --a, + b = function f() { + let c = a && f; + c.var += 0; + }(), + void console.log(b); + var b; + } + expect_stdout: "undefined" + node_version: ">=4" +} + +merge_vars_3: { + options = { + merge_vars: true, + toplevel: true, + } + input: { + "use strict"; + { + let a = 0; + var b = console; + console.log(typeof b); + } + var a = 1; + console.log(typeof a); + } + expect: { + "use strict"; + { + let a = 0; + var b = console; + console.log(typeof b); + } + var b = 1; + console.log(typeof b); + } + expect_stdout: [ + "object", + "number", + ] + node_version: ">=4" +} + +merge_vars_4: { + options = { + merge_vars: true, + toplevel: true, + } + input: { + "use strict"; + var a = 1; + console.log(typeof a); + { + var b = console; + console.log(typeof b); + let a = 0; + } + } + expect: { + "use strict"; + var a = 1; + console.log(typeof a); + { + var b = console; + console.log(typeof b); + let a = 0; + } + } + expect_stdout: [ + "number", + "object", + ] + node_version: ">=4" +} + +use_before_init_1: { + options = { + evaluate: true, + reduce_vars: true, + toplevel: true, + } + input: { + "use strict"; + a = "foo"; + let a = "bar"; + } + expect: { + "use strict"; + a = "foo"; + let a = "bar"; + } + expect_stdout: true + node_version: ">=4" +} + +use_before_init_2: { + options = { + toplevel: true, + unused: true, + } + input: { + "use strict"; + try { + a = "foo"; + } catch (e) { + console.log("PASS"); + } + let a = "bar"; + } + expect: { + "use strict"; + try { + a = "foo"; + } catch (e) { + console.log("PASS"); + } + let a = "bar"; + } + expect_stdout: "PASS" + node_version: ">=4" +} + +use_before_init_3: { + options = { + side_effects: true, + } + input: { + "use strict"; + try { + a; + } catch (e) { + console.log("PASS"); + } + let a = 42; + } + expect: { + "use strict"; + try { + a; + } catch (e) { + console.log("PASS"); + } + let a = 42; + } + expect_stdout: "PASS" + node_version: ">=4" +} + +use_before_init_4: { + options = { + reduce_vars: true, + } + input: { + "use strict"; + try { + console.log(a); + } catch (e) { + console.log("PASS"); + } + let a = "FAIL"; + } + expect: { + "use strict"; + try { + console.log(a); + } catch (e) { + console.log("PASS"); + } + let a = "FAIL"; + } + expect_stdout: "PASS" + node_version: ">=4" +} + +collapse_block: { + options = { + collapse_vars: true, + pure_getters: "strict", + unsafe: true, + } + input: { + "use strict"; + { + let a = typeof console; + console.log(a); + } + } + expect: { + "use strict"; + { + let a = typeof console; + console.log(a); + } + } + expect_stdout: "object" + node_version: ">=4" +} + +reduce_block_1: { + options = { + reduce_vars: true, + } + input: { + "use strict"; + { + let a = typeof console; + console.log(a); + } + } + expect: { + "use strict"; + { + let a = typeof console; + console.log(a); + } + } + expect_stdout: "object" + node_version: ">=4" +} + +reduce_block_2: { + options = { + reduce_vars: true, + } + input: { + "use strict"; + { + let a = typeof console; + console.log(a); + } + console.log(typeof a); + } + expect: { + "use strict"; + { + let a = typeof console; + console.log(a); + } + console.log(typeof a); + } + expect_stdout: [ + "object", + "undefined", + ] + node_version: ">=4" +} + +reduce_block_2_toplevel: { + options = { + reduce_vars: true, + toplevel: true, + } + input: { + "use strict"; + { + let a = typeof console; + console.log(a); + } + console.log(typeof a); + } + expect: { + "use strict"; + { + let a = typeof console; + console.log(a); + } + console.log(typeof a); + } + expect_stdout: [ + "object", + "undefined", + ] + node_version: ">=4" +} + +reduce_vars_1: { + options = { + evaluate: true, + reduce_vars: true, + toplevel: true, + unused: true, + } + input: { + "use strict"; + let a = "PASS"; + console.log(a); + a = "FAIL"; + } + expect: { + "use strict"; + console.log("PASS"); + "FAIL"; + } + expect_stdout: "PASS" + node_version: ">=4" +} + +reduce_vars_2: { + options = { + reduce_funcs: true, + reduce_vars: true, + unused: true, + } + input: { + "use strict"; + (function() { + function f() { + console.log(typeof a); + } + for (let a in [ 42 ]) + f(); + })(); + } + expect: { + "use strict"; + (function() { + function f() { + console.log(typeof a); + } + for (let a in [ 42 ]) + f(); + })(); + } + expect_stdout: "undefined" + node_version: ">=4" +} + +reduce_vars_3: { + options = { + reduce_funcs: true, + reduce_vars: true, + unused: true, + } + input: { + "use strict"; + (function(a) { + let i = 1; + function f() { + i = 0; + } + for (let i = 0, x = 0; i < a.length; i++, x++) { + if (x != i) { + console.log("FAIL"); + break; + } + f(); + console.log(a[i]); + } + console.log(i); + })([ 4, 2 ]); + } + expect: { + "use strict"; + (function(a) { + let i = 1; + function f() { + i = 0; + } + for (let i = 0, x = 0; i < a.length; i++, x++) { + if (x != i) { + console.log("FAIL"); + break; + } + f(); + console.log(a[i]); + } + console.log(i); + })([ 4, 2 ]); + } + expect_stdout: [ + "4", + "2", + "0", + ] + node_version: ">=4" +} + +reduce_lambda: { + options = { + evaluate: true, + functions: true, + reduce_vars: true, + toplevel: true, + unused: true, + } + input: { + "use strict"; + let f = function() { + console.log(a, b); + }; + let a = "foo", b = 42; + f(); + b = "bar"; + f(); + } + expect: { + "use strict"; + function f() { + console.log("foo", b); + } + let b = 42; + f(); + b = "bar"; + f(); + } + expect_stdout: [ + "foo 42", + "foo bar", + ] + node_version: ">=4" +} + +hoist_props: { + options = { + hoist_props: true, + reduce_vars: true, + } + input: { + "use strict"; + { + let o = { + p: "PASS", + }; + console.log(o.p); + } + } + expect: { + "use strict"; + { + let o = { + p: "PASS", + }; + console.log(o.p); + } + } + expect_stdout: "PASS" + node_version: ">=4" +} + +loop_block_1: { + options = { + loops: true, + } + input: { + "use strict"; + do { + let o = console; + console.log(typeof o.log); + } while (!console); + } + expect: { + "use strict"; + do { + let o = console; + console.log(typeof o.log); + } while (!console); + } + expect_stdout: "function" + node_version: ">=4" +} + +loop_block_2: { + options = { + loops: true, + } + input: { + "use strict"; + do { + let o = {}; + (function() { + console.log(typeof this, o.p++); + })(); + } while (!console); + } + expect: { + "use strict"; + do { + let o = {}; + (function() { + console.log(typeof this, o.p++); + })(); + } while (!console); + } + expect_stdout: "undefined NaN" + node_version: ">=4" +} + +do_break: { + options = { + loops: true, + } + input: { + "use strict"; + try { + do { + if (a) + break; + let a; + } while (!console); + } catch (e) { + console.log("PASS"); + } + } + expect: { + "use strict"; + try { + do { + if (a) + break; + let a; + } while (!console); + } catch (e) { + console.log("PASS"); + } + } + expect_stdout: "PASS" + node_version: ">=4" +} + +do_continue: { + options = { + loops: true, + } + input: { + "use strict"; + try { + do { + { + let a = 0; + continue; + } + } while ([ A ]); + } catch (e) { + console.log("PASS"); + } + } + expect: { + "use strict"; + try { + do { + let a = 0; + continue; + } while ([ A ]); + } catch (e) { + console.log("PASS"); + } + } + expect_stdout: "PASS" + node_version: ">=4" +} + +dead_block_after_return: { + options = { + dead_code: true, + } + input: { + "use strict"; + (function(a) { + console.log(a); + return; + { + let a = "FAIL"; + } + })("PASS"); + } + expect: { + "use strict"; + (function(a) { + console.log(a); + return; + { + let a; + } + })("PASS"); + } + expect_stdout: "PASS" + node_version: ">=4" +} + +if_return_1: { + options = { + if_return: true, + } + input: { + "use strict"; + function f(a) { + function g() { + return b = "PASS"; + } + if (a) + return g(); + let b; + return g(); + }; + console.log(f()); + } + expect: { + "use strict"; + function f(a) { + function g() { + return b = "PASS"; + } + if (a) + return g(); + let b; + return g(); + }; + console.log(f()); + } + expect_stdout: "PASS" + node_version: ">=4" +} + +if_return_2: { + options = { + if_return: true, + } + input: { + "use strict"; + function f(a) { + function g() { + return b = "FAIL"; + } + if (a) + return g(); + let b; + return g(); + }; + try { + console.log(f(42)); + } catch (e) { + console.log("PASS"); + } + } + expect: { + "use strict"; + function f(a) { + function g() { + return b = "FAIL"; + } + if (a) + return g(); + let b; + return g(); + }; + try { + console.log(f(42)); + } catch (e) { + console.log("PASS"); + } + } + expect_stdout: "PASS" + node_version: ">=4" +} + +if_return_3: { + options = { + if_return: true, + } + input: { + "use strict"; + var a = "PASS"; + function f(b) { + if (console) { + let b = a; + return b; + } else + while (console.log("FAIL 1")); + return b; + } + console.log(f("FAIL 2")); + } + expect: { + "use strict"; + var a = "PASS"; + function f(b) { + if (console) { + let b = a; + return b; + } else + while (console.log("FAIL 1")); + return b; + } + console.log(f("FAIL 2")); + } + expect_stdout: "PASS" + node_version: ">=4" +} + +do_if_continue_1: { + options = { + if_return: true, + } + input: { + "use strict"; + do { + if (console) { + console.log("PASS"); + { + let a = 0; + var b; + continue; + } + } + } while (b); + } + expect: { + "use strict"; + do { + if (console) { + console.log("PASS"); + { + let a = 0; + var b; + } + } + } while (b); + } + expect_stdout: "PASS" + node_version: ">=4" +} + +do_if_continue_2: { + options = { + if_return: true, + } + input: { + "use strict"; + do { + if (console) { + console.log("FAIL"); + { + let a = 0; + A = 0; + continue; + } + } + } while (A); + } + expect: { + "use strict"; + do { + if (console) { + console.log("FAIL"); + { + let a = 0; + A = 0; + } + } + } while (A); + } + expect_stdout: ReferenceError("A is not defined") + node_version: ">=4" +} + +drop_unused: { + options = { + evaluate: true, + side_effects: true, + unused: true, + } + input: { + "use strict"; + function f(a) { + let b = a, c = b; + 0 && c.p++; + } + console.log(f()); + } + expect: { + "use strict"; + function f(a) { + let b = a; + b; + } + console.log(f()); + } + expect_stdout: "undefined" + node_version: ">=4" +} + +default_init: { + options = { + evaluate: true, + reduce_vars: true, + sequences: true, + toplevel: true, + unused: true, + } + input: { + "use strict"; + let a; + a = "PASS"; + console.log(a); + } + expect: { + "use strict"; + console.log("PASS"); + } + expect_stdout: "PASS" + node_version: ">=4" +} + +join_let_var_1: { + options = { + join_vars: true, + reduce_vars: true, + toplevel: true, + } + input: { + "use strict"; + var a = "foo"; + let b = "bar"; + for (var c of [ a, b ]) + console.log(c); + } + expect: { + "use strict"; + let a = "foo", b = "bar"; + for (var c of [ a, b ]) + console.log(c); + } + expect_stdout: [ + "foo", + "bar", + ] + node_version: ">=4" +} + +join_let_var_2: { + options = { + join_vars: true, + reduce_vars: true, + toplevel: true, + } + input: { + "use strict"; + let a = "foo"; + var b = "bar"; + for (let c of [ a, b ]) + console.log(c); + } + expect: { + "use strict"; + let a = "foo", b = "bar"; + for (let c of [ a, b ]) + console.log(c); + } + expect_stdout: [ + "foo", + "bar", + ] + node_version: ">=4" +} + +keep_let_var_1: { + options = { + join_vars: true, + reduce_vars: true, + toplevel: true, + } + input: { + "use strict"; + var a = "foo"; + let b = "bar"; + for (var c of [ a, b ]) + console.log(c); + function f() { + return a; + } + console.log(f(f)); + } + expect: { + "use strict"; + var a = "foo", c; + let b = "bar"; + for (c of [ a, b ]) + console.log(c); + function f() { + return a; + } + console.log(f(f)); + } + expect_stdout: [ + "foo", + "bar", + "foo", + ] + node_version: ">=4" +} + +keep_let_var_2: { + options = { + join_vars: true, + reduce_vars: true, + toplevel: true, + } + input: { + "use strict"; + let a = "foo"; + var b = "bar"; + for (let c of [ a, b ]) + console.log(c); + function f() { + return b; + } + console.log(f(f)); + } + expect: { + "use strict"; + let a = "foo"; + var b = "bar"; + for (let c of [ a, b ]) + console.log(c); + function f() { + return b; + } + console.log(f(f)); + } + expect_stdout: [ + "foo", + "bar", + "bar", + ] + node_version: ">=4" +} + +issue_4191: { + options = { + functions: true, + reduce_vars: true, + unused: true, + } + input: { + "use strict"; + { + let a = function() {}; + } + console.log(typeof a); + } + expect: { + "use strict"; + { + let a = function() {}; + } + console.log(typeof a); + } + expect_stdout: "undefined" + node_version: ">=4" +} + +issue_4197: { + options = { + collapse_vars: true, + } + input: { + "use strict"; + var a = 0; + try { + let b = function() { + a = 1; + b[1]; + }(); + } catch (e) { + console.log(a); + } + } + expect: { + "use strict"; + var a = 0; + try { + let b = function() { + a = 1; + b[1]; + }(); + } catch (e) { + console.log(a); + } + } + expect_stdout: "1" + node_version: ">=4" +} + +issue_4202: { + options = { + inline: true, + toplevel: true, + } + input: { + "use strict"; + { + let o = {}; + (function() { + function f() { + o.p = 42; + } + f(f); + })(); + console.log(o.p++); + } + } + expect: { + "use strict"; + { + let o = {}; + function f() { + o.p = 42; + } + f(f); + console.log(o.p++); + } + } + expect_stdout: "42" + node_version: ">=4" +} + +issue_4207: { + options = { + reduce_funcs: true, + reduce_vars: true, + unused: true, + } + input: { + "use strict"; + { + let a = function() {}; + console.log(a.length); + } + } + expect: { + "use strict"; + { + let a = function() {}; + console.log(a.length); + } + } + expect_stdout: "0" + node_version: ">=4" +} + +issue_4218: { + options = { + reduce_funcs: true, + reduce_vars: true, + toplevel: true, + unused: true, + } + input: { + "use strict"; + var a; + { + let a = function() {}; + var b = 0 * a; + } + console.log(typeof a, b); + } + expect: { + "use strict"; + var b = 0 * function() {}; + console.log(typeof void 0, b); + } + expect_stdout: "undefined NaN" + node_version: ">=4" +} + +issue_4210: { + options = { + reduce_vars: true, + varify: true, + } + input: { + "use strict"; + var a; + (function() { + try { + throw 42; + } catch (e) { + let a = typeof e; + console.log(a); + } finally { + return a = "foo"; + } + })(); + console.log(typeof a); + } + expect: { + "use strict"; + var a; + (function() { + try { + throw 42; + } catch (e) { + let a = typeof e; + console.log(a); + } finally { + return a = "foo"; + } + })(); + console.log(typeof a); + } + expect_stdout: [ + "number", + "string", + ] + node_version: ">=4" +} + +issue_4212_1: { + options = { + dead_code: true, + reduce_vars: true, + } + input: { + "use strict"; + console.log({ + get b() { + let a = 0; + return a /= 0; + } + }.b); + } + expect: { + "use strict"; + console.log({ + get b() { + let a = 0; + return a / 0; + } + }.b); + } + expect_stdout: "NaN" + node_version: ">=4" +} + +issue_4212_2: { + options = { + reduce_vars: true, + } + input: { + "use strict"; + console.log({ + get b() { + let a = 0; + return a /= 0; + } + }.b); + } + expect: { + "use strict"; + console.log({ + get b() { + let a = 0; + return a /= 0; + } + }.b); + } + expect_stdout: "NaN" + node_version: ">=4" +} + +skip_braces: { + beautify = { + beautify: true, + braces: true, + } + input: { + "use strict"; + if (console) + let a = console.log(typeof a); + } + expect_exact: [ + '"use strict";', + "", + "if (console) let a = console.log(typeof a);", + ] + expect_stdout: true + node_version: ">=4" +} + +issue_4225: { + options = { + side_effects: true, + } + input: { + "use strict"; + let a = void typeof b; + let b = 42; + console.log(a, b); + } + expect: { + "use strict"; + let a = void b; + let b = 42; + console.log(a, b); + } + expect_stdout: true + node_version: ">=4" +} + +issue_4229: { + options = { + ie: true, + side_effects: true, + } + input: { + "use strict"; + try { + (function f() { + f; + let f; + })(); + } catch (e) { + console.log("PASS"); + } + } + expect: { + "use strict"; + try { + (function f() { + f; + let f; + })(); + } catch (e) { + console.log("PASS"); + } + } + expect_stdout: "PASS" + node_version: ">=4" +} + +issue_4231: { + options = { + ie: true, + side_effects: true, + } + input: { + "use strict"; + typeof a == 0; + console.log(typeof function a() { + let a; + }); + } + expect: { + "use strict"; + console.log(typeof function a() { + let a; + }); + } + expect_stdout: "function" + node_version: ">=4" +} + +issue_4245: { + options = { + booleans: true, + } + input: { + "use strict"; + let a = f(); + function f() { + typeof a; + } + } + expect: { + "use strict"; + let a = f(); + function f() { + a, + 1; + } + } + expect_stdout: ReferenceError("a is not defined") + node_version: ">=4" +} + +issue_4248: { + options = { + collapse_vars: true, + } + input: { + var a = "FAIL"; + try { + (function() { + "use strict"; + a = "PASS"; + b[a]; + let b; + })(); + } catch (e) { + console.log(a); + } + } + expect: { + var a = "FAIL"; + try { + (function() { + "use strict"; + a = "PASS"; + b[a]; + let b; + })(); + } catch (e) { + console.log(a); + } + } + expect_stdout: "PASS" + node_version: ">=4" +} + +issue_4274_1: { + options = { + loops: true, + } + input: { + "use strict"; + for (;;) { + if (console.log("PASS")) { + let a; + } else { + break; + var a; + } + } + } + expect: { + "use strict"; + for (; console.log("PASS");) { + { + let a; + } + var a; + } + } + expect_stdout: "PASS" + node_version: ">=4" +} + +issue_4274_2: { + options = { + loops: true, + } + input: { + "use strict"; + for (;;) { + if (!console.log("PASS")) { + break; + var a; + } else { + let a; + } + } + } + expect: { + "use strict"; + for (; console.log("PASS");) { + { + let a; + } + var a; + } + } + expect_stdout: "PASS" + node_version: ">=4" +} + +issue_4276_1: { + options = { + unused: true, + } + input: { + "use strict"; + try { + let a = b, b; + console.log("FAIL"); + } catch (e) { + console.log("PASS"); + } + } + expect: { + "use strict"; + try { + let a = b, b; + console.log("FAIL"); + } catch (e) { + console.log("PASS"); + } + } + expect_stdout: "PASS" + node_version: ">=4" +} + +issue_4276_2: { + options = { + unused: true, + } + input: { + "use strict"; + try { + let a = f(), b; + console.log("FAIL"); + function f() { + return b; + } + } catch (e) { + console.log("PASS"); + } + } + expect: { + "use strict"; + try { + let a = f(), b; + console.log("FAIL"); + function f() { + return b; + } + } catch (e) { + console.log("PASS"); + } + } + expect_stdout: "PASS" + node_version: ">=4" +} + +issue_4290_1: { + options = { + unused: true, + } + input: { + "use strict"; + let a; + var a; + } + expect: { + "use strict"; + let a; + var a; + } + expect_stdout: true + node_version: ">=4" +} + +issue_4290_2: { + options = { + collapse_vars: true, + } + input: { + "use strict"; + try { + console.log(function(a) { + a = c; + let c; + return a; + }()); + } catch (e) { + console.log("PASS"); + } + } + expect: { + "use strict"; + try { + console.log(function(a) { + a = c; + let c; + return a; + }()); + } catch (e) { + console.log("PASS"); + } + } + expect_stdout: "PASS" + node_version: ">=4" +} + +issue_4305_1: { + options = { + reduce_vars: true, + unused: true, + } + input: { + (function() { + let arguments = function() { + while (console.log("PASS")); + }; + arguments(); + })(); + } + expect: { + (function() { + let arguments = function() { + while (console.log("PASS")); + }; + arguments(); + })(); + } + expect_stdout: true + node_version: ">=6" +} + +issue_4305_2: { + options = { + reduce_vars: true, + unused: true, + } + input: { + "use strict"; + (function(a) { + let a = function() { + while (console.log("aaaaa")); + }; + a(); + })(); + } + expect: { + "use strict"; + (function(a) { + let a = function() { + while (console.log("aaaaa")); + }; + a(); + })(); + } + expect_stdout: true + node_version: ">=4" +} + +issue_4438: { + options = { + if_return: true, + } + input: { + "use strict"; + function f() { + if (console) { + { + let a = console.log; + return void a("PASS"); + } + } + } + f(); + } + expect: { + "use strict"; + function f() { + if (console) { + let a = console.log; + a("PASS"); + } + } + f(); + } + expect_stdout: "PASS" + node_version: ">=4" +} + +issue_4531_1: { + mangle = { + ie: true, + toplevel: true, + } + input: { + "use strict"; + var a; + console.log(function a() { + let a; + var b; + }()); + } + expect: { + "use strict"; + var o; + console.log(function o() { + let o; + var t; + }()); + } + expect_stdout: "undefined" + node_version: ">=4" +} + +issue_4531_2: { + options = { + evaluate: true, + ie: true, + toplevel: true, + } + mangle = { + ie: true, + toplevel: true, + } + input: { + var a = console; + console.log(typeof a, function a() { + let { [console]: a } = 0 && a; + var b = console; + while (!b); + }()); + } + expect: { + var o = console; + console.log(typeof o, function o() { + let { [console]: o } = 0; + var e = console; + while (!e); + }()); + } + expect_stdout: "object undefined" + node_version: ">=6" +} + +issue_4689: { + options = { + sequences: true, + } + input: { + "use strict"; + var a = "PASS"; + console.log(a); + for (let a in 42); + } + expect: { + "use strict"; + var a = "PASS"; + console.log(a); + for (let a in 42); + } + expect_stdout: "PASS" + node_version: ">=4" +} + +issue_4691: { + options = { + conditionals: true, + if_return: true, + toplevel: true, + } + input: { + "use strict"; + function A() {} + A.prototype.f = function() { + if (!this) + return; + let a = "PA"; + function g(b) { + h(a + b); + } + [ "SS" ].forEach(function(c) { + g(c); + }); + }; + function h(d) { + console.log(d); + } + new A().f(); + } + expect: { + "use strict"; + function A() {} + A.prototype.f = function() { + if (this) { + let a = "PA"; + [ "SS" ].forEach(function(c) { + g(c); + }); + function g(b) { + h(a + b); + } + } + }; + function h(d) { + console.log(d); + } + new A().f(); + } + expect_stdout: "PASS" + node_version: ">=4" +} + +issue_4848: { + options = { + if_return: true, + } + input: { + "use strict"; + function f(a) { + a(function() { + console.log(b); + }); + if (!console) + return; + let b = "PASS"; + } + var g; + f(function(h) { + g = h; + }); + g(); + } + expect: { + "use strict"; + function f(a) { + a(function() { + console.log(b); + }); + if (!console) + return; + let b = "PASS"; + } + var g; + f(function(h) { + g = h; + }); + g(); + } + expect_stdout: "PASS" + node_version: ">=4" +} + +issue_4985: { + options = { + hoist_props: true, + reduce_vars: true, + toplevel: true, + } + input: { + "use strict"; + let a = { p: 42 }; + console.log(function() { + a; + }()); + } + expect: { + "use strict"; + let a = { p: 42 }; + console.log(function() { + a; + }()); + } + expect_stdout: "undefined" + node_version: ">=4" +} + +issue_5240: { + options = { + inline: true, + } + input: { + "use strict"; + function f() { + if (console) { + let g = function() { + e; + }, e; + (function() { + if (console) { + console.log(e); + var e = "FAIL"; + } + })(console.log(e)); + } + } + f(); + } + expect: { + "use strict"; + function f() { + if (console) { + let g = function() { + e; + }, e; + (function() { + if (console) { + console.log(e); + var e = "FAIL"; + } + })(console.log(e)); + } + } + f(); + } + expect_stdout: [ + "undefined", + "undefined", + ] + node_version: ">=4" +} + +issue_5254: { + options = { + inline: true, + toplevel: true, + } + input: { + "use strict"; + do { + (function() { + let a = console.log; + a && a("foo"); + })(); + } while (console.log("bar")); + } + expect: { + "use strict"; + do { + let a = console.log; + a && a("foo"); + } while (console.log("bar")); + } + expect_stdout: [ + "foo", + "bar", + ] + node_version: ">=4" +} + +issue_5260: { + options = { + collapse_vars: true, + } + input: { + "use strict"; + var a = "foo", o; + while (console.log("bar")); + o = { + baz: function(b) { + console.log(a, b); + }, + }; + for (let a in o) + o[a](a); + } + expect: { + "use strict"; + var a = "foo", o; + while (console.log("bar")); + o = { + baz: function(b) { + console.log(a, b); + }, + }; + for (let a in o) + o[a](a); + } + expect_stdout: [ + "bar", + "foo baz", + ] + node_version: ">=4" +} + +issue_5319: { + options = { + collapse_vars: true, + merge_vars: true, + } + input: { + "use strict"; + (function(a, c) { + var b = a, c = b; + { + let a = c; + console.log(c()); + } + })(function() { + return "PASS"; + }); + } + expect: { + "use strict"; + (function(a, c) { + var b = a, c; + { + let a = c = b; + console.log(c()); + } + })(function() { + return "PASS"; + }); + } + expect_stdout: "PASS" + node_version: ">=4" +} + +issue_5338: { + options = { + unused: true, + } + input: { + "use strict"; + let a = a; + } + expect: { + "use strict"; + a; + let a; + } + expect_stdout: ReferenceError("a is not defined") + node_version: ">=4" +} + +issue_5476: { + mangle = { + keep_fargs: true, + } + input: { + "use strict"; + console.log(function(n) { + let a; + }()); + } + expect: { + "use strict"; + console.log(function(n) { + let o; + }()); + } + expect_stdout: "undefined" + node_version: ">=4" +} + +issue_5591: { + options = { + dead_code: true, + if_return: true, + } + input: { + "use strict"; + function f(a) { + switch (console.log("foo")) { + case console.log("bar"): + if (console.log("baz")) + return; + else { + let a; + return; + } + break; + case null: + FAIL; + } + } + f(); + } + expect: { + "use strict"; + function f(a) { + switch (console.log("foo")) { + case console.log("bar"): + if (console.log("baz")) + return; + else { + let a; + return; + } + case null: + FAIL; + } + } + f(); + } + expect_stdout: [ + "foo", + "bar", + "baz", + ] + node_version: ">=4" +} + +issue_5741: { + options = { + inline: true, + join_vars: true, + reduce_vars: true, + } + input: { + "use strict"; + (function(a) { + let b = function() { + var c = a; + console.log(c); + }(); + function g() { + a++; + b; + } + })("PASS"); + } + expect: { + "use strict"; + (function(a) { + let b = (c = a, void console.log(c)); + var c; + function g() { + a++; + b; + } + })("PASS"); + } + expect_stdout: "PASS" + node_version: ">=4" +} + +issue_5745_1: { + options = { + join_vars: true, + reduce_vars: true, + toplevel: true, + } + input: { + "use strict"; + { + let f = function() { + return f && "PASS"; + }; + var a = f(); + } + a; + console.log(a); + } + expect: { + "use strict"; + { + let f = function() { + return f && "PASS"; + }; + var a = f(); + } + a; + console.log(a); + } + expect_stdout: "PASS" + node_version: ">=4" +} + +issue_5745_2: { + options = { + join_vars: true, + reduce_vars: true, + toplevel: true, + } + input: { + "use strict"; + { + let f = function() { + return f && "PASS"; + }; + var a = f(); + a; + console.log(a); + } + } + expect: { + "use strict"; + { + let f = function() { + return f && "PASS"; + }, a = f(); + a; + console.log(a); + } + } + expect_stdout: "PASS" + node_version: ">=4" +} + +issue_5756_1: { + options = { + join_vars: true, + loops: true, + reduce_vars: true, + toplevel: true, + } + input: { + "use strict"; + do { + function f() { + return b; + } + var a = "PASS".toString(); + let b; + console.log(a); + } while (!console); + } + expect: { + "use strict"; + do { + function f() { + return b; + } + let a = "PASS".toString(), b; + console.log(a); + } while (!console); + } + expect_stdout: "PASS" + node_version: ">=4" +} + +issue_5756_2: { + options = { + join_vars: true, + reduce_vars: true, + toplevel: true, + unused: true, + } + input: { + "use strict"; + function f() { + let a = console.log("PASS"); + { + var b; + for (var c in b) { + b; + var c = function() { + a; + }; + } + } + } + f(); + } + expect: { + "use strict"; + (function() { + let a = console.log("PASS"), b; + for (c in b) { + b; + var c = function() { + a; + }; + } + })(); + } + expect_stdout: "PASS" + node_version: ">=4" +} + +issue_5756_3: { + options = { + module: true, + reduce_vars: true, + toplevel: true, + unused: true, + varify: true, + } + input: { + "use strict"; + console.log(f()()); + function f() { + const a = "PASS"; + return function() { + return a; + }; + } + } + expect: { + "use strict"; + console.log(function() { + let a = "PASS"; + return function() { + return a; + }; + }()()); + } + expect_stdout: "PASS" + node_version: ">=4" +} + +issue_5759: { + options = { + collapse_vars: true, + inline: true, + join_vars: true, + reduce_vars: true, + unused: true, + } + input: { + "use strict"; + function f() { + for (var a in [ true ]) { + let b; + (function() { + var c = void 0; + b; + console.log(c); + var d = null; + console.log(c); + })(); + } + } + f(); + } + expect: { + "use strict"; + function f() { + for (var a in [ true ]) { + let b; + var c = c = void 0; + b; + console.log(c); + console.log(c); + } + } + f(); + } + expect_stdout: [ + "undefined", + "undefined", + ] + node_version: ">=4" +} diff --git a/test/compress/loops.js b/test/compress/loops.js index cdf1f045d9e..d5f9269a630 100644 --- a/test/compress/loops.js +++ b/test/compress/loops.js @@ -1,37 +1,45 @@ while_becomes_for: { - options = { loops: true }; + options = { + loops: true, + } input: { while (foo()) bar(); } expect: { - for (; foo(); ) bar(); + for (;foo();) bar(); } } drop_if_break_1: { - options = { loops: true }; + options = { + loops: true, + } input: { for (;;) if (foo()) break; } expect: { - for (; !foo();); + for (;!foo();); } } drop_if_break_2: { - options = { loops: true }; + options = { + loops: true, + } input: { for (;bar();) if (foo()) break; } expect: { - for (; bar() && !foo();); + for (;bar() && !foo();); } } drop_if_break_3: { - options = { loops: true }; + options = { + loops: true, + } input: { for (;bar();) { if (foo()) break; @@ -48,7 +56,10 @@ drop_if_break_3: { } drop_if_break_4: { - options = { loops: true, sequences: true }; + options = { + loops: true, + sequences: true, + } input: { for (;bar();) { x(); @@ -59,22 +70,26 @@ drop_if_break_4: { } } expect: { - for (; bar() && (x(), y(), !foo());) z(), k(); + for (;bar() && (x(), y(), !foo());) z(), k(); } } drop_if_else_break_1: { - options = { loops: true }; + options = { + loops: true, + } input: { for (;;) if (foo()) bar(); else break; } expect: { - for (; foo(); ) bar(); + for (;foo();) bar(); } } drop_if_else_break_2: { - options = { loops: true }; + options = { + loops: true, + } input: { for (;bar();) { if (foo()) baz(); @@ -82,12 +97,14 @@ drop_if_else_break_2: { } } expect: { - for (; bar() && foo();) baz(); + for (;bar() && foo();) baz(); } } drop_if_else_break_3: { - options = { loops: true }; + options = { + loops: true, + } input: { for (;bar();) { if (foo()) baz(); @@ -97,7 +114,7 @@ drop_if_else_break_3: { } } expect: { - for (; bar() && foo();) { + for (;bar() && foo();) { baz(); stuff1(); stuff2(); @@ -106,7 +123,10 @@ drop_if_else_break_3: { } drop_if_else_break_4: { - options = { loops: true, sequences: true }; + options = { + loops: true, + sequences: true, + } input: { for (;bar();) { x(); @@ -118,6 +138,1213 @@ drop_if_else_break_4: { } } expect: { - for (; bar() && (x(), y(), foo());) baz(), z(), k(); + for (;bar() && (x(), y(), foo());) baz(), z(), k(); + } +} + +parse_do_while_with_semicolon: { + options = { + loops: false, + } + input: { + do { + x(); + } while (false);y() + } + expect: { + do x(); while (false);y(); + } +} + +parse_do_while_without_semicolon: { + options = { + loops: false, + } + input: { + do { + x(); + } while (false)y() + } + expect: { + do x(); while (false);y(); + } +} + +evaluate: { + options = { + dead_code: true, + evaluate: true, + loops: true, + passes: 2, + side_effects: true, + } + input: { + while (true) { + a(); + } + while (false) { + b(); + } + do { + c(); + } while (true); + do { + d(); + } while (false); + } + expect: { + for (;;) + a(); + for (;;) + c(); + d(); + } +} + +issue_1532_1: { + options = { + evaluate: true, + loops: true, + } + input: { + function f(x, y) { + do { + if (x) break; + console.log(y); + } while (false); + } + f(null, "PASS"); + f(42, "FAIL"); + } + expect: { + function f(x, y) { + for (; !x && (console.log(y), false);); + } + f(null, "PASS"); + f(42, "FAIL"); + } + expect_stdout: "PASS" +} + +issue_1532_2: { + options = { + evaluate: true, + loops: true, + } + input: { + function f(x, y) { + do { + if (x) { + console.log(x); + break; + } + console.log(y); + } while (false); + } + f(null, "PASS"); + f(42, "FAIL"); + } + expect: { + function f(x, y) { + do { + if (x) { + console.log(x); + break; + } + } while (console.log(y), false); + } + f(null, "PASS"); + f(42, "FAIL"); + } + expect_stdout: [ + "PASS", + "42", + ] +} + +issue_186: { + beautify = { + beautify: false, + ie: false, + } + input: { + var x = 3; + if (foo()) + do + do + alert(x); + while (--x); + while (x); + else + bar(); + } + expect_exact: 'var x=3;if(foo())do{do{alert(x)}while(--x)}while(x);else bar();' +} + +issue_186_ie8: { + beautify = { + beautify: false, + ie: true, + } + input: { + var x = 3; + if (foo()) + do + do + alert(x); + while (--x); + while (x); + else + bar(); + } + expect_exact: 'var x=3;if(foo()){do{do{alert(x)}while(--x)}while(x)}else bar();' +} + +issue_186_beautify: { + beautify = { + beautify: true, + ie: false, + } + input: { + var x = 3; + if (foo()) + do + do + alert(x); + while (--x); + while (x); + else + bar(); + } + expect_exact: [ + 'var x = 3;', + '', + 'if (foo()) do {', + ' do {', + ' alert(x);', + ' } while (--x);', + '} while (x); else bar();', + ] +} + +issue_186_beautify_ie8: { + beautify = { + beautify: true, + ie: true, + } + input: { + var x = 3; + if (foo()) + do + do + alert(x); + while (--x); + while (x); + else + bar(); + } + expect_exact: [ + 'var x = 3;', + '', + 'if (foo()) {', + ' do {', + ' do {', + ' alert(x);', + ' } while (--x);', + ' } while (x);', + '} else bar();', + ] +} + +issue_186_braces: { + beautify = { + beautify: false, + braces: true, + ie: false, + } + input: { + var x = 3; + if (foo()) + do + do + alert(x); + while (--x); + while (x); + else + bar(); + } + expect_exact: 'var x=3;if(foo()){do{do{alert(x)}while(--x)}while(x)}else{bar()}' +} + +issue_186_braces_ie8: { + beautify = { + beautify: false, + braces: true, + ie: true, + } + input: { + var x = 3; + if (foo()) + do + do + alert(x); + while (--x); + while (x); + else + bar(); + } + expect_exact: 'var x=3;if(foo()){do{do{alert(x)}while(--x)}while(x)}else{bar()}' +} + +issue_186_beautify_braces: { + beautify = { + beautify: true, + braces: true, + ie: false, + } + input: { + var x = 3; + if (foo()) + do + do + alert(x); + while (--x); + while (x); + else + bar(); + } + expect_exact: [ + 'var x = 3;', + '', + 'if (foo()) {', + ' do {', + ' do {', + ' alert(x);', + ' } while (--x);', + ' } while (x);', + '} else {', + ' bar();', + '}', + ] +} + +issue_186_beautify_braces_ie8: { + beautify = { + beautify: true, + braces: true, + ie: true, + } + input: { + var x = 3; + if (foo()) + do + do + alert(x); + while (--x); + while (x); + else + bar(); + } + expect_exact: [ + 'var x = 3;', + '', + 'if (foo()) {', + ' do {', + ' do {', + ' alert(x);', + ' } while (--x);', + ' } while (x);', + '} else {', + ' bar();', + '}', + ] +} + +issue_1648: { + options = { + join_vars: true, + loops: true, + passes: 2, + sequences: true, + unused: true, + } + input: { + function f() { + x(); + var b = 1; + while (1); + } + } + expect_exact: "function f(){for(x();1;);}" +} + +do_switch: { + options = { + evaluate: true, + loops: true, + } + input: { + do { + switch (a) { + case b: + continue; + } + } while (false); + } + expect: { + do { + switch (a) { + case b: + continue; + } + } while (false); + } +} + +in_parentheses_1: { + input: { + for (("foo" in {});0;); + } + expect_exact: 'for(("foo"in{});0;);' +} + +in_parentheses_2: { + input: { + for ((function(){ "foo" in {}; });0;); + } + expect_exact: 'for(function(){"foo"in{}};0;);' +} + +init_side_effects: { + options = { + loops: true, + side_effects: true, + } + input: { + for (function() {}(), i = 0; i < 5; i++) console.log(i); + for (function() {}(); i < 10; i++) console.log(i); + } + expect: { + for (i = 0; i < 5; i++) console.log(i); + for (; i < 10; i++) console.log(i); + } + expect_stdout: true +} + +dead_code_condition: { + options = { + dead_code: true, + evaluate: true, + loops: true, + sequences: true, + } + input: { + for (var a = 0, b = 5; (a += 1, 3) - 3 && b > 0; b--) { + var c = function() { + b--; + }(a++); + } + console.log(a); + } + expect: { + var a = 0, b = 5; + var c; + a += 1, 0, + console.log(a); + } + expect_stdout: "1" +} + +issue_2740_1: { + options = { + dead_code: true, + loops: true, + } + input: { + for (;;) break; + for (a();;) break; + for (;b();) break; + for (c(); d();) break; + for (;;e()) break; + for (f();; g()) break; + for (;h(); i()) break; + for (j(); k(); l()) break; + } + expect: { + a(); + b(); + c(); + d(); + f(); + h(); + j(); + k(); + } +} + +issue_2740_2: { + options = { + dead_code: true, + loops: true, + passes: 2, + unused: true, + } + input: { + L1: while (x()) { + break L1; + } + } + expect: { + x(); + } +} + +issue_2740_3: { + options = { + dead_code: true, + loops: true, + unused: true, + } + input: { + L1: for (var x = 0; x < 3; x++) { + L2: for (var y = 0; y < 2; y++) { + break L1; + } + } + console.log(x, y); + } + expect: { + L1: for (var x = 0; x < 3; x++) { + var y = 0; + if (y < 2) + break L1; + } + console.log(x, y); + } + expect_stdout: "0 0" +} + +issue_2740_4: { + options = { + dead_code: true, + loops: true, + passes: 2, + unused: true, + } + input: { + L1: for (var x = 0; x < 3; x++) { + L2: for (var y = 0; y < 2; y++) { + break L2; + } + } + console.log(x, y); + } + expect: { + for (var x = 0; x < 3; x++) { + var y = 0; + y < 2; + } + console.log(x, y); + } + expect_stdout: "3 0" +} + +issue_2740_5: { + options = { + dead_code: true, + loops: true, + passes: 2, + unused: true, + } + input: { + L1: for (var x = 0; x < 3; x++) { + break L1; + L2: for (var y = 0; y < 2; y++) { + break L2; + } + } + console.log(x, y); + } + expect: { + var x = 0; + x < 3; + var y; + console.log(x,y); + } + expect_stdout: "0 undefined" +} + +issue_2904: { + options = { + join_vars: true, + loops: true, + } + input: { + var a = 1; + do { + console.log(a); + } while (--a); + } + expect: { + for (var a = 1; console.log(a), --a;); + } + expect_stdout: "1" +} + +issue_3371: { + options = { + functions: true, + join_vars: true, + loops: true, + reduce_vars: true, + unused: true, + } + input: { + (function() { + var a = function() { + console.log("PASS"); + }; + while (a()); + })(); + } + expect: { + (function() { + function a() { + console.log("PASS"); + } + for (;a();); + })(); + } + expect_stdout: "PASS" +} + +step: { + options = { + loops: true, + side_effects: true, + } + input: { + for (var i = 0; i < 42; "foo", i++, "bar"); + console.log(i); + } + expect: { + for (var i = 0; i < 42; i++); + console.log(i); + } + expect_stdout: "42" +} + +empty_for_in: { + options = { + loops: true, + toplevel: true, + unused: true, + } + input: { + for (var a in [ 1, 2, 3 ]) { + var b = a + 1; + } + } + expect: {} + expect_warnings: [ + "WARN: Dropping unused variable b [test/compress/loops.js:2,16]", + "INFO: Dropping unused loop variable a [test/compress/loops.js:1,17]", + ] +} + +empty_for_in_used: { + options = { + loops: true, + toplevel: true, + unused: true, + } + input: { + for (var a in [ 1, 2, 3 ]) { + var b = a + 1; + } + console.log(a); + } + expect: { + for (var a in [ 1, 2, 3 ]); + console.log(a); + } + expect_stdout: "2" + expect_warnings: [ + "WARN: Dropping unused variable b [test/compress/loops.js:2,16]", + ] +} + +empty_for_in_side_effects: { + options = { + loops: true, + toplevel: true, + unused: true, + } + input: { + for (var a in { + foo: console.log("PASS") + }) { + var b = a + "bar"; + } + } + expect: { + console.log("PASS"); + } + expect_stdout: "PASS" + expect_warnings: [ + "WARN: Dropping unused variable b [test/compress/loops.js:4,16]", + "INFO: Dropping unused loop variable a [test/compress/loops.js:1,17]", + "WARN: Side effects in object of for-in loop [test/compress/loops.js:2,17]", + ] +} + +empty_for_in_prop_init: { + options = { + loops: true, + pure_getters: "strict", + unused: true, + } + input: { + console.log(function f() { + var a = "bar"; + for ((a, f)[a] in console.log("foo")); + return a; + }()); + } + expect: { + console.log(function() { + var a = "bar"; + console.log("foo"); + return a; + }()); + } + expect_stdout: [ + "foo", + "bar", + ] + expect_warnings: [ + "INFO: Dropping unused loop variable f [test/compress/loops.js:3,21]", + "WARN: Side effects in object of for-in loop [test/compress/loops.js:3,30]", + ] +} + +for_of: { + input: { + var a = [ "PASS", 42 ]; + a.p = "FAIL"; + for (a of (null, a)) + console.log(a); + } + expect_exact: 'var a=["PASS",42];a.p="FAIL";for(a of(null,a))console.log(a);' + expect_stdout: true + node_version: ">=0.12" +} + +for_async_of: { + input: { + var async = [ "PASS", 42 ]; + async.p = "FAIL"; + for (async of (null, async)) + console.log(async); + } + expect_exact: 'var async=["PASS",42];async.p="FAIL";for(async of(null,async))console.log(async);' + expect_stdout: true + node_version: ">=0.12 <16" +} + +for_of_regexp: { + input: { + for (var a of /foo/); + } + expect_exact: "for(var a of/foo/);" +} + +for_await_of_regexp: { + input: { + for await (var a of /foo/); + } + expect_exact: "for await(var a of/foo/);" +} + +issue_3631_1: { + options = { + dead_code: true, + evaluate: true, + loops: true, + reduce_vars: true, + toplevel: true, + unused: true, + } + input: { + var c = 0; + L: do { + for (;;) continue L; + var b = 1; + } while (b && c++); + console.log(c); + } + expect: { + var c = 0; + do { + var b; + } while (b && c++); + console.log(c); + } + expect_stdout: "0" +} + +issue_3631_2: { + options = { + dead_code: true, + evaluate: true, + loops: true, + reduce_vars: true, + toplevel: true, + unused: true, + } + input: { + L: for (var a = 1; a--; console.log(b)) { + for (;;) continue L; + var b = "FAIL"; + } + } + expect: { + for (var a = 1; a--; console.log(b)) + var b; + } + expect_stdout: "undefined" +} + +loop_if_break: { + options = { + dead_code: true, + loops: true, + } + input: { + function f(a, b) { + try { + while (a) { + if (b) { + break; + var c = 42; + console.log(c); + } else { + var d = false; + throw d; + } + } + } catch (e) { + console.log("E:", e); + } + console.log(a, b, c, d); + } + f(0, 0); + f(0, 1); + f(1, 0); + f(1, 1); + } + expect: { + function f(a, b) { + try { + for (;a && !b;) { + var d = false; + throw d; + var c; + } + } catch (e) { + console.log("E:", e); + } + console.log(a, b, c, d); + } + f(0, 0); + f(0, 1); + f(1, 0); + f(1, 1); + } + expect_stdout: [ + "0 0 undefined undefined", + "0 1 undefined undefined", + "E: false", + "1 0 undefined false", + "1 1 undefined undefined", + ] +} + +loop_return: { + options = { + dead_code: true, + loops: true, + } + input: { + function f(a) { + while (a) return 42; + return "foo"; + } + console.log(f(0), f(1)); + } + expect: { + function f(a) { + if (a) return 42; + return "foo"; + } + console.log(f(0), f(1)); + } + expect_stdout: "foo 42" +} + +issue_3634_1: { + options = { + loops: true, + } + input: { + var b = 0; + L: while (++b < 2) + while (1) + if (b) break L; + console.log(b); + } + expect: { + var b = 0; + L: for (;++b < 2;) + for (;1;) + if (b) break L; + console.log(b); + } + expect_stdout: "1" +} + +issue_3634_2: { + options = { + loops: true, + } + input: { + var b = 0; + L: while (++b < 2) + while (1) + if (!b) + continue L; + else + break L; + console.log(b); + } + expect: { + var b = 0; + L: for (;++b < 2;) + for (;1;) + if (!b) + continue L; + else + break L; + console.log(b); + } + expect_stdout: "1" +} + +issue_4075: { + options = { + loops: true, + unused: true, + } + input: { + var a = "FAIL"; + (function() { + for (a in { PASS: 0 }); + })() + console.log(a); + } + expect: { + var a = "FAIL"; + (function() { + for (a in { PASS: 0 }); + })() + console.log(a); + } + expect_stdout: "PASS" +} + +issue_4082: { + options = { + keep_fargs: false, + loops: true, + unused: true, + } + input: { + var a = "PASS"; + (function(a) { + for (a in "foo") + var b; + })(); + console.log(a); + } + expect: { + var a = "PASS"; + (function(a) { + for (a in "foo"); + })(); + console.log(a); + } + expect_stdout: "PASS" +} + +issue_4084: { + options = { + keep_fargs: false, + loops: true, + reduce_vars: true, + unused: true, + } + input: { + console.log(function() { + function f(a) { + var b = a++; + for (a in "foo"); + } + f(); + return typeof a; + }()); + } + expect: { + console.log(function() { + (function() { + 0; + })(); + return typeof a; + }()); + } + expect_stdout: "undefined" +} + +issue_4091_1: { + options = { + loops: true, + toplevel: true, + unused: true, + } + input: { + try { + throw "FAIL"; + } catch (e) { + for (var e in 42); + } + console.log(e && e); + } + expect: { + try { + throw "FAIL"; + } catch (e) { + var e; + } + console.log(e && e); + } + expect_stdout: "undefined" +} + +issue_4091_2: { + options = { + loops: true, + toplevel: true, + unused: true, + } + input: { + try { + throw "FAIL"; + } catch (e) { + for (e in 42); + var e; + } + console.log(e && e); + } + expect: { + try { + throw "FAIL"; + } catch (e) { + var e; + } + console.log(e && e); + } + expect_stdout: "undefined" +} + +issue_4182_1: { + options = { + loops: true, + } + input: { + (function() { + do { + try { + return; + } finally { + continue; + } + console.log("FAIL"); + } while (0); + console.log("PASS"); + })(); + } + expect: { + (function() { + do { + try { + return; + } finally { + continue; + } + console.log("FAIL"); + } while (0); + console.log("PASS"); + })(); + } + expect_stdout: "PASS" +} + +issue_4182_2: { + options = { + loops: true, + } + input: { + (function() { + L: do { + do { + try { + return; + } finally { + continue L; + } + console.log("FAIL"); + } while (0); + console.log("FAIL"); + } while (0); + console.log("PASS"); + })(); + } + expect: { + (function() { + L: do { + do { + try { + return; + } finally { + continue L; + } + } while (console.log("FAIL"), 0); + console.log("FAIL"); + } while (0); + console.log("PASS"); + })(); + } + expect_stdout: "PASS" +} + +do_continue: { + options = { + loops: true, + } + input: { + try { + do { + continue; + } while ([ A ]); + } catch (e) { + console.log("PASS"); + } + } + expect: { + try { + do { + continue; + } while ([ A ]); + } catch (e) { + console.log("PASS"); + } + } + expect_stdout: "PASS" +} + +issue_4240: { + options = { + loops: true, + reduce_funcs: true, + reduce_vars: true, + unused: true, + } + input: { + (function(a) { + function f() { + var o = { PASS: 42 }; + for (a in o); + } + (function() { + if (f()); + })(); + console.log(a); + })(); + } + expect: { + (function(a) { + (function() { + if (function() { + for (a in { PASS: 42 }); + }()); + })(); + console.log(a); + })(); + } + expect_stdout: "PASS" +} + +issue_4355: { + options = { + dead_code: true, + evaluate: true, + loops: true, + passes: 2, + side_effects: true, + unused: true, + } + input: { + while (function() { + var a; + for (a in console.log("PASS")) + var b = 0; + }()) + var c; + } + expect: { + (function() { + console.log("PASS"); + })(); + var c; + } + expect_stdout: "PASS" +} + +issue_4564: { + options = { + loops: true, + unused: true, + } + input: { + try { + throw null; + } catch (a) { + var a; + (function() { + for (a in "foo"); + })(); + console.log(a); + } + } + expect: { + try { + throw null; + } catch (a) { + var a; + (function() { + for (a in "foo"); + })(); + console.log(a); + } } + expect_stdout: "2" } diff --git a/test/compress/max_line_len.js b/test/compress/max_line_len.js new file mode 100644 index 00000000000..c72fa5efe4f --- /dev/null +++ b/test/compress/max_line_len.js @@ -0,0 +1,98 @@ +too_short: { + beautify = { + max_line_len: 10, + } + input: { + function f(a) { + return { c: 42, d: a(), e: "foo"}; + } + } + expect_exact: [ + "function f(", + "a){return{", + "c:42,d:a(", + '),e:"foo"}', + "}", + ] + expect_warnings: [ + "WARN: Output exceeds 10 characters", + ] +} + +just_enough: { + beautify = { + max_line_len: 14, + } + input: { + function f(a) { + return { c: 42, d: a(), e: "foo"}; + } + } + expect_exact: [ + "function f(a){", + "return{c:42,", + 'd:a(),e:"foo"}', + "}", + ] + expect_warnings: [] +} + +drop_semicolon: { + beautify = { + max_line_len: 5, + semicolons: true, + } + input: { + var a; + console.log(a || 42); + } + expect_exact: [ + "var a", + "console.log(", + "a||42", + ");", + ] + expect_stdout: "42" + expect_warnings: [ + "WARN: Output exceeds 5 characters", + ] +} + +template_newline: { + beautify = { + max_line_len: 2, + } + input: { + console.log(`foo +bar`); + } + expect_exact: [ + "console.log(", + "`foo", + "bar`", + ");", + ] + expect_stdout: [ + "foo", + "bar", + ] + expect_warnings: [ + "WARN: Output exceeds 2 characters", + ] + node_version: ">=4" +} + +issue_304: { + beautify = { + max_line_len: 10, + } + input: { + var a = 0, b = 0, c = 0, d = 0, e = 0; + } + expect_exact: [ + "var a=0,", + "b=0,c=0,", + "d=0,e=0;", + ] + expect_warnings: [] +} diff --git a/test/compress/merge_vars.js b/test/compress/merge_vars.js new file mode 100644 index 00000000000..e00bc3b9ed9 --- /dev/null +++ b/test/compress/merge_vars.js @@ -0,0 +1,4002 @@ +merge: { + options = { + merge_vars: true, + toplevel: false, + } + input: { + var a = "foo"; + console.log(a); + function f(b) { + var c; + console.log(b); + c = "bar"; + console.log(c); + } + f("baz"); + var d = "moo"; + console.log(d); + } + expect: { + var a = "foo"; + console.log(a); + function f(b) { + var b; + console.log(b); + b = "bar"; + console.log(b); + } + f("baz"); + var d = "moo"; + console.log(d); + } + expect_stdout: [ + "foo", + "baz", + "bar", + "moo", + ] +} + +merge_toplevel: { + options = { + merge_vars: true, + toplevel: true, + } + input: { + var a = "foo"; + console.log(a); + function f(b) { + var c; + console.log(b); + c = "bar"; + console.log(c); + } + f("baz"); + var d = "moo"; + console.log(d); + } + expect: { + var a = "foo"; + console.log(a); + function f(b) { + var b; + console.log(b); + b = "bar"; + console.log(b); + } + f("baz"); + var a = "moo"; + console.log(a); + } + expect_stdout: [ + "foo", + "baz", + "bar", + "moo", + ] +} + +segment: { + options = { + merge_vars: true, + toplevel: true, + } + input: { + var a = "foo"; + console.log(a); + for (var c, i = 0; i < 1; i++) { + var b = "bar"; + console.log(b); + c = "baz"; + console.log(c); + } + var d = "moo"; + console.log(d); + } + expect: { + var a = "foo"; + console.log(a); + for (var b, i = 0; i < 1; i++) { + var b = "bar"; + console.log(b); + b = "baz"; + console.log(b); + } + var a = "moo"; + console.log(a); + } + expect_stdout: [ + "foo", + "bar", + "baz", + "moo", + ] +} + +init_scope_vars: { + options = { + merge_vars: true, + unsafe_proto: true, + } + input: { + Function.prototype.call(); + } + expect: { + (function() {}).call(); + } + expect_stdout: true +} + +binary_branch: { + options = { + merge_vars: true, + } + input: { + console.log(function(a) { + var b = "FAIL", c; + a && (c = b); + return c || "PASS"; + }()); + } + expect: { + console.log(function(a) { + var b = "FAIL", c; + a && (c = b); + return c || "PASS"; + }()); + } + expect_stdout: "PASS" +} + +conditional_branch: { + options = { + merge_vars: true, + } + input: { + console.log(function(a) { + var b = "FAIL", c; + a ? (c = b) : void 0; + return c || "PASS"; + }()); + } + expect: { + console.log(function(a) { + var b = "FAIL", c; + a ? (c = b) : void 0; + return c || "PASS"; + }()); + } + expect_stdout: "PASS" +} + +conditional_chain_1: { + options = { + merge_vars: true, + } + input: { + function f(a, b) { + var c, d; + if (a && (c = a)) + console.log(c); + else + b || (d = b) ? console.log("foo") : console.log(d); + } + f("", null); + f("", true); + f(42, null); + f(42, true); + } + expect: { + function f(a, b) { + var a, a; + if (a && (a = a)) + console.log(a); + else + b || (a = b) ? console.log("foo") : console.log(a); + } + f("", null); + f("", true); + f(42, null); + f(42, true); + } + expect_stdout: [ + "null", + "foo", + "42", + "42", + ] +} + +conditional_chain_2: { + options = { + merge_vars: true, + } + input: { + function f(a, b) { + var c, d; + if (a && (c = a)) + console.log(c); + else + b || (d = b) ? console.log(c) : console.log(d); + } + f("", null); + f("", true); + f(42, null); + f(42, true); + } + expect: { + function f(a, b) { + var c, a; + if (a && (c = a)) + console.log(c); + else + b || (a = b) ? console.log(c) : console.log(a); + } + f("", null); + f("", true); + f(42, null); + f(42, true); + } + expect_stdout: [ + "null", + "undefined", + "42", + "42", + ] +} + +conditional_chain_3: { + options = { + merge_vars: true, + } + input: { + function f(a, b) { + var c, d; + if (a && (c = a) || b || (d = b)) + console.log(c); + else + console.log(d); + } + f("", null); + f("", true); + f(42, null); + f(42, true); + } + expect: { + function f(a, b) { + var c, a; + if (a && (c = a) || b || (a = b)) + console.log(c); + else + console.log(a); + } + f("", null); + f("", true); + f(42, null); + f(42, true); + } + expect_stdout: [ + "null", + "undefined", + "42", + "42", + ] +} + +conditional_chain_4: { + options = { + merge_vars: true, + } + input: { + function f(a, b) { + var c, d; + if (a && b ? c = a : d = b) + console.log(c); + else + console.log(d); + } + f("", null); + f("", true); + f(42, null); + f(42, true); + } + expect: { + function f(a, b) { + var c, d; + if (a && b ? c = a : d = b) + console.log(c); + else + console.log(d); + } + f("", null); + f("", true); + f(42, null); + f(42, true); + } + expect_stdout: [ + "null", + "undefined", + "null", + "42", + ] +} + +if_branch: { + options = { + merge_vars: true, + } + input: { + console.log(function(a) { + var b = "FAIL", c; + if (a) c = b; + return c || "PASS"; + }()); + } + expect: { + console.log(function(a) { + var b = "FAIL", c; + if (a) c = b; + return c || "PASS"; + }()); + } + expect_stdout: "PASS" +} + +switch_branch: { + options = { + merge_vars: true, + } + input: { + console.log(function(a) { + var b = "FAIL", c; + switch (a) { + case 1: + c = b; + break; + } + return c || "PASS"; + }()); + } + expect: { + console.log(function(a) { + var b = "FAIL", c; + switch (a) { + case 1: + c = b; + break; + } + return c || "PASS"; + }()); + } + expect_stdout: "PASS" +} + +try_branch: { + options = { + merge_vars: true, + } + input: { + console.log(function(a) { + var b = "FAIL", c; + try { + a && F(); + } catch (e) { + c = b; + } + return c || "PASS"; + }()); + } + expect: { + console.log(function(a) { + var b = "FAIL", c; + try { + a && F(); + } catch (e) { + c = b; + } + return c || "PASS"; + }()); + } + expect_stdout: "PASS" +} + +read_before_assign_1: { + options = { + inline: true, + merge_vars: true, + sequences: true, + side_effects: true, + toplevel: true, + } + input: { + var c = 0; + c = 0; + (function() { + var a = console.log(++a); + a; + })(); + c; + } + expect: { + var c = 0; + var a; + c = 0, + a = console.log(++a); + } + expect_stdout: "NaN" +} + +read_before_assign_2: { + options = { + dead_code: true, + loops: true, + merge_vars: true, + } + input: { + console.log(function(a, a) { + while (b) + return "FAIL"; + var b = 1; + return "PASS"; + }(0, [])); + } + expect: { + console.log(function(a, a) { + if (b) + return "FAIL"; + var b = 1; + return "PASS"; + }(0, [])); + } + expect_stdout: "PASS" +} + +collapse_vars_1: { + options = { + collapse_vars: true, + merge_vars: true, + toplevel: true, + } + input: { + var a = a && a.p; + var b = "PASS"; + var b = b && console.log(b); + } + expect: { + var a = a && a.p; + var a; + var a = (a = "PASS") && console.log(a); + } + expect_stdout: "PASS" +} + +collapse_vars_2: { + options = { + collapse_vars: true, + merge_vars: true, + } + input: { + "use strict"; + var log = console.log; + (function g(a) { + var b = a; + var c = Math.random(); + var c = b; + log(c); + return c; + })("PASS"); + } + expect: { + "use strict"; + var log = console.log; + (function g(a) { + var a = a; + var c = Math.random(); + var c; + log(c = a); + return c; + })("PASS"); + } + expect_stdout: "PASS" +} + +not_redefined: { + options = { + inline: true, + join_vars: true, + merge_vars: true, + reduce_vars: true, + unused: true, + varify: true, + } + input: { + var log = console.log; + (function() { + return f("PASS"); + function f(a) { + const b = a; + const c = log(b); + const d = log; + c && log(d); + } + })(); + } + expect: { + var log = console.log; + (function() { + return a = "PASS", + a = log(a), + d = log, + void (a && log(d)); + var a, d; + })(); + } + expect_stdout: "PASS" +} + +issue_4103: { + options = { + merge_vars: true, + side_effects: true, + toplevel: true, + } + input: { + function f(a) { + console.log(a); + } + var b = 0; + var c = f(b++ + (c %= 1 >> console.log(c = 0))); + b; + } + expect: { + function f(a) { + console.log(a); + } + var b = 0; + var c = f(b++ + (c %= 1 >> console.log(c = 0))); + } + expect_stdout: [ + "0", + "NaN", + ] +} + +issue_4107_1: { + options = { + keep_fargs: false, + merge_vars: true, + reduce_vars: true, + unused: true, + } + input: { + (function() { + function f(b, b, c) { + var d = 1 && a, a = console || c; + console.log(typeof a); + } + f(); + })(); + console.log(typeof a); + } + expect: { + (function() { + (function(c) { + c = console || c; + console.log(typeof c); + })(); + })(); + console.log(typeof a); + } + expect_stdout: [ + "object", + "undefined", + ] +} + +issue_4107_2: { + options = { + keep_fargs: false, + merge_vars: true, + reduce_vars: true, + unused: true, + } + input: { + (function() { + function f(b, b, a) { + var d = 1 && c, c = console || a; + console.log(typeof c); + } + f(); + })(); + console.log(typeof a); + } + expect: { + (function() { + (function(a) { + a = console || a; + console.log(typeof a); + })(); + })(); + console.log(typeof a); + } + expect_stdout: [ + "object", + "undefined", + ] +} + +issue_4109: { + options = { + ie: true, + merge_vars: true, + toplevel: true, + } + input: { + var a = "foo"; + try { + throw "bar"; + } catch (e) { + console.log(e); + } finally { + var o = a; + for (var k in o); + (function() { + a++; + }); + } + console.log(a); + } + expect: { + var a = "foo"; + try { + throw "bar"; + } catch (e) { + console.log(e); + } finally { + var o = a; + for (var k in o); + (function() { + a++; + }); + } + console.log(a); + } + expect_stdout: [ + "bar", + "foo", + ] +} + +issue_4110: { + options = { + merge_vars: true, + toplevel: true, + } + input: { + while (a) + var c; + var b, a = c += b = a; + console.log(b); + } + expect: { + while (a) + var c; + var b, a = c += b = a; + console.log(b); + } + expect_stdout: "undefined" +} + +issue_4111: { + options = { + join_vars: true, + loops: true, + merge_vars: true, + toplevel: true, + } + input: { + var a = 0; + if (a) + a = 0; + else + for (var b = 0; --b && ++a < 2;) { + var o = console, k; + for (k in o); + } + console.log(a); + } + expect: { + var a = 0; + if (a) + a = 0; + else + for (var b = 0; --b && ++a < 2;) { + var o = console, k; + for (k in o); + } + console.log(a); + } + expect_stdout: "2" +} + +issue_4112: { + options = { + functions: true, + merge_vars: true, + reduce_vars: true, + unused: true, + } + input: { + console.log(typeof function() { + try { + throw 42; + } catch (e) { + var o = e; + for (e in o); + var a = function() {}; + console.log(typeof a); + return a; + } + }()); + } + expect: { + console.log(typeof function() { + try { + throw 42; + } catch (e) { + var o = e; + for (e in o); + function a() {} + console.log(typeof a); + return a; + } + }()); + } + expect_stdout: [ + "function", + "function", + ] +} + +issue_4115: { + options = { + merge_vars: true, + toplevel: true, + } + input: { + L: { + var o = typeof console; + for (var k in o) + break L; + var a = 0; + } + console.log(typeof a); + } + expect: { + L: { + var o = typeof console; + for (var k in o) + break L; + var a = 0; + } + console.log(typeof a); + } + expect_stdout: "undefined" +} + +cross_branch_1_1: { + options = { + merge_vars: true, + } + input: { + var a; + function f() { + var x, y; + if (a) + x = "foo"; + console.log(x); + y = "bar"; + console.log(y); + } + a = 0; + f(); + a = 1; + f(); + } + expect: { + var a; + function f() { + var x, x; + if (a) + x = "foo"; + console.log(x); + x = "bar"; + console.log(x); + } + a = 0; + f(); + a = 1; + f(); + } + expect_stdout: [ + "undefined", + "bar", + "foo", + "bar", + ] +} + +cross_branch_1_2: { + options = { + merge_vars: true, + } + input: { + var a; + function f() { + var x, y; + if (a) { + x = "foo"; + console.log(x); + } + y = "bar"; + console.log(y); + } + a = 0; + f(); + a = 1; + f(); + } + expect: { + var a; + function f() { + var x, x; + if (a) { + x = "foo"; + console.log(x); + } + x = "bar"; + console.log(x); + } + a = 0; + f(); + a = 1; + f(); + } + expect_stdout: [ + "bar", + "foo", + "bar", + ] +} + +cross_branch_1_3: { + options = { + merge_vars: true, + } + input: { + var a; + function f() { + var x, y; + if (a) { + x = "foo"; + console.log(x); + y = "bar"; + } + console.log(y); + } + a = 0; + f(); + a = 1; + f(); + } + expect: { + var a; + function f() { + var x, x; + if (a) { + x = "foo"; + console.log(x); + x = "bar"; + } + console.log(x); + } + a = 0; + f(); + a = 1; + f(); + } + expect_stdout: [ + "undefined", + "foo", + "bar", + ] +} + +cross_branch_1_4: { + options = { + merge_vars: true, + } + input: { + var a; + function f() { + var x, y; + x = "foo"; + if (a) + console.log(x); + y = "bar"; + console.log(y); + } + a = 0; + f(); + a = 1; + f(); + } + expect: { + var a; + function f() { + var x, x; + x = "foo"; + if (a) + console.log(x); + x = "bar"; + console.log(x); + } + a = 0; + f(); + a = 1; + f(); + } + expect_stdout: [ + "bar", + "foo", + "bar", + ] +} + +cross_branch_1_5: { + options = { + merge_vars: true, + } + input: { + var a; + function f() { + var x, y; + x = "foo"; + if (a) { + console.log(x); + y = "bar"; + } + console.log(y); + } + a = 0; + f(); + a = 1; + f(); + } + expect: { + var a; + function f() { + var x, y; + x = "foo"; + if (a) { + console.log(x); + y = "bar"; + } + console.log(y); + } + a = 0; + f(); + a = 1; + f(); + } + expect_stdout: [ + "undefined", + "foo", + "bar", + ] +} + +cross_branch_1_6: { + options = { + merge_vars: true, + } + input: { + var a; + function f() { + var x, y; + x = "foo"; + if (a) { + console.log(x); + y = "bar"; + console.log(y); + } + } + a = 0; + f(); + a = 1; + f(); + } + expect: { + var a; + function f() { + var x, x; + x = "foo"; + if (a) { + console.log(x); + x = "bar"; + console.log(x); + } + } + a = 0; + f(); + a = 1; + f(); + } + expect_stdout: [ + "foo", + "bar", + ] +} + +cross_branch_1_7: { + options = { + merge_vars: true, + } + input: { + var a; + function f() { + var x, y; + x = "foo"; + console.log(x); + if (a) + y = "bar"; + console.log(y); + } + a = 0; + f(); + a = 1; + f(); + } + expect: { + var a; + function f() { + var x, y; + x = "foo"; + console.log(x); + if (a) + y = "bar"; + console.log(y); + } + a = 0; + f(); + a = 1; + f(); + } + expect_stdout: [ + "foo", + "undefined", + "foo", + "bar", + ] +} + +cross_branch_1_8: { + options = { + merge_vars: true, + } + input: { + var a; + function f() { + var x, y; + x = "foo"; + console.log(x); + if (a) { + y = "bar"; + console.log(y); + } + } + a = 0; + f(); + a = 1; + f(); + } + expect: { + var a; + function f() { + var x, x; + x = "foo"; + console.log(x); + if (a) { + x = "bar"; + console.log(x); + } + } + a = 0; + f(); + a = 1; + f(); + } + expect_stdout: [ + "foo", + "foo", + "bar", + ] +} + +cross_branch_1_9: { + options = { + merge_vars: true, + } + input: { + var a; + function f() { + var x, y; + x = "foo"; + console.log(x); + y = "bar"; + if (a) + console.log(y); + } + a = 0; + f(); + a = 1; + f(); + } + expect: { + var a; + function f() { + var x, x; + x = "foo"; + console.log(x); + x = "bar"; + if (a) + console.log(x); + } + a = 0; + f(); + a = 1; + f(); + } + expect_stdout: [ + "foo", + "foo", + "bar", + ] +} + +cross_branch_2a_1: { + options = { + merge_vars: true, + } + input: { + var a, b; + function f() { + var x, y; + if (a) { + if (b) + x = "foo"; + console.log(x); + } + y = "bar"; + console.log(y); + } + a = 0, b = 0; + f(); + a = 1, b = 0; + f(); + a = 0, b = 1; + f(); + a = 1, b = 1; + f(); + } + expect: { + var a, b; + function f() { + var x, x; + if (a) { + if (b) + x = "foo"; + console.log(x); + } + x = "bar"; + console.log(x); + } + a = 0, b = 0; + f(); + a = 1, b = 0; + f(); + a = 0, b = 1; + f(); + a = 1, b = 1; + f(); + } + expect_stdout: [ + "bar", + "undefined", + "bar", + "bar", + "foo", + "bar", + ] +} + +cross_branch_2a_2: { + options = { + merge_vars: true, + } + input: { + var a, b; + function f() { + var x, y; + if (a) { + x = "foo"; + if (b) + console.log(x); + } + y = "bar"; + console.log(y); + } + a = 0, b = 0; + f(); + a = 1, b = 0; + f(); + a = 0, b = 1; + f(); + a = 1, b = 1; + f(); + } + expect: { + var a, b; + function f() { + var x, x; + if (a) { + x = "foo"; + if (b) + console.log(x); + } + x = "bar"; + console.log(x); + } + a = 0, b = 0; + f(); + a = 1, b = 0; + f(); + a = 0, b = 1; + f(); + a = 1, b = 1; + f(); + } + expect_stdout: [ + "bar", + "bar", + "bar", + "foo", + "bar", + ] +} + +cross_branch_2a_3: { + options = { + merge_vars: true, + } + input: { + var a, b; + function f() { + var x, y; + if (a) { + if (b) + x = "foo"; + console.log(x); + y = "bar"; + } + console.log(y); + } + a = 0, b = 0; + f(); + a = 1, b = 0; + f(); + a = 0, b = 1; + f(); + a = 1, b = 1; + f(); + } + expect: { + var a, b; + function f() { + var x, x; + if (a) { + if (b) + x = "foo"; + console.log(x); + x = "bar"; + } + console.log(x); + } + a = 0, b = 0; + f(); + a = 1, b = 0; + f(); + a = 0, b = 1; + f(); + a = 1, b = 1; + f(); + } + expect_stdout: [ + "undefined", + "undefined", + "bar", + "undefined", + "foo", + "bar", + ] +} + +cross_branch_2a_4: { + options = { + merge_vars: true, + } + input: { + var a, b; + function f() { + var x, y; + if (a) { + if (b) { + x = "foo"; + console.log(x); + } + y = "bar"; + } + console.log(y); + } + a = 0, b = 0; + f(); + a = 1, b = 0; + f(); + a = 0, b = 1; + f(); + a = 1, b = 1; + f(); + } + expect: { + var a, b; + function f() { + var x, x; + if (a) { + if (b) { + x = "foo"; + console.log(x); + } + x = "bar"; + } + console.log(x); + } + a = 0, b = 0; + f(); + a = 1, b = 0; + f(); + a = 0, b = 1; + f(); + a = 1, b = 1; + f(); + } + expect_stdout: [ + "undefined", + "bar", + "undefined", + "foo", + "bar", + ] +} + +cross_branch_2a_5: { + options = { + merge_vars: true, + } + input: { + var a, b; + function f() { + var x, y; + if (a) { + x = "foo"; + if (b) + console.log(x); + y = "bar"; + } + console.log(y); + } + a = 0, b = 0; + f(); + a = 1, b = 0; + f(); + a = 0, b = 1; + f(); + a = 1, b = 1; + f(); + } + expect: { + var a, b; + function f() { + var x, x; + if (a) { + x = "foo"; + if (b) + console.log(x); + x = "bar"; + } + console.log(x); + } + a = 0, b = 0; + f(); + a = 1, b = 0; + f(); + a = 0, b = 1; + f(); + a = 1, b = 1; + f(); + } + expect_stdout: [ + "undefined", + "bar", + "undefined", + "foo", + "bar", + ] +} + +cross_branch_2a_6: { + options = { + merge_vars: true, + } + input: { + var a, b; + function f() { + var x, y; + if (a) { + x = "foo"; + if (b) { + console.log(x); + y = "bar"; + } + } + console.log(y); + } + a = 0, b = 0; + f(); + a = 1, b = 0; + f(); + a = 0, b = 1; + f(); + a = 1, b = 1; + f(); + } + expect: { + var a, b; + function f() { + var x, y; + if (a) { + x = "foo"; + if (b) { + console.log(x); + y = "bar"; + } + } + console.log(y); + } + a = 0, b = 0; + f(); + a = 1, b = 0; + f(); + a = 0, b = 1; + f(); + a = 1, b = 1; + f(); + } + expect_stdout: [ + "undefined", + "undefined", + "undefined", + "foo", + "bar", + ] +} + +cross_branch_2a_7: { + options = { + merge_vars: true, + } + input: { + var a, b; + function f() { + var x, y; + if (a) { + x = "foo"; + console.log(x); + if (b) + y = "bar"; + } + console.log(y); + } + a = 0, b = 0; + f(); + a = 1, b = 0; + f(); + a = 0, b = 1; + f(); + a = 1, b = 1; + f(); + } + expect: { + var a, b; + function f() { + var x, y; + if (a) { + x = "foo"; + console.log(x); + if (b) + y = "bar"; + } + console.log(y); + } + a = 0, b = 0; + f(); + a = 1, b = 0; + f(); + a = 0, b = 1; + f(); + a = 1, b = 1; + f(); + } + expect_stdout: [ + "undefined", + "foo", + "undefined", + "undefined", + "foo", + "bar", + ] +} + +cross_branch_2a_8: { + options = { + merge_vars: true, + } + input: { + var a, b; + function f() { + var x, y; + x = "foo"; + if (a) { + if (b) + console.log(x); + y = "bar"; + } + console.log(y); + } + a = 0, b = 0; + f(); + a = 1, b = 0; + f(); + a = 0, b = 1; + f(); + a = 1, b = 1; + f(); + } + expect: { + var a, b; + function f() { + var x, y; + x = "foo"; + if (a) { + if (b) + console.log(x); + y = "bar"; + } + console.log(y); + } + a = 0, b = 0; + f(); + a = 1, b = 0; + f(); + a = 0, b = 1; + f(); + a = 1, b = 1; + f(); + } + expect_stdout: [ + "undefined", + "bar", + "undefined", + "foo", + "bar", + ] +} + +cross_branch_2a_9: { + options = { + merge_vars: true, + } + input: { + var a, b; + function f() { + var x, y; + x = "foo"; + if (a) { + console.log(x); + if (b) + y = "bar"; + } + console.log(y); + } + a = 0, b = 0; + f(); + a = 1, b = 0; + f(); + a = 0, b = 1; + f(); + a = 1, b = 1; + f(); + } + expect: { + var a, b; + function f() { + var x, y; + x = "foo"; + if (a) { + console.log(x); + if (b) + y = "bar"; + } + console.log(y); + } + a = 0, b = 0; + f(); + a = 1, b = 0; + f(); + a = 0, b = 1; + f(); + a = 1, b = 1; + f(); + } + expect_stdout: [ + "undefined", + "foo", + "undefined", + "undefined", + "foo", + "bar", + ] +} + +cross_branch_2a_10: { + options = { + merge_vars: true, + } + input: { + var a, b; + function f() { + var x, y; + x = "foo"; + if (a) { + if (b) + console.log(x); + y = "bar"; + console.log(y); + } + } + a = 0, b = 0; + f(); + a = 1, b = 0; + f(); + a = 0, b = 1; + f(); + a = 1, b = 1; + f(); + } + expect: { + var a, b; + function f() { + var x, x; + x = "foo"; + if (a) { + if (b) + console.log(x); + x = "bar"; + console.log(x); + } + } + a = 0, b = 0; + f(); + a = 1, b = 0; + f(); + a = 0, b = 1; + f(); + a = 1, b = 1; + f(); + } + expect_stdout: [ + "bar", + "foo", + "bar", + ] +} + +cross_branch_2a_11: { + options = { + merge_vars: true, + } + input: { + var a, b; + function f() { + var x, y; + x = "foo"; + if (a) { + if (b) { + console.log(x); + y = "bar"; + } + console.log(y); + } + } + a = 0, b = 0; + f(); + a = 1, b = 0; + f(); + a = 0, b = 1; + f(); + a = 1, b = 1; + f(); + } + expect: { + var a, b; + function f() { + var x, y; + x = "foo"; + if (a) { + if (b) { + console.log(x); + y = "bar"; + } + console.log(y); + } + } + a = 0, b = 0; + f(); + a = 1, b = 0; + f(); + a = 0, b = 1; + f(); + a = 1, b = 1; + f(); + } + expect_stdout: [ + "undefined", + "foo", + "bar", + ] +} + +cross_branch_2a_12: { + options = { + merge_vars: true, + } + input: { + var a, b; + function f() { + var x, y; + x = "foo"; + if (a) { + console.log(x); + if (b) + y = "bar"; + console.log(y); + } + } + a = 0, b = 0; + f(); + a = 1, b = 0; + f(); + a = 0, b = 1; + f(); + a = 1, b = 1; + f(); + } + expect: { + var a, b; + function f() { + var x, y; + x = "foo"; + if (a) { + console.log(x); + if (b) + y = "bar"; + console.log(y); + } + } + a = 0, b = 0; + f(); + a = 1, b = 0; + f(); + a = 0, b = 1; + f(); + a = 1, b = 1; + f(); + } + expect_stdout: [ + "foo", + "undefined", + "foo", + "bar", + ] +} + +cross_branch_2a_13: { + options = { + merge_vars: true, + } + input: { + var a, b; + function f() { + var x, y; + x = "foo"; + if (a) { + console.log(x); + if (b) { + y = "bar"; + console.log(y); + } + } + } + a = 0, b = 0; + f(); + a = 1, b = 0; + f(); + a = 0, b = 1; + f(); + a = 1, b = 1; + f(); + } + expect: { + var a, b; + function f() { + var x, x; + x = "foo"; + if (a) { + console.log(x); + if (b) { + x = "bar"; + console.log(x); + } + } + } + a = 0, b = 0; + f(); + a = 1, b = 0; + f(); + a = 0, b = 1; + f(); + a = 1, b = 1; + f(); + } + expect_stdout: [ + "foo", + "foo", + "bar", + ] +} + +cross_branch_2a_14: { + options = { + merge_vars: true, + } + input: { + var a, b; + function f() { + var x, y; + x = "foo"; + if (a) { + console.log(x); + y = "bar"; + if (b) + console.log(y); + } + } + a = 0, b = 0; + f(); + a = 1, b = 0; + f(); + a = 0, b = 1; + f(); + a = 1, b = 1; + f(); + } + expect: { + var a, b; + function f() { + var x, x; + x = "foo"; + if (a) { + console.log(x); + x = "bar"; + if (b) + console.log(x); + } + } + a = 0, b = 0; + f(); + a = 1, b = 0; + f(); + a = 0, b = 1; + f(); + a = 1, b = 1; + f(); + } + expect_stdout: [ + "foo", + "foo", + "bar", + ] +} + +cross_branch_2a_15: { + options = { + merge_vars: true, + } + input: { + var a, b; + function f() { + var x, y; + x = "foo"; + console.log(x); + if (a) { + if (b) + y = "bar"; + console.log(y); + } + } + a = 0, b = 0; + f(); + a = 1, b = 0; + f(); + a = 0, b = 1; + f(); + a = 1, b = 1; + f(); + } + expect: { + var a, b; + function f() { + var x, y; + x = "foo"; + console.log(x); + if (a) { + if (b) + y = "bar"; + console.log(y); + } + } + a = 0, b = 0; + f(); + a = 1, b = 0; + f(); + a = 0, b = 1; + f(); + a = 1, b = 1; + f(); + } + expect_stdout: [ + "foo", + "foo", + "undefined", + "foo", + "foo", + "bar", + ] +} + +cross_branch_2a_16: { + options = { + merge_vars: true, + } + input: { + var a, b; + function f() { + var x, y; + x = "foo"; + console.log(x); + if (a) { + y = "bar"; + if (b) + console.log(y); + } + } + a = 0, b = 0; + f(); + a = 1, b = 0; + f(); + a = 0, b = 1; + f(); + a = 1, b = 1; + f(); + } + expect: { + var a, b; + function f() { + var x, x; + x = "foo"; + console.log(x); + if (a) { + x = "bar"; + if (b) + console.log(x); + } + } + a = 0, b = 0; + f(); + a = 1, b = 0; + f(); + a = 0, b = 1; + f(); + a = 1, b = 1; + f(); + } + expect_stdout: [ + "foo", + "foo", + "foo", + "foo", + "bar", + ] +} + +cross_branch_2b_1: { + options = { + merge_vars: true, + } + input: { + var a, b; + function f() { + var x, y; + if (a) + x = "foo"; + if (b) + console.log(x); + y = "bar"; + console.log(y); + } + a = 0, b = 0; + f(); + a = 1, b = 0; + f(); + a = 0, b = 1; + f(); + a = 1, b = 1; + f(); + } + expect: { + var a, b; + function f() { + var x, x; + if (a) + x = "foo"; + if (b) + console.log(x); + x = "bar"; + console.log(x); + } + a = 0, b = 0; + f(); + a = 1, b = 0; + f(); + a = 0, b = 1; + f(); + a = 1, b = 1; + f(); + } + expect_stdout: [ + "bar", + "bar", + "undefined", + "bar", + "foo", + "bar", + ] +} + +cross_branch_2b_2: { + options = { + merge_vars: true, + } + input: { + var a, b; + function f() { + var x, y; + if (a) + x = "foo"; + if (b) { + console.log(x); + y = "bar"; + } + console.log(y); + } + a = 0, b = 0; + f(); + a = 1, b = 0; + f(); + a = 0, b = 1; + f(); + a = 1, b = 1; + f(); + } + expect: { + var a, b; + function f() { + var x, y; + if (a) + x = "foo"; + if (b) { + console.log(x); + y = "bar"; + } + console.log(y); + } + a = 0, b = 0; + f(); + a = 1, b = 0; + f(); + a = 0, b = 1; + f(); + a = 1, b = 1; + f(); + } + expect_stdout: [ + "undefined", + "undefined", + "undefined", + "bar", + "foo", + "bar", + ] +} + +cross_branch_2b_3: { + options = { + merge_vars: true, + } + input: { + var a, b; + function f() { + var x, y; + if (a) + x = "foo"; + if (b) { + console.log(x); + y = "bar"; + console.log(y); + } + } + a = 0, b = 0; + f(); + a = 1, b = 0; + f(); + a = 0, b = 1; + f(); + a = 1, b = 1; + f(); + } + expect: { + var a, b; + function f() { + var x, x; + if (a) + x = "foo"; + if (b) { + console.log(x); + x = "bar"; + console.log(x); + } + } + a = 0, b = 0; + f(); + a = 1, b = 0; + f(); + a = 0, b = 1; + f(); + a = 1, b = 1; + f(); + } + expect_stdout: [ + "undefined", + "bar", + "foo", + "bar", + ] +} + +cross_branch_2b_4: { + options = { + merge_vars: true, + } + input: { + var a, b; + function f() { + var x, y; + if (a) + x = "foo"; + console.log(x); + if (b) + y = "bar"; + console.log(y); + } + a = 0, b = 0; + f(); + a = 1, b = 0; + f(); + a = 0, b = 1; + f(); + a = 1, b = 1; + f(); + } + expect: { + var a, b; + function f() { + var x, y; + if (a) + x = "foo"; + console.log(x); + if (b) + y = "bar"; + console.log(y); + } + a = 0, b = 0; + f(); + a = 1, b = 0; + f(); + a = 0, b = 1; + f(); + a = 1, b = 1; + f(); + } + expect_stdout: [ + "undefined", + "undefined", + "foo", + "undefined", + "undefined", + "bar", + "foo", + "bar", + ] +} + +cross_branch_2b_5: { + options = { + merge_vars: true, + } + input: { + var a, b; + function f() { + var x, y; + if (a) + x = "foo"; + console.log(x); + if (b) { + y = "bar"; + console.log(y); + } + } + a = 0, b = 0; + f(); + a = 1, b = 0; + f(); + a = 0, b = 1; + f(); + a = 1, b = 1; + f(); + } + expect: { + var a, b; + function f() { + var x, x; + if (a) + x = "foo"; + console.log(x); + if (b) { + x = "bar"; + console.log(x); + } + } + a = 0, b = 0; + f(); + a = 1, b = 0; + f(); + a = 0, b = 1; + f(); + a = 1, b = 1; + f(); + } + expect_stdout: [ + "undefined", + "foo", + "undefined", + "bar", + "foo", + "bar", + ] +} + +cross_branch_2b_6: { + options = { + merge_vars: true, + } + input: { + var a, b; + function f() { + var x, y; + if (a) { + x = "foo"; + console.log(x); + } + if (b) + y = "bar"; + console.log(y); + } + a = 0, b = 0; + f(); + a = 1, b = 0; + f(); + a = 0, b = 1; + f(); + a = 1, b = 1; + f(); + } + expect: { + var a, b; + function f() { + var x, y; + if (a) { + x = "foo"; + console.log(x); + } + if (b) + y = "bar"; + console.log(y); + } + a = 0, b = 0; + f(); + a = 1, b = 0; + f(); + a = 0, b = 1; + f(); + a = 1, b = 1; + f(); + } + expect_stdout: [ + "undefined", + "foo", + "undefined", + "bar", + "foo", + "bar", + ] +} + +cross_branch_2b_7: { + options = { + merge_vars: true, + } + input: { + var a, b; + function f() { + var x, y; + if (a) { + x = "foo"; + console.log(x); + } + if (b) { + y = "bar"; + console.log(y); + } + } + a = 0, b = 0; + f(); + a = 1, b = 0; + f(); + a = 0, b = 1; + f(); + a = 1, b = 1; + f(); + } + expect: { + var a, b; + function f() { + var x, x; + if (a) { + x = "foo"; + console.log(x); + } + if (b) { + x = "bar"; + console.log(x); + } + } + a = 0, b = 0; + f(); + a = 1, b = 0; + f(); + a = 0, b = 1; + f(); + a = 1, b = 1; + f(); + } + expect_stdout: [ + "foo", + "bar", + "foo", + "bar", + ] +} + +cross_branch_2b_8: { + options = { + merge_vars: true, + } + input: { + var a, b; + function f() { + var x, y; + if (a) + x = "foo"; + console.log(x); + y = "bar"; + if (b) + console.log(y); + } + a = 0, b = 0; + f(); + a = 1, b = 0; + f(); + a = 0, b = 1; + f(); + a = 1, b = 1; + f(); + } + expect: { + var a, b; + function f() { + var x, x; + if (a) + x = "foo"; + console.log(x); + x = "bar"; + if (b) + console.log(x); + } + a = 0, b = 0; + f(); + a = 1, b = 0; + f(); + a = 0, b = 1; + f(); + a = 1, b = 1; + f(); + } + expect_stdout: [ + "undefined", + "foo", + "undefined", + "bar", + "foo", + "bar", + ] +} + +cross_branch_2b_9: { + options = { + merge_vars: true, + } + input: { + var a, b; + function f() { + var x, y; + if (a) { + x = "foo"; + console.log(x); + } + y = "bar"; + if (b) + console.log(y); + } + a = 0, b = 0; + f(); + a = 1, b = 0; + f(); + a = 0, b = 1; + f(); + a = 1, b = 1; + f(); + } + expect: { + var a, b; + function f() { + var x, x; + if (a) { + x = "foo"; + console.log(x); + } + x = "bar"; + if (b) + console.log(x); + } + a = 0, b = 0; + f(); + a = 1, b = 0; + f(); + a = 0, b = 1; + f(); + a = 1, b = 1; + f(); + } + expect_stdout: [ + "foo", + "bar", + "foo", + "bar", + ] +} + +cross_branch_2b_10: { + options = { + merge_vars: true, + } + input: { + var a, b; + function f() { + var x, y; + if (a) { + x = "foo"; + console.log(x); + y = "bar"; + } + if (b) + console.log(y); + } + a = 0, b = 0; + f(); + a = 1, b = 0; + f(); + a = 0, b = 1; + f(); + a = 1, b = 1; + f(); + } + expect: { + var a, b; + function f() { + var x, x; + if (a) { + x = "foo"; + console.log(x); + x = "bar"; + } + if (b) + console.log(x); + } + a = 0, b = 0; + f(); + a = 1, b = 0; + f(); + a = 0, b = 1; + f(); + a = 1, b = 1; + f(); + } + expect_stdout: [ + "foo", + "undefined", + "foo", + "bar", + ] +} + +cross_branch_2b_11: { + options = { + merge_vars: true, + } + input: { + var a, b; + function f() { + var x, y; + x = "foo"; + if (a) + console.log(x); + if (b) + y = "bar"; + console.log(y); + } + a = 0, b = 0; + f(); + a = 1, b = 0; + f(); + a = 0, b = 1; + f(); + a = 1, b = 1; + f(); + } + expect: { + var a, b; + function f() { + var x, y; + x = "foo"; + if (a) + console.log(x); + if (b) + y = "bar"; + console.log(y); + } + a = 0, b = 0; + f(); + a = 1, b = 0; + f(); + a = 0, b = 1; + f(); + a = 1, b = 1; + f(); + } + expect_stdout: [ + "undefined", + "foo", + "undefined", + "bar", + "foo", + "bar", + ] +} + +cross_branch_2b_12: { + options = { + merge_vars: true, + } + input: { + var a, b; + function f() { + var x, y; + x = "foo"; + if (a) + console.log(x); + if (b) { + y = "bar"; + console.log(y); + } + } + a = 0, b = 0; + f(); + a = 1, b = 0; + f(); + a = 0, b = 1; + f(); + a = 1, b = 1; + f(); + } + expect: { + var a, b; + function f() { + var x, x; + x = "foo"; + if (a) + console.log(x); + if (b) { + x = "bar"; + console.log(x); + } + } + a = 0, b = 0; + f(); + a = 1, b = 0; + f(); + a = 0, b = 1; + f(); + a = 1, b = 1; + f(); + } + expect_stdout: [ + "foo", + "bar", + "foo", + "bar", + ] +} + +cross_branch_2b_13: { + options = { + merge_vars: true, + } + input: { + var a, b; + function f() { + var x, y; + x = "foo"; + if (a) + console.log(x); + y = "bar"; + if (b) + console.log(y); + } + a = 0, b = 0; + f(); + a = 1, b = 0; + f(); + a = 0, b = 1; + f(); + a = 1, b = 1; + f(); + } + expect: { + var a, b; + function f() { + var x, x; + x = "foo"; + if (a) + console.log(x); + x = "bar"; + if (b) + console.log(x); + } + a = 0, b = 0; + f(); + a = 1, b = 0; + f(); + a = 0, b = 1; + f(); + a = 1, b = 1; + f(); + } + expect_stdout: [ + "foo", + "bar", + "foo", + "bar", + ] +} + +cross_branch_2b_14: { + options = { + merge_vars: true, + } + input: { + var a, b; + function f() { + var x, y; + x = "foo"; + if (a) { + console.log(x); + y = "bar"; + } + if (b) + console.log(y); + } + a = 0, b = 0; + f(); + a = 1, b = 0; + f(); + a = 0, b = 1; + f(); + a = 1, b = 1; + f(); + } + expect: { + var a, b; + function f() { + var x, y; + x = "foo"; + if (a) { + console.log(x); + y = "bar"; + } + if (b) + console.log(y); + } + a = 0, b = 0; + f(); + a = 1, b = 0; + f(); + a = 0, b = 1; + f(); + a = 1, b = 1; + f(); + } + expect_stdout: [ + "foo", + "undefined", + "foo", + "bar", + ] +} + +cross_branch_2b_15: { + options = { + merge_vars: true, + } + input: { + var a, b; + function f() { + var x, y; + x = "foo"; + console.log(x); + if (a) + y = "bar"; + if (b) + console.log(y); + } + a = 0, b = 0; + f(); + a = 1, b = 0; + f(); + a = 0, b = 1; + f(); + a = 1, b = 1; + f(); + } + expect: { + var a, b; + function f() { + var x, y; + x = "foo"; + console.log(x); + if (a) + y = "bar"; + if (b) + console.log(y); + } + a = 0, b = 0; + f(); + a = 1, b = 0; + f(); + a = 0, b = 1; + f(); + a = 1, b = 1; + f(); + } + expect_stdout: [ + "foo", + "foo", + "foo", + "undefined", + "foo", + "bar", + ] +} + +issue_4126_1: { + options = { + merge_vars: true, + } + input: { + function f(a) { + try { + console.log("PASS"); + } catch (e) { + var b = a; + } finally { + var c = b; + } + console.log(c); + } + f("FAIL"); + } + expect: { + function f(a) { + try { + console.log("PASS"); + } catch (e) { + var b = a; + } finally { + var a = b; + } + console.log(a); + } + f("FAIL"); + } + expect_stdout: [ + "PASS", + "undefined", + ] +} + +issue_4126_2: { + options = { + inline: true, + merge_vars: true, + side_effects: true, + toplevel: true, + } + input: { + try { + var a = function() { + var b = 0; + function f() { + b; + } + THROW(b); + }(); + } catch (e) { + console.log(a); + } + } + expect: { + try { + var a = (b = 0, void THROW(b)); + } catch (e) { + console.log(a); + } + function f() {} + var b; + } + expect_stdout: "undefined" +} + +issue_4130: { + options = { + merge_vars: true, + side_effects: true, + toplevel: true, + } + input: { + var a = 2; + while (a) + try { + console.log(a); + } catch (e) { + var b = 0; + } finally { + b && console.log("FAIL"); + var c = --a; + for (var k in c) + c; + } + } + expect: { + var a = 2; + while (a) + try { + console.log(a); + } catch (e) { + var b = 0; + } finally { + b && console.log("FAIL"); + var c = --a; + for (var k in c); + } + } + expect_stdout: [ + "2", + "1", + ] +} + +issue_4135: { + options = { + evaluate: true, + inline: true, + merge_vars: true, + reduce_vars: true, + side_effects: true, + toplevel: true, + unused: true, + } + input: { + var a = 0, b = 0; + --b; + a++; + if (!a) + var c = function() { + var d = 0; + function f() { + d && d.p; + } + f(); + this; + }(a++); + console.log(a, b, c); + } + expect: { + var a = 0; + 0; + a++; + if (!a) + var c = void a++; + console.log(a, -1, c); + } + expect_stdout: "1 -1 undefined" +} + +issue_4139: { + options = { + merge_vars: true, + toplevel: true, + } + input: { + try { + console.log; + } catch (e) { + var a, arguments = 0; + } finally { + a = typeof arguments; + console.log(a); + } + } + expect: { + try { + console.log; + } catch (e) { + var a, arguments = 0; + } finally { + a = typeof arguments; + console.log(a); + } + } + expect_stdout: "object" +} + +lambda_reuse: { + options = { + merge_vars: true, + toplevel: true, + } + input: { + var a, b, f = function() { + console.log(a); + }; + f(); + a = "PASS"; + b = "FAIL"; + f(); + if (console.log(typeof b)) + console.log(b); + } + expect: { + var a, b, f = function() { + console.log(a); + }; + f(); + a = "PASS"; + b = "FAIL"; + f(); + if (console.log(typeof b)) + console.log(b); + } + expect_stdout: [ + "undefined", + "PASS", + "string", + ] +} + +conditional_write: { + options = { + merge_vars: true, + toplevel: true, + } + input: { + var a = "FAIL", b; + if (console) + a = "PASS"; + b = [a, 42].join(); + console.log(b); + } + expect: { + var a = "FAIL", a; + if (console) + a = "PASS"; + a = [a, 42].join(); + console.log(a); + } + expect_stdout: "PASS,42" +} + +issue_4155: { + options = { + inline: true, + merge_vars: true, + } + input: { + (function() { + try { + throw "PASS"; + } catch (e) { + var a; + (function() { + console.log(e, a); + })(a = NaN); + } + var e = function() {}; + e && console.log(typeof e); + })(); + } + expect: { + (function() { + try { + throw "PASS"; + } catch (e) { + var a; + a = NaN, + void console.log(e, a); + } + var e = function() {}; + e && console.log(typeof e); + })(); + } + expect_stdout: [ + "PASS NaN", + "function", + ] +} + +issue_4157_1: { + options = { + dead_code: true, + loops: true, + merge_vars: true, + } + input: { + (function() { + try { + for (var a = "FAIL"; a; a++) + return; + var b = 0; + } finally { + console.log(b); + } + })(); + } + expect: { + (function() { + try { + var a = "FAIL"; + if (a) + return; + var b = 0; + } finally { + console.log(b); + } + })(); + } + expect_stdout: "undefined" +} + +issue_4157_2: { + options = { + dead_code: true, + loops: true, + merge_vars: true, + } + input: { + (function() { + try { + throw "FAIL"; + } catch (e) { + for (var a = e; a; a++) + return; + var b = 0; + } finally { + console.log(b); + } + })(); + } + expect: { + (function() { + try { + throw "FAIL"; + } catch (e) { + var a = e; + if (a) + return; + var b = 0; + } finally { + console.log(b); + } + })(); + } + expect_stdout: "undefined" +} + +issue_4168: { + options = { + merge_vars: true, + } + input: { + var o = { + f: function(a, b, c) { + var d = a.d; + var e = b.e; + var f = c.f; + this.g(arguments); + if (d) + console.log(e, f); + }, + g: function(args) { + console.log(args[0], args[1], args[2]); + }, + }; + o.f("PASS", true, 42); + } + expect: { + var o = { + f: function(a, b, c) { + var d = a.d; + var e = b.e; + var f = c.f; + this.g(arguments); + if (d) + console.log(e, f); + }, + g: function(args) { + console.log(args[0], args[1], args[2]); + }, + }; + o.f("PASS", true, 42); + } + expect_stdout: "PASS true 42" +} + +issue_4168_use_strict: { + options = { + merge_vars: true, + } + input: { + "use strict"; + var o = { + f: function(a, b, c) { + var d = a.d; + var e = b.e; + var f = c.f; + this.g(arguments); + if (d) + console.log(e, f); + }, + g: function(args) { + console.log(args[0], args[1], args[2]); + }, + }; + o.f("PASS", true, 42); + } + expect: { + "use strict"; + var o = { + f: function(a, b, c) { + var a = a.d; + var b = b.e; + var c = c.f; + this.g(arguments); + if (a) + console.log(b, c); + }, + g: function(args) { + console.log(args[0], args[1], args[2]); + }, + }; + o.f("PASS", true, 42); + } + expect_stdout: "PASS true 42" +} + +issue_4237_1: { + options = { + merge_vars: true, + } + input: { + console.log(function(a) { + do { + var b = a++; + if (b) + return "FAIL"; + continue; + var c = 42; + } while ("undefined" != typeof c); + return "PASS"; + }(0)); + } + expect: { + console.log(function(a) { + do { + var b = a++; + if (b) + return "FAIL"; + continue; + var c = 42; + } while ("undefined" != typeof c); + return "PASS"; + }(0)); + } + expect_stdout: "PASS" +} + +issue_4237_2: { + options = { + dead_code: true, + evaluate: true, + loops: true, + merge_vars: true, + switches: true, + } + input: { + console.log(function(a) { + do { + switch (0) { + case 0: + var b = a++; + default: + while (b) + return "FAIL"; + } + try { + var c = 0; + } finally { + continue; + } + var d = 0; + } while ("undefined" != typeof d); + return "PASS"; + }(0)); + } + expect: { + console.log(function(a) { + do { + switch (0) { + default: + var b = a++; + if (b) + return "FAIL"; + } + try { + var c = 0; + } finally { + continue; + } + var d = 0; + } while ("undefined" != typeof d); + return "PASS"; + }(0)); + } + expect_stdout: "PASS" +} + +issue_4253: { + options = { + merge_vars: true, + toplevel: true, + } + input: { + switch (0) { + default: + var a = "FAIL"; + a = a && a; + try { + break; + } catch (e) {} + var b = 42; + } + console.log(b); + } + expect: { + switch (0) { + default: + var a = "FAIL"; + a = a && a; + try { + break; + } catch (e) {} + var b = 42; + } + console.log(b); + } + expect_stdout: "undefined" +} + +issue_4255: { + options = { + dead_code: true, + loops: true, + merge_vars: true, + toplevel: true, + } + input: { + L: for (var a = 2; --a;) + for (var b = 0; console.log(b); --b) + break L; + } + expect: { + L: for (var a = 2; --a;) { + var b = 0; + if (console.log(b)) + break L; + } + } + expect_stdout: "0" +} + +issue_4257: { + options = { + merge_vars: true, + toplevel: true, + } + input: { + var a = 0; + for (var i = 0; i < 2; i++) + switch (--a) { + case 0: + var b = 0; + break; + case 0: + default: + var c = 1 + (0 | (b && A)); + console.log(c); + } + } + expect: { + var a = 0; + for (var i = 0; i < 2; i++) + switch (--a) { + case 0: + var b = 0; + break; + case 0: + default: + var c = 1 + (0 | (b && A)); + console.log(c); + } + } + expect_stdout: [ + "1", + "1", + ] +} + +issue_4628: { + options = { + merge_vars: true, + } + input: { + (function() { + try { + console; + } finally { + var b = a; + } + for (var a in "foo"); + console.log(b); + })(); + } + expect: { + (function() { + try { + console; + } finally { + var b = a; + } + for (var a in "foo"); + console.log(b); + })(); + } + expect_stdout: "undefined" +} + +issue_4653: { + options = { + evaluate: true, + merge_vars: true, + reduce_vars: true, + toplevel: true, + unused: true, + } + input: { + var a = 1, b; + function f(c, d) { + c || console.log(d); + } + f(a++ + (b = b), b |= console.log(a)); + } + expect: { + var b, a = 1; + (function(c, d) { + c || console.log(d); + })(+a + (b = b), b |= console.log(2)); + } + expect_stdout: [ + "2", + "0", + ] +} + +issue_4759: { + options = { + merge_vars: true, + toplevel: true, + } + input: { + var i = 2, a = 1, b, c, d; + while (i--) { + try { + if (1 != b) { + d = []; + null.p; + c = d; + } else { + b = 0; + a = c; + } + } catch (e) {} + b = a; + } + console.log(a); + } + expect: { + var i = 2, a = 1, b, c, d; + while (i--) { + try { + if (1 != b) { + d = []; + null.p; + c = d; + } else { + b = 0; + a = c; + } + } catch (e) {} + b = a; + } + console.log(a); + } + expect_stdout: "undefined" +} + +issue_4761: { + options = { + merge_vars: true, + toplevel: true, + } + input: { + var a = "FAIL", b; + try { + !a && --a && (b = 0)[console] || console.log(b); + } catch (e) {} + } + expect: { + var a = "FAIL", b; + try { + !a && --a && (b = 0)[console] || console.log(b); + } catch (e) {} + } + expect_stdout: "undefined" +} + +issue_4956_1: { + options = { + merge_vars: true, + toplevel: true, + } + input: { + var a, b; + function f(c) { + switch (c) { + case 0: + a = { p: 42 }; + + case 1: + b = a.p; + console.log(b); + } + } + f(0); + f(1); + } + expect: { + var a, b; + function f(c) { + switch (c) { + case 0: + a = { p: 42 }; + + case 1: + b = a.p; + console.log(b); + } + } + f(0); + f(1); + } + expect_stdout: [ + "42", + "42", + ] +} + +issue_4956_2: { + options = { + merge_vars: true, + toplevel: true, + } + input: { + var a, b; + function f(c) { + if (0 == c) { + console; + a = { p: 42 }; + } + b = a.p; + if (1 == c) + console.log(b); + } + f(0); + f(1); + } + expect: { + var a, b; + function f(c) { + if (0 == c) { + console; + a = { p: 42 }; + } + b = a.p; + if (1 == c) + console.log(b); + } + f(0); + f(1); + } + expect_stdout: "42" +} + +issue_5182: { + options = { + arrows: true, + collapse_vars: true, + evaluate: true, + hoist_props: true, + inline: true, + merge_vars: true, + passes: 4, + reduce_vars: true, + sequences: true, + side_effects: true, + toplevel: true, + unused: true, + } + input: { + try { + var con = console; + } catch (x) {} + global.log = con.log; + var jump = function(x) { + console.log("JUMP:", x * 10); + return x + x; + }; + var jump2 = jump; + var run = function(x) { + console.log("RUN:", x * -10); + return x * x; + }; + var run2 = run; + var bar = (x, y) => { + console.log("BAR:", x + y); + return x - y; + }; + var bar2 = bar; + var obj = { + foo: bar2, + go: run2, + not_used: jump2, + }; + console.log(obj.foo(1, 2), global.log("PASS")); + } + expect: { + try { + var con = console; + } catch (x) {} + global.log = con.log, + console.log((console.log("BAR:", 3), -1), global.log("PASS")); + } + expect_stdout: [ + "BAR: 3", + "PASS", + "-1 undefined", + ] + node_version: ">=4" +} + +issue_5420: { + options = { + merge_vars: true, + toplevel: true, + } + input: { + do { + var a = "FAIL 1"; + a && a.p; + a = "FAIL 2"; + try { + continue; + } catch (e) {} + var b = "FAIL 3"; + } while (console.log(b || "PASS")); + } + expect: { + do { + var a = "FAIL 1"; + a && a.p; + a = "FAIL 2"; + try { + continue; + } catch (e) {} + var b = "FAIL 3"; + } while (console.log(b || "PASS")); + } + expect_stdout: "PASS" +} + +issue_5451: { + options = { + merge_vars: true, + toplevel: true, + } + input: { + A = 1; + var a = 1, b; + console.log(function f() { + return a-- && f(b = A, b); + }()); + } + expect: { + A = 1; + var a = 1, b; + console.log(function f() { + return a-- && f(b = A, b); + }()); + } + expect_stdout: "0" +} + +issue_5471_1: { + options = { + conditionals: true, + inline: true, + merge_vars: true, + reduce_vars: true, + sequences: true, + side_effects: true, + toplevel: true, + unused: true, + } + input: { + var a = "FAIL 1"; + function f(b, c) { + function g() { + if (console) + return 42; + else + c = "FAIL 2"; + } + var d = g(); + console.log(c || "PASS"); + var e = function h() { + while (b && e); + }(); + } + f(a++) && a; + } + expect: { + var a = "FAIL 1"; + var b, c, e; + b = +a, + function() { + if (console) + return; + c = "FAIL 2"; + }(), + console.log(c || "PASS"), + e = function() { + while (b && e); + }(); + } + expect_stdout: "PASS" +} + +issue_5471_2: { + options = { + conditionals: true, + evaluate: true, + inline: true, + merge_vars: true, + reduce_vars: true, + sequences: true, + toplevel: true, + unused: true, + } + input: { + var a = "FAIL 1"; + function f(b, c) { + function g() { + if (console) + return 42; + else + c = "FAIL 2"; + } + var d = g(); + console.log(c || "PASS"); + var e = function h() { + while (b && e); + }(); + } + f(a++) && a; + } + expect: { + var a = "FAIL 1"; + var b, c, e; + b = +a, + function() { + if (console) + return; + c = "FAIL 2"; + }(), + console.log(c || "PASS"), + e = function() { + while (b && e); + }(), + void 0; + } + expect_stdout: "PASS" +} + +issue_5714: { + options = { + inline: true, + join_vars: true, + loops: true, + merge_vars: true, + unused: true, + } + input: { + "use strict"; + console.log(function() { + var i = 1; + while (i--) { + var a = function f(b) { + console.log(b); + var c = function(d) { + console.log(typeof d); + }(console); + }(); + var e = 42; + } + return e; + }()); + } + expect: { + "use strict"; + console.log(function() { + for (var i = 1; i--;) { + var b = void 0; + console.log(b); + b = console, + console.log(typeof b); + var e = 42; + } + return e; + }()); + } + expect_stdout: [ + "undefined", + "object", + "42", + ] +} + +issue_5770_1: { + options = { + dead_code: true, + loops: true, + merge_vars: true, + toplevel: true, + } + input: { + L: do { + if (console) + for (var a = "FAIL 1"; a; a--) + continue L; + var b = "FAIL 2"; + } while (console.log(b || "PASS")); + } + expect: { + L: do { + if (console) { + var a = "FAIL 1"; + if (a) + continue L; + } + var b = "FAIL 2"; + } while (console.log(b || "PASS")); + } + expect_stdout: "PASS" +} + +issue_5770_2: { + options = { + conditionals: true, + dead_code: true, + if_return: true, + loops: true, + merge_vars: true, + toplevel: true, + } + input: { + L: do { + for (var a = "FAIL 1"; a; a--) + continue L; + var b = "FAIL 2"; + } while (console.log(b || "PASS")); + } + expect: { + L: do { + var a = "FAIL 1"; + var b; + } while (a || (b = "FAIL 2"), console.log(b || "PASS")); + } + expect_stdout: "PASS" +} + +issue_5772_1: { + options = { + dead_code: true, + merge_vars: true, + loops: true, + } + input: { + (function(a) { + while (--a) + return; + var b = console.log("foo") && (c = 42) ? 0 : console.log(c); + var c = b; + })(); + } + expect: { + (function(a) { + if (--a) + return; + var a = console.log("foo") && (c = 42) ? 0 : console.log(c); + var c = a; + })(); + } + expect_stdout: [ + "foo", + "undefined", + ] +} + +issue_5772_2: { + options = { + dead_code: true, + merge_vars: true, + loops: true, + } + input: { + (function(a) { + while (--a) + return; + var b; + var c = console.log("foo") && (b = 1) ? 2 : 3; + console.log(b, c); + })(); + } + expect: { + (function(a) { + if (--a) + return; + var b; + var a = console.log("foo") && (b = 1) ? 2 : 3; + console.log(b, a); + })(); + } + expect_stdout: [ + "foo", + "undefined 3", + ] +} diff --git a/test/compress/negate-iife.js b/test/compress/negate-iife.js index 89c3f0646a3..b555b053a6a 100644 --- a/test/compress/negate-iife.js +++ b/test/compress/negate-iife.js @@ -1,7 +1,7 @@ negate_iife_1: { options = { - negate_iife: true - }; + negate_iife: true, + } input: { (function(){ stuff() })(); } @@ -10,56 +10,256 @@ negate_iife_1: { } } +negate_iife_1_off: { + options = { + negate_iife: false, + } + input: { + (function(){ stuff() })(); + } + expect_exact: '(function(){stuff()})();' +} + negate_iife_2: { options = { - negate_iife: true - }; + inline: true, + negate_iife: true, + } input: { - (function(){ return {} })().x = 10; // should not transform this one + (function(){ return {} })().x = 10; } - expect: { + expect_exact: "({}).x=10;" +} + +negate_iife_2_side_effects: { + options = { + inline: true, + negate_iife: true, + side_effects: true, + } + input: { (function(){ return {} })().x = 10; } + expect_exact: "({}).x=10;" } negate_iife_3: { options = { + conditionals: true, + negate_iife: true, + } + input: { + (function(){ return t })() ? console.log(true) : console.log(false); + } + expect: { + !function(){ return t }() ? console.log(false) : console.log(true); + } +} + +negate_iife_3_evaluate: { + options = { + conditionals: true, + evaluate: true, + inline: true, negate_iife: true, - }; + } input: { (function(){ return true })() ? console.log(true) : console.log(false); } expect: { - !function(){ return true }() ? console.log(false) : console.log(true); + true, console.log(true); } + expect_stdout: true } -negate_iife_3: { +negate_iife_3_side_effects: { options = { + conditionals: true, negate_iife: true, - sequences: true - }; + side_effects: true, + } + input: { + (function(){ return t })() ? console.log(true) : console.log(false); + } + expect: { + !function(){ return t }() ? console.log(false) : console.log(true); + } +} + +negate_iife_3_off: { + options = { + conditionals: true, + negate_iife: false, + } + input: { + (function(){ return t })() ? console.log(true) : console.log(false); + } + expect: { + !function(){ return t }() ? console.log(false) : console.log(true); + } +} + +negate_iife_3_off_evaluate: { + options = { + conditionals: true, + evaluate: true, + inline: true, + negate_iife: false, + } input: { (function(){ return true })() ? console.log(true) : console.log(false); - (function(){ + } + expect: { + true, console.log(true); + } + expect_stdout: true +} + +negate_iife_4: { + options = { + conditionals: true, + negate_iife: true, + sequences: true, + } + input: { + (function() { + return t; + })() ? console.log(true) : console.log(false); + (function() { console.log("something"); })(); } expect: { - !function(){ return true }() ? console.log(false) : console.log(true), function(){ + !function() { + return t; + }() ? console.log(false) : console.log(true), !function() { console.log("something"); }(); } } -negate_iife_4: { +negate_iife_4_drop_side_effect_free: { options = { + conditionals: true, negate_iife: true, sequences: true, + side_effects: true, + } + input: { + (function() { + return t; + })() ? console.log(true) : console.log(false); + (function() { + console.log("something"); + })(); + } + expect: { + !function() { + return t; + }() ? console.log(false) : console.log(true), function() { + console.log("something"); + }(); + } +} + +sequence_off: { + options = { + conditionals: true, + negate_iife: false, + passes: 2, + sequences: true, + } + input: { + function f() { + (function(){ return t })() ? console.log(true) : console.log(false); + (function(){ + console.log("something"); + })(); + } + function g() { + (function(){ + console.log("something"); + })(); + (function(){ return t })() ? console.log(true) : console.log(false); + } + } + expect: { + function f() { + !function(){ return t }() ? console.log(false) : console.log(true), function(){ + console.log("something"); + }(); + } + function g() { + (function(){ + console.log("something"); + })(), function(){ return t }() ? console.log(true) : console.log(false); + } + } +} + +negate_iife_5: { + options = { conditionals: true, - }; + negate_iife: true, + sequences: true, + } input: { - if ((function(){ return true })()) { + if (function() { + return t; + }()) { + foo(true); + } else { + bar(false); + } + (function() { + console.log("something"); + })(); + } + expect: { + !function() { + return t; + }() ? bar(false) : foo(true), !function() { + console.log("something"); + }(); + } +} + +negate_iife_5_drop_side_effect_free: { + options = { + conditionals: true, + negate_iife: true, + sequences: true, + side_effects: true, + } + input: { + if (function() { + return t; + }()) { + foo(true); + } else { + bar(false); + } + (function() { + console.log("something"); + })(); + } + expect: { + !function() { + return t; + }() ? bar(false) : foo(true), function() { + console.log("something"); + }(); + } +} + +negate_iife_5_off: { + options = { + conditionals: true, + negate_iife: false, + sequences: true, + } + input: { + if ((function(){ return t })()) { foo(true); } else { bar(false); @@ -69,8 +269,211 @@ negate_iife_4: { })(); } expect: { - !function(){ return true }() ? bar(false) : foo(true), function(){ + !function(){ return t }() ? bar(false) : foo(true), function(){ console.log("something"); }(); } } + +negate_iife_nested: { + options = { + conditionals: true, + negate_iife: true, + sequences: true, + } + input: { + function Foo(f) { + this.f = f; + } + new Foo(function() { + (function(x) { + (function(y) { + console.log(y); + })(x); + })(7); + }).f(); + } + expect: { + function Foo(f) { + this.f = f; + } + new Foo(function() { + !function(x) { + !function(y) { + console.log(y); + }(x); + }(7); + }).f(); + } + expect_stdout: true +} + +negate_iife_nested_off: { + options = { + conditionals: true, + negate_iife: false, + sequences: true, + } + input: { + function Foo(f) { + this.f = f; + } + new Foo(function() { + (function(x) { + (function(y) { + console.log(y); + })(x); + })(7); + }).f(); + } + expect: { + function Foo(f) { + this.f = f; + } + new Foo(function() { + (function(x) { + (function(y) { + console.log(y); + })(x); + })(7); + }).f(); + } + expect_stdout: true +} + +negate_iife_issue_1073: { + options = { + conditionals: true, + negate_iife: true, + sequences: true, + } + input: { + new (function(a) { + return function Foo() { + this.x = a; + console.log(this); + }; + }(7))(); + } + expect: { + new (function(a) { + return function Foo() { + this.x = a, + console.log(this); + }; + }(7))(); + } + expect_stdout: true +} + +issue_1254_negate_iife_false: { + options = { + negate_iife: false, + } + input: { + (function() { + return function() { + console.log('test') + }; + })()(); + } + expect_exact: '(function(){return function(){console.log("test")}})()();' + expect_stdout: true +} + +issue_1254_negate_iife_true: { + options = { + negate_iife: true, + } + input: { + (function() { + return function() { + console.log('test') + }; + })()(); + } + expect_exact: '!function(){return function(){console.log("test")}}()();' + expect_stdout: true +} + +issue_1254_negate_iife_nested: { + options = { + negate_iife: true, + } + input: { + (function() { + return function() { + console.log('test') + }; + })()()()()(); + } + expect_exact: '!function(){return function(){console.log("test")}}()()()()();' + expect_stdout: true +} + +issue_1288: { + options = { + conditionals: true, + negate_iife: true, + side_effects: false, + } + input: { + if (w) ; + else { + (function f() {})(); + } + if (!x) { + (function() { + x = {}; + })(); + } + if (y) + (function() {})(); + else + (function(z) { + return z; + })(0); + } + expect: { + w || !function f() {}(); + x || !function() { + x = {}; + }(); + y ? !function() {}() : !function(z) { + return z; + }(0); + } +} + +issue_1288_side_effects: { + options = { + conditionals: true, + negate_iife: true, + sequences: true, + side_effects: true, + } + input: { + if (w) ; + else { + (function f() {})(); + } + if (!x) { + (function() { + x = {}; + })(); + } + if (y) + (function() {})(); + else + (function(z) { + return z; + })(0); + } + expect: { + w, + x || function() { + x = {}; + }(), + y; + } +} diff --git a/test/compress/new.js b/test/compress/new.js new file mode 100644 index 00000000000..9c99f016de7 --- /dev/null +++ b/test/compress/new.js @@ -0,0 +1,102 @@ +new_statement: { + input: { + new x(1); + new x(1)(2); + new x(1)(2)(3); + new new x(1); + new new x(1)(2); + new (new x(1))(2); + (new new x(1))(2); + } + expect_exact: "new x(1);new x(1)(2);new x(1)(2)(3);new new x(1);new new x(1)(2);new new x(1)(2);(new new x(1))(2);" +} + +new_statements_2: { + input: { + new x; + new new x; + new new new x; + new true; + new (0); + new (!0); + new (bar = function(foo) {this.foo=foo;})(123); + new (bar = function(foo) {this.foo=foo;})(); + } + expect_exact: "new x;new(new x);new(new(new x));new true;new 0;new(!0);new(bar=function(foo){this.foo=foo})(123);new(bar=function(foo){this.foo=foo});" +} + +new_statements_3: { + input: { + new (function(foo){this.foo=foo;})(1); + new (function(foo){this.foo=foo;})(); + new (function test(foo){this.foo=foo;})(1); + new (function test(foo){this.foo=foo;})(); + } + expect_exact: "new function(foo){this.foo=foo}(1);new function(foo){this.foo=foo};new function test(foo){this.foo=foo}(1);new function test(foo){this.foo=foo};" +} + +new_with_rewritten_true_value: { + options = { + booleans: true, + } + input: { + new true; + } + expect_exact: "new(!0);" +} + +new_with_many_parameters: { + input: { + new foo.bar("baz"); + new x(/123/, 456); + } + expect_exact: 'new foo.bar("baz");new x(/123/,456);' +} + +new_constructor_with_unary_arguments: { + input: { + new x(); + new x(-1); + new x(-1, -2); + new x(void 1, +2, -3, ~4, !5, --a, ++b, c--, d++, typeof e, delete f); + new (-1); // should parse despite being invalid at runtime. + new (-1)(); // should parse despite being invalid at runtime. + new (-1)(-2); // should parse despite being invalid at runtime. + } + expect_exact: "new x;new x(-1);new x(-1,-2);new x(void 1,+2,-3,~4,!5,--a,++b,c--,d++,typeof e,delete f);new(-1);new(-1);new(-1)(-2);" +} + +call_with_unary_arguments: { + input: { + x(); + x(-1); + x(-1, -2); + x(void 1, +2, -3, ~4, !5, --a, ++b, c--, d++, typeof e, delete f); + (-1)(); // should parse despite being invalid at runtime. + (-1)(-2); // should parse despite being invalid at runtime. + } + expect_exact: "x();x(-1);x(-1,-2);x(void 1,+2,-3,~4,!5,--a,++b,c--,d++,typeof e,delete f);(-1)();(-1)(-2);" +} + +new_with_unary_prefix: { + input: { + var bar = (+new Date()).toString(32); + } + expect_exact: 'var bar=(+new Date).toString(32);'; +} + +dot_parentheses_1: { + input: { + console.log(new (Math.random().constructor) instanceof Number); + } + expect_exact: "console.log(new(Math.random().constructor)instanceof Number);" + expect_stdout: "true" +} + +dot_parentheses_2: { + input: { + console.log(typeof new function(){Math.random()}.constructor); + } + expect_exact: "console.log(typeof new function(){Math.random()}.constructor);" + expect_stdout: "function" +} diff --git a/test/compress/node_version.js b/test/compress/node_version.js new file mode 100644 index 00000000000..ef62fd70a98 --- /dev/null +++ b/test/compress/node_version.js @@ -0,0 +1,38 @@ +eval_let_6: { + input: { + eval("let a;"); + console.log(); + } + expect: { + eval("let a;"); + console.log(); + } + expect_stdout: "" + node_version: ">=6" +} + +eval_let_4: { + input: { + eval("let a;"); + console.log(); + } + expect: { + eval("let a;"); + console.log(); + } + expect_stdout: SyntaxError("Block-scoped declarations (let, const, function, class) not yet supported outside strict mode") + node_version: "4" +} + +eval_let_0: { + input: { + eval("let a;"); + console.log(); + } + expect: { + eval("let a;"); + console.log(); + } + expect_stdout: SyntaxError("Unexpected identifier") + node_version: "<=0.12" +} diff --git a/test/compress/nullish.js b/test/compress/nullish.js new file mode 100644 index 00000000000..2f438d45aea --- /dev/null +++ b/test/compress/nullish.js @@ -0,0 +1,346 @@ +parentheses: { + input: { + (console.log("foo") || console.log("bar") ?? console.log("baz")) && console.log("moo"); + } + expect_exact:'((console.log("foo")||console.log("bar"))??console.log("baz"))&&console.log("moo");' + expect_stdout: [ + "foo", + "bar", + "baz", + ] + node_version: ">=14" +} + +evaluate: { + options = { + evaluate: true, + side_effects: true, + } + input: { + void console.log("foo" ?? "bar") ?? console.log("baz"); + } + expect: { + console.log("foo"), + console.log("baz"); + } + expect_stdout: [ + "foo", + "baz", + ] + node_version: ">=14" +} + +conditional_assignment_1: { + options = { + collapse_vars: true, + } + input: { + console.log(function(a, b) { + b ?? (a = "FAIL"); + return a; + }("PASS", !console)); + } + expect: { + console.log(function(a, b) { + b ?? (a = "FAIL"); + return a; + }("PASS", !console)); + } + expect_stdout: "PASS" + node_version: ">=14" +} + +conditional_assignment_2: { + options = { + conditionals: true, + } + input: { + var a, b = false; + a = "PASS", + b ?? (a = "FAIL"), + console.log(a); + } + expect: { + var a, b = false; + a = "PASS", + b ?? (a = "FAIL"), + console.log(a); + } + expect_stdout: "PASS" + node_version: ">=14" +} + +conditional_assignment_3: { + options = { + conditionals: true, + join_vars: true, + } + input: { + var a, b = false; + a = "PASS", + b ?? (a = "FAIL"), + console.log(a); + } + expect: { + var a, b = false, a = "PASS"; + b ?? (a = "FAIL"), + console.log(a); + } + expect_stdout: "PASS" + node_version: ">=14" +} + +conditional_assignment_4: { + options = { + side_effects: true, + } + input: { + console.log(function(a) { + !console ?? (a = "FAIL"); + return a; + }("PASS")); + } + expect: { + console.log(function(a) { + !console ?? (a = "FAIL"); + return a; + }("PASS")); + } + expect_stdout: "PASS" + node_version: ">=14" +} + +de_morgan_1: { + options = { + booleans: true, + } + input: { + function f(a) { + return a ?? a; + } + console.log(f(null), f(42)); + } + expect: { + function f(a) { + return a; + } + console.log(f(null), f(42)); + } + expect_stdout: "null 42" + node_version: ">=14" +} + +de_morgan_2a: { + options = { + booleans: true, + conditionals: true, + evaluate: true, + } + input: { + function f(a, b) { + return a || (a ?? b); + } + console.log(f(null), f(null, {})); + console.log(f(42), f(42, {})); + } + expect: { + function f(a, b) { + return a || (a ?? b); + } + console.log(f(null), f(null, {})); + console.log(f(42), f(42, {})); + } + expect_stdout: [ + "undefined {}", + "42 42", + ] + node_version: ">=14" +} + +de_morgan_2b: { + options = { + booleans: true, + evaluate: true, + } + input: { + function f(a, b) { + return a && (a ?? b); + } + console.log(f(null), f(null, {})); + console.log(f(42), f(42, {})); + } + expect: { + function f(a, b) { + return a; + } + console.log(f(null), f(null, {})); + console.log(f(42), f(42, {})); + } + expect_stdout: [ + "null null", + "42 42", + ] + node_version: ">=14" +} + +de_morgan_2c: { + options = { + booleans: true, + evaluate: true, + side_effects: true, + } + input: { + function f(a, b) { + return a ?? (a || b); + } + console.log(f(null), f(null, {})); + console.log(f(42), f(42, {})); + } + expect: { + function f(a, b) { + return a ?? b; + } + console.log(f(null), f(null, {})); + console.log(f(42), f(42, {})); + } + expect_stdout: [ + "undefined {}", + "42 42", + ] + node_version: ">=14" +} + +de_morgan_2d: { + options = { + booleans: true, + evaluate: true, + } + input: { + function f(a, b) { + return a ?? (a && b); + } + console.log(f(null), f(null, {})); + console.log(f(42), f(42, {})); + } + expect: { + function f(a, b) { + return a; + } + console.log(f(null), f(null, {})); + console.log(f(42), f(42, {})); + } + expect_stdout: [ + "null null", + "42 42", + ] + node_version: ">=14" +} + +de_morgan_2e: { + options = { + booleans: true, + conditionals: true, + } + input: { + function f(a, b) { + return a ?? (a ?? b); + } + console.log(f(null), f(null, {})); + console.log(f(42), f(42, {})); + } + expect: { + function f(a, b) { + return a ?? b; + } + console.log(f(null), f(null, {})); + console.log(f(42), f(42, {})); + } + expect_stdout: [ + "undefined {}", + "42 42", + ] + node_version: ">=14" +} + +inline_binary_nullish: { + options = { + inline: true, + } + input: { + (function() { + while (console.log("foo")); + })() ?? (function() { + while (console.log("bar")); + })(); + } + expect: { + if (null == function() { + while (console.log("foo")); + }()) + while (console.log("bar")); + } + expect_stdout: [ + "foo", + "bar", + ] + node_version: ">=14" +} + +issue_4679: { + options = { + comparisons: true, + ie: true, + } + input: { + var a; + if (void 0 === (undefined ?? a)) + console.log("PASS"); + } + expect: { + var a; + if (void 0 === (undefined ?? a)) + console.log("PASS"); + } + expect_stdout: "PASS" + node_version: ">=14" +} + +issue_5266: { + options = { + inline: true, + side_effects: true, + } + input: { + [ + 42, + null, + false, + void 0, + "FAIL", + ].forEach(function (a) { + a ?? function() { + while (console.log(a)); + }(); + }); + } + expect: { + [ + 42, + null, + false, + void 0, + "FAIL", + ].forEach(function (a) { + if (null == a) { + while (console.log(a)); + return; + } else + return; + }); + } + expect_stdout: [ + "null", + "undefined", + ] + node_version: ">=14" +} diff --git a/test/compress/numbers.js b/test/compress/numbers.js new file mode 100644 index 00000000000..4996f31ea2b --- /dev/null +++ b/test/compress/numbers.js @@ -0,0 +1,1501 @@ +literal_infinity: { + input: { + console.log(2e308, -1e2345); + } + expect_exact: "console.log(1/0,-(1/0));" +} + +parentheses_for_prototype_functions: { + beautify = { + beautify: true, + } + input: { + (function() { + console.log((-2)); + console.log((-2).toFixed(0)); + + console.log((2)); + console.log((2).toFixed(0)); + + console.log((0.2)); + console.log((0.2).toFixed(0)); + + console.log((2.34e20)); + console.log((2.34e20).toFixed(0)); + + console.log((0.00000002)); + console.log((0.00000002).toFixed(0)); + + console.log((1000000000000000128)); + console.log((1000000000000000128).toFixed(0)); + + console.log((-1000000000000000128)); + console.log((-1000000000000000128).toFixed(0)); + })(); + } + expect_exact: [ + "(function() {", + " console.log(-2);", + " console.log((-2).toFixed(0));", + " console.log(2);", + " console.log(2..toFixed(0));", + " console.log(.2);", + " console.log(.2.toFixed(0));", + " console.log(234e18);", + " console.log(234e18.toFixed(0));", + " console.log(2e-8);", + " console.log(2e-8.toFixed(0));", + " console.log(0xde0b6b3a7640080);", + " console.log(0xde0b6b3a7640080.toFixed(0));", + " console.log(-0xde0b6b3a7640080);", + " console.log((-0xde0b6b3a7640080).toFixed(0));", + "})();", + ] + expect_stdout: true +} + +parentheses_for_prototype_functions_galio: { + beautify = { + beautify: true, + galio: true, + } + input: { + (function() { + console.log((-2)); + console.log((-2).toFixed(0)); + + console.log((2)); + console.log((2).toFixed(0)); + + console.log((0.2)); + console.log((0.2).toFixed(0)); + + console.log((2.34e20)); + console.log((2.34e20).toFixed(0)); + + console.log((0.00000002)); + console.log((0.00000002).toFixed(0)); + + console.log((1000000000000000128)); + console.log((1000000000000000128).toFixed(0)); + + console.log((-1000000000000000128)); + console.log((-1000000000000000128).toFixed(0)); + })(); + } + expect_exact: [ + "(function() {", + " console.log(-2);", + " console.log((-2).toFixed(0));", + " console.log(2);", + " console.log(2..toFixed(0));", + " console.log(.2);", + " console.log(.2.toFixed(0));", + " console.log(234e18);", + " console.log(234e18.toFixed(0));", + " console.log(2e-8);", + " console.log(2e-8.toFixed(0));", + " console.log(0xde0b6b3a7640080);", + " console.log((0xde0b6b3a7640080).toFixed(0));", + " console.log(-0xde0b6b3a7640080);", + " console.log((-0xde0b6b3a7640080).toFixed(0));", + "})();", + ] + expect_stdout: true +} + +octal: { + beautify = { + beautify: true, + } + input: { + (function() { + console.log(052); + console.log(-052); + + console.log(018); + console.log(-018); + + console.log(052.toFixed(0)); + console.log(-052.toFixed(0)); + + console.log(018..toFixed(0)); + console.log(-018..toFixed(0)); + })(); + } + expect_exact: [ + "(function() {", + " console.log(42);", + " console.log(-42);", + " console.log(18);", + " console.log(-18);", + " console.log(42..toFixed(0));", + " console.log(-42..toFixed(0));", + " console.log(18..toFixed(0));", + " console.log(-18..toFixed(0));", + "})();", + ] + expect_stdout: true +} + +comparisons: { + options = { + comparisons: true, + } + input: { + var x = "42", y = "0x30"; + console.log( + ~x === 42, + x % y === 42 + ); + } + expect: { + var x = "42", y = "0x30"; + console.log( + 42 == ~x, + x % y == 42 + ); + } + expect_stdout: "false true" +} + +evaluate_1: { + options = { + evaluate: true, + unsafe_math: false, + } + input: { + console.log( + x + 1 + 2, + x * 1 * 2, + +x + 1 + 2, + 1 + x + 2 + 3, + 1 | x | 2 | 3, + 1 + x-- + 2 + 3, + 1 + (x*y + 2) + 3, + 1 + (2 + x + 3), + 1 + (2 + ~x + 3), + -y + (2 + ~x + 3), + 1 & (2 & x & 3), + 1 + (2 + (x |= 0) + 3) + ); + } + expect: { + console.log( + x + 1 + 2, + 2 * +x, + +x + 1 + 2, + 1 + x + 2 + 3, + 3 | x, + 1 + x-- + 2 + 3, + x*y + 2 + 1 + 3, + 1 + (2 + x + 3), + 2 + ~x + 3 + 1, + -y + (2 + ~x + 3), + 0 & x, + 2 + (x |= 0) + 3 + 1 + ); + } +} + +evaluate_1_unsafe_math: { + options = { + evaluate: true, + unsafe_math: true, + } + input: { + console.log( + x + 1 + 2, + x * 1 * 2, + +x + 1 + 2, + 1 + x + 2 + 3, + 1 | x | 2 | 3, + 1 + x-- + 2 + 3, + 1 + (x*y + 2) + 3, + 1 + (2 + x + 3), + 1 + (2 + ~x + 3), + -y + (2 + ~x + 3), + 1 & (2 & x & 3), + 1 + (2 + (x |= 0) + 3) + ); + } + expect: { + console.log( + x + 1 + 2, + 2 * +x, + +x + 3, + 1 + x + 2 + 3, + 3 | x, + 6 + x--, + x*y + 6, + 1 + (2 + x + 3), + 6 + ~x, + 5 - y + ~x, + 0 & x, + 6 + (x |= 0) + ); + } +} + +evaluate_2: { + options = { + evaluate: true, + reduce_vars: true, + unsafe_math: false, + } + input: { + function f(num) { + var x = "" + num, y = null; + [ + x + 1 + 2, + x * 1 * 2, + +x + 1 + 2, + 1 + x + 2 + 3, + 1 | x | 2 | 3, + 1 + x-- + 2 + 3, + 1 + (x*y + 2) + 3, + 1 + (2 + x + 3), + 1 + (2 + ~x + 3), + -y + (2 + ~x + 3), + 1 & (2 & x & 3), + 1 + (2 + (x |= 0) + 3), + ].forEach(function(n) { + console.log(typeof n, n); + }); + } + f(42); + } + expect: { + function f(num) { + var x = "" + num, y = null; + [ + x + "12", + 2 * x, + +x + 1 + 2, + 1 + x + "23", + 3 | x, + 1 + x-- + 2 + 3, + x*y + 2 + 1 + 3, + 2 + x + 3 + 1, + 2 + ~x + 3 + 1, + 2 + ~x + 3, + 0 & x, + 2 + (x |= 0) + 3 + 1, + ].forEach(function(n) { + console.log(typeof n, n); + }); + } + f(42); + } + expect_stdout: [ + "string 4212", + "number 84", + "number 45", + "string 14223", + "number 43", + "number 48", + "number 6", + "number 47", + "number -36", + "number -37", + "number 0", + "number 47", + ] +} + +evaluate_2_unsafe_math: { + options = { + evaluate: true, + reduce_vars: true, + unsafe_math: true, + } + input: { + function f(num) { + var x = "" + num, y = null; + [ + x + 1 + 2, + x * 1 * 2, + +x + 1 + 2, + 1 + x + 2 + 3, + 1 | x | 2 | 3, + 1 + x-- + 2 + 3, + 1 + (x*y + 2) + 3, + 1 + (2 + x + 3), + 1 + (2 + ~x + 3), + -y + (2 + ~x + 3), + 1 & (2 & x & 3), + 1 + (2 + (x |= 0) + 3), + ].forEach(function(n) { + console.log(typeof n, n); + }); + } + f(42); + } + expect: { + function f(num) { + var x = "" + num, y = null; + [ + x + "12", + 2 * x, + +x + 3, + 1 + x + "23", + 3 | x, + 6 + x--, + x*y + 6, + 6 + x, + 6 + ~x, + 5 + ~x, + 0 & x, + 6 + (x |= 0), + ].forEach(function(n) { + console.log(typeof n, n); + }); + } + f(42); + } + expect_stdout: [ + "string 4212", + "number 84", + "number 45", + "string 14223", + "number 43", + "number 48", + "number 6", + "number 47", + "number -36", + "number -37", + "number 0", + "number 47", + ] +} + +evaluate_3: { + options = { + evaluate: true, + unsafe: true, + unsafe_math: true, + } + input: { + console.log(1 + Number(x) + 2); + } + expect: { + console.log(+("" + x) + 3); + } +} + +evaluate_4: { + options = { + evaluate: true, + } + input: { + console.log( + 1+ +a, + +a+1, + 1+-a, + -a+1, + +a+ +b, + +a+-b, + -a+ +b, + -a+-b + ); + } + expect: { + console.log( + +a+1, + +a+1, + 1-a, + 1-a, + +a+ +b, + +a-b, + -a+ +b, + -a-b + ); + } +} + +evaluate_5: { + options = { + evaluate: true, + reduce_vars: true, + unsafe_math: false, + } + input: { + function f(num) { + var a = "" + num; + [ + +a + 2 + 3, + +a + 2 - 3, + +a - 2 + 3, + +a - 2 - 3, + 2 + +a + 3, + 2 + +a - 3, + 2 - +a + 3, + 2 - +a - 3, + 2 + 3 + +a, + 2 + 3 - +a, + 2 - 3 + +a, + 2 - 3 - +a, + ].forEach(function(n) { + console.log(typeof n, n); + }); + } + f(1); + } + expect: { + function f(num) { + var a = "" + num; + [ + +a + 2 + 3, + +a + 2 - 3, + a - 2 + 3, + a - 2 - 3, + +a + 2 + 3, + +a + 2 - 3, + 2 - a + 3, + 2 - a - 3, + +a + 5, + 5 - a, + +a - 1, + -1 - a, + ].forEach(function(n) { + console.log(typeof n, n); + }); + } + f(1); + } + expect_stdout: [ + "number 6", + "number 0", + "number 2", + "number -4", + "number 6", + "number 0", + "number 4", + "number -2", + "number 6", + "number 4", + "number 0", + "number -2", + ] +} + +evaluate_5_unsafe_math: { + options = { + evaluate: true, + reduce_vars: true, + unsafe_math: true, + } + input: { + function f(num) { + var a = "" + num; + [ + +a + 2 + 3, + +a + 2 - 3, + +a - 2 + 3, + +a - 2 - 3, + 2 + +a + 3, + 2 + +a - 3, + 2 - +a + 3, + 2 - +a - 3, + 2 + 3 + +a, + 2 + 3 - +a, + 2 - 3 + +a, + 2 - 3 - +a, + ].forEach(function(n) { + console.log(typeof n, n); + }); + } + f(1); + } + expect: { + function f(num) { + var a = "" + num; + [ + +a + 5, + +a + -1, + a - -1, + a - 5, + +a + 5, + +a + -1, + 5 - a, + -1 - a, + +a + 5, + 5 - a, + +a - 1, + -1 - a, + ].forEach(function(n) { + console.log(typeof n, n); + }); + } + f(1); + } + expect_stdout: [ + "number 6", + "number 0", + "number 2", + "number -4", + "number 6", + "number 0", + "number 4", + "number -2", + "number 6", + "number 4", + "number 0", + "number -2", + ] +} + +evaluate_6: { + options = { + evaluate: true, + unsafe_math: false, + } + input: { + var a = "1"; + [ + -a + 2 + 3, + -a + 2 - 3, + -a - 2 + 3, + -a - 2 - 3, + 2 + -a + 3, + 2 + -a - 3, + 2 - -a + 3, + 2 - -a - 3, + 2 + 3 + -a, + 2 + 3 - -a, + 2 - 3 + -a, + 2 - 3 - -a, + ].forEach(function(n) { + console.log(typeof n, n); + }); + } + expect: { + var a = "1"; + [ + 2 - a + 3, + 2 - a - 3, + -a - 2 + 3, + -a - 2 - 3, + 2 - a + 3, + 2 - a - 3, + 2 - -a + 3, + 2 - -a - 3, + 5 - a, + 5 - -a, + -1 - a, + -1 - -a, + ].forEach(function(n) { + console.log(typeof n, n); + }); + } + expect_stdout: [ + "number 4", + "number -2", + "number 0", + "number -6", + "number 4", + "number -2", + "number 6", + "number 0", + "number 4", + "number 6", + "number -2", + "number 0", + ] +} + +evaluate_6_unsafe_math: { + options = { + evaluate: true, + unsafe_math: true, + } + input: { + var a = "1"; + [ + -a + 2 + 3, + -a + 2 - 3, + -a - 2 + 3, + -a - 2 - 3, + 2 + -a + 3, + 2 + -a - 3, + 2 - -a + 3, + 2 - -a - 3, + 2 + 3 + -a, + 2 + 3 - -a, + 2 - 3 + -a, + 2 - 3 - -a, + ].forEach(function(n) { + console.log(typeof n, n); + }); + } + expect: { + var a = "1"; + [ + 5 - a, + -1 - a, + -a - -1, + -a - 5, + 5 - a, + -1 - a, + 5 - -a, + -1 - -a, + 5 - a, + 5 - -a, + -1 - a, + -1 - -a, + ].forEach(function(n) { + console.log(typeof n, n); + }); + } + expect_stdout: [ + "number 4", + "number -2", + "number 0", + "number -6", + "number 4", + "number -2", + "number 6", + "number 0", + "number 4", + "number 6", + "number -2", + "number 0", + ] +} + +evaluate_7: { + options = { + evaluate: true, + reduce_vars: true, + unsafe_math: false, + } + input: { + function f(num, y) { + var x = "" + num; + [ + +x + 2 + (3 + !y), + +x + 2 + (3 - !y), + +x + 2 - (3 + !y), + +x + 2 - (3 - !y), + +x - 2 + (3 + !y), + +x - 2 + (3 - !y), + +x - 2 - (3 + !y), + +x - 2 - (3 - !y), + ].forEach(function(n) { + console.log(typeof n, n); + }); + } + f(42); + } + expect: { + function f(num, y) { + var x = "" + num; + [ + +x + 2 + (3 + !y), + +x + 2 + (3 - !y), + +x + 2 - (3 + !y), + +x + 2 - (3 - !y), + x - 2 + (3 + !y), + x - 2 + (3 - !y), + x - 2 - (3 + !y), + x - 2 - (3 - !y), + ].forEach(function(n) { + console.log(typeof n, n); + }); + } + f(42); + } + expect_stdout: [ + "number 48", + "number 46", + "number 40", + "number 42", + "number 44", + "number 42", + "number 36", + "number 38", + ] +} + +evaluate_7_unsafe_math: { + options = { + evaluate: true, + reduce_vars: true, + unsafe_math: true, + } + input: { + function f(num, y) { + var x = "" + num; + [ + +x + 2 + (3 + !y), + +x + 2 + (3 - !y), + +x + 2 - (3 + !y), + +x + 2 - (3 - !y), + +x - 2 + (3 + !y), + +x - 2 + (3 - !y), + +x - 2 - (3 + !y), + +x - 2 - (3 - !y), + ].forEach(function(n) { + console.log(typeof n, n); + }); + } + f(42); + } + expect: { + function f(num, y) { + var x = "" + num; + [ + +x + 5 + !y, + +x + 5 - !y, + +x + -1 - !y, + +x + -1 + !y, + x - -1 + !y, + x - -1 - !y, + x - 5 - !y, + x - 5 + !y, + ].forEach(function(n) { + console.log(typeof n, n); + }); + } + f(42); + } + expect_stdout: [ + "number 48", + "number 46", + "number 40", + "number 42", + "number 44", + "number 42", + "number 36", + "number 38", + ] +} + +evaluate_8_unsafe_math: { + options = { + evaluate: true, + unsafe_math: true, + } + input: { + var a = [ "42" ]; + console.log(a * (1 / 7)); + } + expect: { + var a = [ "42" ]; + console.log(+a / 7); + } + expect_stdout: "6" +} + +NaN_redefined: { + options = { + evaluate: true, + unsafe_math: true, + } + input: { + var NaN; + console.log(1 / (0 / 0)); + } + expect: { + var NaN; + console.log(0 / 0); + } + expect_stdout: "NaN" +} + +issue_1710: { + options = { + evaluate: true, + } + input: { + var x = {}; + console.log((x += 1) + -x); + } + expect: { + var x = {}; + console.log((x += 1) + -x); + } + expect_stdout: true +} + +unary_binary_parentheses: { + options = { + evaluate: true, + } + input: { + var v = [ 0, 1, NaN, Infinity, null, undefined, true, false, "", "foo", /foo/ ]; + v.forEach(function(x) { + v.forEach(function(y) { + console.log( + +(x*y), + +(x/y), + +(x%y), + -(x*y), + -(x/y), + -(x%y) + ); + }); + }); + } + expect: { + var v = [ 0, 1, NaN, 1/0, null, void 0, true, false, "", "foo", /foo/ ]; + v.forEach(function(x) { + v.forEach(function(y) { + console.log( + x*y, + x/y, + x%y, + -x*y, + -x/y, + -x%y + ); + }); + }); + } + expect_stdout: true +} + +issue_3531_1: { + options = { + evaluate: true, + unsafe_math: true, + } + input: { + var a = "1"; + console.log(typeof (a + 1 - .1 - .1 - .1)); + } + expect: { + var a = "1"; + console.log(typeof (a + 1 - .3)); + } + expect_stdout: "number" +} + +issue_3531_2: { + options = { + evaluate: true, + unsafe_math: true, + } + input: { + console.log(1 - (2 - {})); + } + expect: { + console.log(-1 + +{}); + } + expect_stdout: "NaN" +} + +issue_3531_3: { + options = { + evaluate: true, + unsafe_math: true, + } + input: { + var a = "3"; + console.log(1 - (2 + a)); + } + expect: { + var a = "3"; + console.log(1 - (2 + a)); + } + expect_stdout: "-22" +} + +issue_3536: { + options = { + evaluate: true, + unsafe_math: true, + } + input: { + var a = 100, b = 10; + var c = --a + ("23" - (b++, 1)); + console.log(typeof c, a, b, c); + } + expect: { + var a = 100, b = 10; + var c = --a + ("23" - (b++, 1)); + console.log(typeof c, a, b, c); + } + expect_stdout: "number 99 11 121" +} + +issue_3539: { + options = { + evaluate: true, + unsafe_math: true, + } + input: { + var a = -0 + -""; + console.log(0/a, 1/a, -1/a); + } + expect: { + var a = -0; + console.log(0/a, 1/a, -1/a); + } + expect_stdout: "NaN -Infinity Infinity" +} + +issue_3547_1: { + options = { + evaluate: true, + unsafe_math: true, + } + input: { + [ + 1/0 + "1" + 0, + 1/0 + "1" - 0, + 1/0 - "1" + 0, + 1/0 - "1" - 0, + ].forEach(function(n) { + console.log(typeof n, n); + }); + } + expect: { + [ + 1/0 + "10", + NaN, + 1/0, + 1/0, + ].forEach(function(n) { + console.log(typeof n, n); + }); + } + expect_stdout: [ + "string Infinity10", + "number NaN", + "number Infinity", + "number Infinity", + ] +} + +issue_3547_2: { + options = { + evaluate: true, + unsafe_math: true, + } + input: { + [ + "1" + 1/0 + 0, + "1" + 1/0 - 0, + "1" - 1/0 + 0, + "1" - 1/0 - 0, + ].forEach(function(n) { + console.log(typeof n, n); + }); + } + expect: { + [ + "1" + 1/0 + 0, + NaN, + -1/0, + -1/0, + ].forEach(function(n) { + console.log(typeof n, n); + }); + } + expect_stdout: [ + "string 1Infinity0", + "number NaN", + "number -Infinity", + "number -Infinity", + ] +} + +issue_3547_3: { + options = { + evaluate: true, + unsafe_math: true, + } + input: { + var a = "3"; + [ + a + "2" + 1, + a + "2" - 1, + a - "2" + 1, + a - "2" - 1, + ].forEach(function(n) { + console.log(typeof n, n); + }); + } + expect: { + var a = "3"; + [ + a + "21", + a + "2" - 1, + a - 1, + a - 3, + ].forEach(function(n) { + console.log(typeof n, n); + }); + } + expect_stdout: [ + "string 321", + "number 31", + "number 2", + "number 0", + ] +} + +issue_3547_4: { + options = { + evaluate: true, + unsafe_math: true, + } + input: { + var a = "2"; + [ + "3" + a + 1, + "3" + a - 1, + "3" - a + 1, + "3" - a - 1, + ].forEach(function(n) { + console.log(typeof n, n); + }); + } + expect: { + var a = "2"; + [ + "3" + a + 1, + "3" + a - 1, + 4 - a, + 2 - a, + ].forEach(function(n) { + console.log(typeof n, n); + }); + } + expect_stdout: [ + "string 321", + "number 31", + "number 2", + "number 0", + ] +} + +unsafe_math_rounding: { + options = { + evaluate: true, + unsafe_math: true, + } + input: { + console.log(4 / -3 + 1 === 1 / -3); + } + expect: { + console.log(false); + } + expect_stdout: "false" +} + +issue_3593: { + options = { + evaluate: true, + unsafe_math: true, + } + input: { + console.log((0 === this) - 1 - "1"); + } + expect: { + console.log((0 === this) - 2); + } + expect_stdout: "-2" +} + +unsafe_math_swap_constant: { + options = { + evaluate: true, + unsafe_math: true, + } + input: { + var a = 1, b = 2; + console.log( + a++ + b-- + 3, + a++ + b + 3, + a + b-- + 3, + a + b + 3, + a++ - b-- + 3, + a++ - b + 3, + a - b-- + 3, + a - b + 3 + ); + } + expect: { + var a = 1, b = 2; + console.log( + 3 + a++ + b--, + a++ + b + 3, + a + b-- + 3, + a + b + 3, + 3 + a++ - b--, + 3 + a++ - b, + a - b-- + 3, + a - b + 3 + ); + } + expect_stdout: "6 6 7 6 6 8 9 10" +} + +identity_1: { + options = { + evaluate: true, + } + input: { + 0 + a; + a + 0; + 0 - a; + a - 0; + 1 * a; + a * 1; + 1 / a; + a / 1; + } + expect: { + 0 + a; + a + 0; + 0 - a; + +a; + +a; + +a; + 1 / a; + +a; + } +} + +identity_2: { + options = { + evaluate: true, + } + input: { + 0 + !a; + !a + 0; + 0 - !a; + !a - 0; + 1 * !a; + !a * 1; + 1 / !a; + !a / 1; + } + expect: { + +!a; + +!a; + 0 - !a; + +!a; + +!a; + +!a; + 1 / !a; + +!a; + } +} + +identity_3: { + options = { + evaluate: true, + } + input: { + 0 + --a; + --a + 0; + 0 - --a; + --a - 0; + 1 * --a; + --a * 1; + 1 / --a; + --a / 1; + } + expect: { + --a; + --a; + 0 - --a; + --a; + --a; + --a; + 1 / --a; + --a; + } +} + +issue_3653: { + options = { + evaluate: true, + } + input: { + console.log(0 - (console && 0)); + console.log(0 + (0 - (console && 0))); + console.log(0 - (0 - (console && 0))); + console.log(1 * (0 - (console && 0))); + console.log(1 / (0 - (console && 0))); + console.log((0 - (console && 0)) + 0); + console.log((0 - (console && 0)) - 0); + console.log((0 - (console && 0)) * 1); + console.log((0 - (console && 0)) / 1); + } + expect: { + console.log(0 - (console && 0)); + console.log(0 - (console && 0)); + console.log(0 - (0 - (console && 0))); + console.log(0 - (console && 0)); + console.log(1 / (0 - (console && 0))); + console.log(0 - (console && 0)); + console.log(0 - (console && 0)); + console.log(0 - (console && 0)); + console.log(0 - (console && 0)); + } + expect_stdout: [ + "0", + "0", + "0", + "0", + "Infinity", + "0", + "0", + "0", + "0", + ] +} + +issue_3655: { + options = { + evaluate: true, + } + input: { + console.log(0 + 0 * -[].length); + console.log(0 + (0 + 0 * -[].length)); + console.log(0 - (0 + 0 * -[].length)); + console.log(1 * (0 + 0 * -[].length)); + console.log(1 / (0 + 0 * -[].length)); + console.log((0 + 0 * -[].length) + 0); + console.log((0 + 0 * -[].length) - 0); + console.log((0 + 0 * -[].length) * 1); + console.log((0 + 0 * -[].length) / 1); + } + expect: { + console.log(0 + 0 * -[].length); + console.log(0 + 0 * -[].length); + console.log(0 - (0 + 0 * -[].length)); + console.log(0 + 0 * -[].length); + console.log(1 / (0 + 0 * -[].length)); + console.log(0 + 0 * -[].length); + console.log(0 + 0 * -[].length); + console.log(0 + 0 * -[].length); + console.log(0 + 0 * -[].length); + } + expect_stdout: [ + "0", + "0", + "0", + "0", + "Infinity", + "0", + "0", + "0", + "0", + ] +} + +issue_3676_1: { + options = { + evaluate: true, + unsafe_math: true, + } + input: { + var a = []; + console.log(false - (a - (a[1] = 42))); + } + expect: { + var a = []; + console.log(false - (a - (a[1] = 42))); + } + expect_stdout: "NaN" +} + +issue_3676_2: { + options = { + evaluate: true, + unsafe_math: true, + } + input: { + var a; + console.log(false - ((a = []) - (a[1] = 42))); + } + expect: { + var a; + console.log(false - ((a = []) - (a[1] = 42))); + } + expect_stdout: "NaN" +} + +issue_3682_1: { + options = { + evaluate: true, + unsafe_math: true, + } + input: { + var a = -0; + console.log(1 / (a - 1 + 1)); + } + expect: { + var a = -0; + console.log(1 / (a - 1 + 1)); + } + expect_stdout: "Infinity" +} + +issue_3682_2: { + options = { + evaluate: true, + unsafe_math: true, + } + input: { + var a = -0, b = 1; + console.log(1 / (a - (b - b))); + } + expect: { + var a = -0, b = 1; + console.log(1 / (a - (b - b))); + } + expect_stdout: "-Infinity" +} + +issue_3682_3: { + options = { + evaluate: true, + unsafe_math: true, + } + input: { + var a = -0, b = 1, c = -1; + console.log(1 / (a - (+b + +c))); + } + expect: { + var a = -0, b = 1, c = -1; + console.log(1 / (a - (+b + +c))); + } + expect_stdout: "-Infinity" +} + +issue_3684: { + options = { + evaluate: true, + } + input: { + console.log(1 / (-1 * (0 & console) + 0)); + console.log(1 / ((0 & console) / -1 + 0)); + } + expect: { + console.log(1 / (-1 * (0 & console) + 0)); + console.log(1 / ((0 & console) / -1 + 0)); + } + expect_stdout: [ + "Infinity", + "Infinity", + ] +} + +issue_3695: { + options = { + evaluate: true, + } + input: { + var a = []; + console.log(+(a * (a[0] = false))); + } + expect: { + var a = []; + console.log(a * (a[0] = false)); + } + expect_stdout: "NaN" +} + +issue_4137: { + options = { + evaluate: true, + } + input: { + console.log(+(A = []) * (A[0] = 1)); + } + expect: { + console.log(+(A = []) * (A[0] = 1)); + } + expect_stdout: "0" +} + +issue_4142: { + options = { + evaluate: true, + } + input: { + console.log("" + +(0 === console)); + } + expect: { + console.log("" + +(0 === console)); + } + expect_stdout: "0" +} + +issue_4542_1: { + options = { + evaluate: true, + unsafe_math: true, + } + input: { + console.log(function(a) { + return a / (1 / (a[0] = 2)); + }([ 3 ])); + } + expect: { + console.log(function(a) { + return a / (1 / (a[0] = 2)); + }([ 3 ])); + } + expect_stdout: "4" +} + +issue_4542_2: { + options = { + evaluate: true, + unsafe_math: true, + } + input: { + console.log(function(a) { + return a / (1 / --a[0]); + }([ 3 ])); + } + expect: { + console.log(function(a) { + return a / (1 / --a[0]); + }([ 3 ])); + } + expect_stdout: "4" +} + +issue_4542_3: { + options = { + evaluate: true, + unsafe_math: true, + } + input: { + console.log(function(a) { + return a / (0 / (a[0] = 0, 1)); + }([ 1 ])); + } + expect: { + console.log(function(a) { + return a / (0 / (a[0] = 0, 1)); + }([ 1 ])); + } + expect_stdout: "NaN" +} + +issue_4542_4: { + options = { + evaluate: true, + unsafe_math: true, + } + input: { + console.log(function(a) { + return a / (1 / (a.length = 1)); + }([ 2, 3 ])); + } + expect: { + console.log(function(a) { + return a / (1 / (a.length = 1)); + }([ 2, 3 ])); + } + expect_stdout: "2" +} diff --git a/test/compress/objects.js b/test/compress/objects.js new file mode 100644 index 00000000000..fdb9ea832e2 --- /dev/null +++ b/test/compress/objects.js @@ -0,0 +1,547 @@ +duplicate_key: { + options = { + objects: true, + side_effects: true, + } + input: { + var o = { + a: 1, + b: 2, + a: 3, + }; + for (var k in o) + console.log(k, o[k]); + } + expect: { + var o = { + a: 3, + b: 2, + }; + for (var k in o) + console.log(k, o[k]); + } + expect_stdout: [ + "a 3", + "b 2", + ] +} + +duplicate_key_strict: { + options = { + objects: true, + side_effects: true, + } + input: { + "use strict"; + var o = { + a: 1, + b: 2, + a: 3, + }; + for (var k in o) + console.log(k, o[k]); + } + expect: { + "use strict"; + var o = { + a: 1, + a: 3, + b: 2, + }; + for (var k in o) + console.log(k, o[k]); + } + expect_stdout: true +} + +duplicate_key_side_effect: { + options = { + objects: true, + side_effects: true, + } + input: { + var o = { + a: 1, + b: o = 2, + a: 3, + }; + for (var k in o) + console.log(k, o[k]); + } + expect: { + var o = { + a: 1, + b: o = 2, + a: 3, + }; + for (var k in o) + console.log(k, o[k]); + } + expect_stdout: [ + "a 3", + "b 2", + ] +} + +duplicate_key_with_accessor: { + options = { + objects: true, + side_effects: true, + } + input: { + [ + { + a: 0, + b: 1, + a: 2, + set b(v) {}, + }, + { + a: 3, + b: 4, + get a() { + return 5; + }, + a: 6, + b: 7, + a: 8, + b: 9, + }, + ].forEach(function(o) { + for (var k in o) + console.log(k, o[k]); + }); + } + expect: { + [ + { + a: 2, + b: 1, + set b(v) {}, + }, + { + a: 3, + b: 4, + get a() { + return 5; + }, + a: 8, + b: 9, + }, + ].forEach(function(o) { + for (var k in o) + console.log(k, o[k]); + }); + } + expect_stdout: true +} + +unsafe_object_repeated: { + options = { + evaluate: true, + objects: true, + reduce_funcs: true, + reduce_vars: true, + side_effects: true, + toplevel: true, + unsafe: true, + } + input: { + var o = { a: { b: 1 }, a: 1 }; + console.log( + o + 1, + o.a + 1, + o.b + 1, + o.a.b + 1 + ); + } + expect: { + var o = { a: 1 }; + console.log( + o + 1, + 2, + o.b + 1, + NaN + ); + } + expect_stdout: true +} + +numeric_literal: { + options = { + objects: true, + side_effects: true, + } + mangle = { + properties: { + domprops: true, + }, + } + beautify = { + beautify: true, + } + input: { + var obj = { + 0: 0, + "-0": 1, + 42: 2, + "42": 3, + 0x25: 4, + "0x25": 5, + 1E42: 6, + "1E42": 7, + "1e+42": 8, + }; + console.log(obj[-0], obj[-""], obj["-0"]); + console.log(obj[42], obj["42"]); + console.log(obj[0x25], obj["0x25"], obj[37], obj["37"]); + console.log(obj[1E42], obj["1E42"], obj["1e+42"]); + } + expect_exact: [ + 'var obj = {', + ' 0: 0,', + ' "-0": 1,', + ' 42: 3,', + ' 37: 4,', + ' o: 5,', + ' 1e42: 8,', + ' b: 7', + '};', + '', + 'console.log(obj[-0], obj[-""], obj["-0"]);', + '', + 'console.log(obj[42], obj["42"]);', + '', + 'console.log(obj[37], obj["o"], obj[37], obj["37"]);', + '', + 'console.log(obj[1e42], obj["b"], obj["1e+42"]);', + ] + expect_stdout: [ + "0 0 1", + "3 3", + "4 5 4 4", + "8 7 8", + ] +} + +evaluate_computed_key: { + options = { + evaluate: true, + objects: true, + } + input: { + console.log({ + ["foo" + "bar"]: "PASS", + }.foobar); + } + expect: { + console.log({ + foobar: "PASS", + }.foobar); + } + expect_stdout: "PASS" + node_version: ">=4" +} + +keep_computed_key: { + options = { + side_effects: true, + } + input: { + ({ + [console.log("PASS")]: 42, + }); + } + expect: { + console.log("PASS"); + } + expect_stdout: "PASS" + node_version: ">=4" +} + +shorthand_keywords: { + options = { + evaluate: true, + reduce_vars: true, + toplevel: true, + unsafe: true, + unused: true, + } + input: { + var async = 1, get = 2, set = 3, o = { + async, + get, + set, + }; + console.log(o.async, o.get, o.set); + } + expect: { + console.log(1, 2, 3); + } + expect_stdout: "1 2 3" + node_version: ">=6" +} + +object_super: { + input: { + var o = { + f() { + return super.p; + }, + p: "FAIL", + }; + Object.setPrototypeOf(o, { p: "PASS" }); + console.log(o.f()); + } + expect_exact: 'var o={f(){return super.p},p:"FAIL"};Object.setPrototypeOf(o,{p:"PASS"});console.log(o.f());' + expect_stdout: "PASS" + node_version: ">=4" +} + +object_super_async: { + input: { + var o = { + async f() { + return super.p; + }, + p: "FAIL", + }; + Object.setPrototypeOf(o, { p: "PASS" }); + o.f().then(console.log); + } + expect_exact: 'var o={async f(){return super.p},p:"FAIL"};Object.setPrototypeOf(o,{p:"PASS"});o.f().then(console.log);' + expect_stdout: "PASS" + node_version: ">=8" +} + +object_super_generator: { + input: { + var o = { + *f() { + yield super.p; + }, + p: "FAIL", + }; + Object.setPrototypeOf(o, { p: "PASS" }); + console.log(o.f().next().value); + } + expect_exact: 'var o={*f(){yield super.p},p:"FAIL"};Object.setPrototypeOf(o,{p:"PASS"});console.log(o.f().next().value);' + expect_stdout: "PASS" + node_version: ">=4" +} + +object_super_async_generator: { + input: { + var o = { + async *f() { + return super.p; + }, + p: "FAIL", + }; + Object.setPrototypeOf(o, { p: "PASS" }); + o.f().next().then(function(v) { + console.log(v.value, v.done); + }); + } + expect_exact: 'var o={async*f(){return super.p},p:"FAIL"};Object.setPrototypeOf(o,{p:"PASS"});o.f().next().then(function(v){console.log(v.value,v.done)});' + expect_stdout: "PASS true" + node_version: ">=10" +} + +issue_4269_1: { + options = { + evaluate: true, + objects: true, + } + input: { + console.log({ + get 0() { + return "FAIL"; + }, + [0]: "PASS", + }[0]); + } + expect: { + console.log({ + get 0() { + return "FAIL"; + }, + [0]: "PASS", + }[0]); + } + expect_stdout: "PASS" + node_version: ">=4" +} + +issue_4269_2: { + options = { + evaluate: true, + objects: true, + } + input: { + console.log({ + get [0]() { + return "FAIL"; + }, + 0: "PASS", + }[0]); + } + expect: { + console.log({ + get [0]() { + return "FAIL"; + }, + 0: "PASS", + }[0]); + } + expect_stdout: "PASS" + node_version: ">=4" +} + +issue_4269_3: { + options = { + evaluate: true, + objects: true, + } + input: { + console.log({ + ["foo"]: "bar", + get 42() { + return "FAIL"; + }, + 42: "PASS", + }[42]); + } + expect: { + console.log({ + foo: "bar", + get [42]() { + return "FAIL"; + }, + 42: "PASS", + }[42]); + } + expect_stdout: "PASS" + node_version: ">=4" +} + +issue_4269_4: { + options = { + evaluate: true, + objects: true, + } + input: { + console.log({ + get 42() { + return "FAIL"; + }, + ["foo"]: "bar", + 42: "PASS", + }[42]); + } + expect: { + console.log({ + get 42() { + return "FAIL"; + }, + foo: "bar", + [42]: "PASS", + }[42]); + } + expect_stdout: "PASS" + node_version: ">=4" +} + +issue_4269_5: { + options = { + evaluate: true, + objects: true, + } + input: { + console.log({ + get 42() { + return "FAIL"; + }, + [console]: "bar", + 42: "PASS", + }[42]); + } + expect: { + console.log({ + get 42() { + return "FAIL"; + }, + [console]: "bar", + 42: "PASS", + }[42]); + } + expect_stdout: "PASS" + node_version: ">=4" +} + +issue_4380: { + options = { + evaluate: true, + objects: true, + } + input: { + console.log({ + get 0() { + return "FAIL 1"; + }, + 0: "FAIL 2", + [0]: "PASS", + }[0]); + } + expect: { + console.log({ + get 0() { + return "FAIL 1"; + }, + [0]: ("FAIL 2", "PASS"), + }[0]); + } + expect_stdout: "PASS" + node_version: ">=4" +} + +issue_4415: { + options = { + evaluate: true, + objects: true, + } + input: { + console.log({ + ["00"]: "FAIL", + }[0] || "PASS"); + } + expect: { + console.log({ + "00": "FAIL", + }[0] || "PASS"); + } + expect_stdout: "PASS" + node_version: ">=4" +} + +issue_5213: { + options = { + objects: true, + } + input: { + var a = "FAIL"; + console.log({ + p: a = "PASS", + 0: a, + p: null, + }[0]); + } + expect: { + var a = "FAIL"; + console.log({ + p: (a = "PASS", null), + 0: a, + }[0]); + } + expect_stdout: "PASS" +} diff --git a/test/compress/optional-chains.js b/test/compress/optional-chains.js new file mode 100644 index 00000000000..fcff9a8a94d --- /dev/null +++ b/test/compress/optional-chains.js @@ -0,0 +1,619 @@ +call: { + input: { + console.log?.(undefined?.(console.log("FAIL"))); + } + expect_exact: 'console.log?.((void 0)?.(console.log("FAIL")));' + expect_stdout: "undefined" + node_version: ">=14" +} + +dot: { + input: { + console?.log((void 0)?.p); + } + expect_exact: "console?.log((void 0)?.p);" + expect_stdout: "undefined" + node_version: ">=14" +} + +dot_in: { + input: { + var o = { in: 42 }; + console.log(o.in, o?.in); + } + expect_exact: "var o={in:42};console.log(o.in,o?.in);" + expect_stdout: "42 42" + node_version: ">=14" +} + +sub: { + input: { + console?.["log"](null?.[console.log("FAIL")]); + } + expect_exact: 'console?.["log"](null?.[console.log("FAIL")]);' + expect_stdout: "undefined" + node_version: ">=14" +} + +ternary_decimal: { + input: { + null ? .42 : console.log("PASS"); + } + expect_exact: 'null?.42:console.log("PASS");' + expect_stdout: "PASS" +} + +assign_parentheses_call: { + input: { + var o = {}; + ((() => o)?.()).p = "PASS"; + console.log(o.p); + } + expect_exact: 'var o={};((()=>o)?.()).p="PASS";console.log(o.p);' + expect_stdout: "PASS" + node_version: ">=14" +} + +assign_parentheses_dot: { + input: { + (console?.log).name.p = console.log("PASS"); + } + expect_exact: '(console?.log).name.p=console.log("PASS");' + expect_stdout: "PASS" + node_version: ">=14" +} + +assign_no_parentheses: { + input: { + console[console.log?.("PASS")] = 42; + } + expect_exact: 'console[console.log?.("PASS")]=42;' + expect_stdout: "PASS" + node_version: ">=14" +} + +call_parentheses: { + input: { + (function(o) { + console.log(o.f("FAIL"), (o.f)("FAIL"), (0, o.f)(42)); + console.log(o?.f("FAIL"), (o?.f)("FAIL"), (0, o?.f)(42)); + })({ + a: "PASS", + f(b) { + return this.a || b; + }, + }); + } + expect_exact: '(function(o){console.log(o.f("FAIL"),o.f("FAIL"),(0,o.f)(42));console.log(o?.f("FAIL"),(o?.f)("FAIL"),(0,o?.f)(42))})({a:"PASS",f(b){return this.a||b}});' + expect_stdout: [ + "PASS PASS 42", + "PASS PASS 42", + ] + node_version: ">=14" +} + +unary_parentheses: { + input: { + var o = { p: 41 }; + (function() { + return o; + }?.()).p++; + console.log(o.p); + } + expect_exact: "var o={p:41};(function(){return o}?.()).p++;console.log(o.p);" + expect_stdout: "42" + node_version: ">=14" +} + +collapse_vars_1: { + options = { + collapse_vars: true, + } + input: { + var a; + A = 42; + a?.[42]; + console.log(typeof A); + } + expect: { + var a; + A = 42; + a?.[42]; + console.log(typeof A); + } + expect_stdout: "number" + node_version: ">=14" +} + +collapse_vars_2: { + options = { + collapse_vars: true, + } + input: { + var a; + A = 42; + a?.(42); + console.log(typeof A); + } + expect: { + var a; + A = 42; + a?.(42); + console.log(typeof A); + } + expect_stdout: "number" + node_version: ">=14" +} + +properties: { + options = { + evaluate: true, + properties: true, + } + input: { + var a; + console.log(a?.["FAIL"]); + } + expect: { + var a; + console.log(a?.FAIL); + } + expect_stdout: "undefined" + node_version: ">=14" +} + +reduce_vars_1: { + options = { + evaluate: true, + reduce_vars: true, + toplevel: true, + } + input: { + var a = 1; + null?.[a = 0]; + console.log(a ? "PASS" : "FAIL"); + } + expect: { + var a = 1; + null?.[a = 0]; + console.log(a ? "PASS" : "FAIL"); + } + expect_stdout: "PASS" + node_version: ">=14" +} + +reduce_vars_2: { + options = { + evaluate: true, + reduce_vars: true, + toplevel: true, + } + input: { + var a = 1; + null?.(a = 0); + console.log(a ? "PASS" : "FAIL"); + } + expect: { + var a = 1; + null?.(a = 0); + console.log(a ? "PASS" : "FAIL"); + } + expect_stdout: "PASS" + node_version: ">=14" +} + +side_effects: { + options = { + side_effects: true, + } + input: { + var a; + a?.[a = "FAIL"]; + console.log(a); + } + expect: { + var a; + a?.[a = "FAIL"]; + console.log(a); + } + expect_stdout: "undefined" + node_version: ">=14" +} + +trim_1: { + options = { + evaluate: true, + optional_chains: true, + reduce_vars: true, + unsafe: true, + } + input: { + (function(a, b) { + console?.log?.(a?.p, b?.[console.log("FAIL")]); + })?.({ p: "PASS" }); + } + expect: { + (function(a, b) { + console?.log?.(a.p, void 0); + })({ p: "PASS" }); + } + expect_stdout: "PASS undefined" + node_version: ">=14" +} + +trim_2: { + options = { + evaluate: true, + optional_chains: true, + side_effects: true, + } + input: { + (void console.log("PASS"))?.[console.log("FAIL")]; + } + expect: { + console.log("PASS"); + } + expect_stdout: "PASS" + node_version: ">=14" +} + +trim_dot_call_1: { + options = { + evaluate: true, + optional_chains: true, + } + input: { + console.log(null?.f()); + } + expect: { + console.log(void 0); + } + expect_stdout: "undefined" + node_version: ">=14" +} + +trim_dot_call_2: { + options = { + evaluate: true, + optional_chains: true, + unsafe: true, + } + input: { + try { + (null?.p)(); + } catch (e) { + console.log("PASS"); + } + } + expect: { + try { + (void 0)(); + } catch (e) { + console.log("PASS"); + } + } + expect_stdout: "PASS" + node_version: ">=14" +} + +trim_dot_call_3: { + options = { + evaluate: true, + optional_chains: true, + unsafe: true, + } + input: { + try { + ({ p: null })?.p(); + } catch (e) { + console.log("PASS"); + } + } + expect: { + try { + null(); + } catch (e) { + console.log("PASS"); + } + } + expect_stdout: "PASS" + node_version: ">=14" +} + +trim_dot_sub: { + options = { + evaluate: true, + optional_chains: true, + } + input: { + console.log(null?.p[42]); + } + expect: { + console.log(void 0); + } + expect_stdout: "undefined" + node_version: ">=14" +} + +trim_sub_call_call: { + options = { + evaluate: true, + optional_chains: true, + } + input: { + console.log(null?.[42]()()); + } + expect: { + console.log(void 0); + } + expect_stdout: "undefined" + node_version: ">=14" +} + +issue_4906: { + options = { + toplevel: true, + unused: true, + } + input: { + do { + var a = a?.[42]; + } while (console.log("PASS")); + } + expect: { + do { + var a = a?.[42]; + } while (console.log("PASS")); + } + expect_stdout: "PASS" + node_version: ">=14" +} + +issue_4928: { + options = { + ie: true, + toplevel: true, + unused: true, + } + input: { + var a = a?.[function f() { + f(a); + }]; + console.log(typeof f); + } + expect: { + var a = a?.[function f() { + f(a); + }]; + console.log(typeof f); + } + expect_stdout: "undefined" + node_version: ">=14" +} + +issue_4947_1: { + options = { + conditionals: true, + } + input: { + console.log(console.foo ? 42..p : console.bar?.p); + } + expect: { + console.log(console.foo ? 42..p : console.bar?.p); + } + expect_stdout: "undefined" + node_version: ">=14" +} + +issue_4947_2: { + options = { + conditionals: true, + } + input: { + var log = console.log, fail; + log("PASS") ? log(42) : fail?.(42); + } + expect: { + var log = console.log, fail; + log("PASS") ? log(42) : fail?.(42); + } + expect_stdout: "PASS" + node_version: ">=14" +} + +issue_5039: { + options = { + ie: true, + side_effects: true, + toplevel: true, + unused: true, + } + input: { + var a = a?.[function f() { + f; + a; + }]; + console.log("PASS"); + } + expect: { + var a = a?.[function f() {}]; + console.log("PASS"); + } + expect_stdout: "PASS" + node_version: ">=14" +} + +issue_5091: { + options = { + merge_vars: true, + } + input: { + function f(a) { + var b = a.p; + var c; + b?.[c = "FAIL 2"]; + return b || c; + } + console.log(f("FAIL 1") || "PASS"); + } + expect: { + function f(a) { + var a = a.p; + var c; + a?.[c = "FAIL 2"]; + return a || c; + } + console.log(f("FAIL 1") || "PASS"); + } + expect_stdout: "PASS" + node_version: ">=14" +} + +issue_5292_dot: { + options = { + side_effects: true, + } + input: { + var o = { + get p() { + console.log("PASS"); + } + }; + o?.p; + } + expect: { + var o = { + get p() { + console.log("PASS"); + } + }; + o?.p; + } + expect_stdout: "PASS" + node_version: ">=14" +} + +issue_5292_dot_pure_getters: { + options = { + pure_getters: true, + side_effects: true, + } + input: { + var o = { + get p() { + console.log("PASS"); + } + }; + o?.p; + } + expect: { + var o = { + get p() { + console.log("PASS"); + } + }; + } +} + +issue_5292_dot_pure_getters_strict: { + options = { + pure_getters: "strict", + side_effects: true, + } + input: { + var o = { + get p() { + console.log("PASS"); + } + }; + o?.p; + } + expect: { + var o = { + get p() { + console.log("PASS"); + } + }; + o?.p; + } + expect_stdout: "PASS" + node_version: ">=14" +} + +issue_5292_sub: { + options = { + side_effects: true, + } + input: { + var o = { + get p() { + console.log("foo"); + } + }; + o?.[console.log("bar"), "p"]; + } + expect: { + var o = { + get p() { + console.log("foo"); + } + }; + o?.[console.log("bar"), "p"]; + } + expect_stdout: [ + "bar", + "foo", + ] + node_version: ">=14" +} + +issue_5292_sub_pure_getters: { + options = { + pure_getters: true, + side_effects: true, + } + input: { + var o = { + get p() { + console.log("foo"); + } + }; + o?.[console.log("bar"), "p"]; + } + expect: { + var o = { + get p() { + console.log("foo"); + } + }; + console.log("bar"); + } +} + +issue_5292_sub_pure_getters_strict: { + options = { + pure_getters: "strict", + side_effects: true, + } + input: { + var o = { + get p() { + console.log("foo"); + } + }; + o?.[console.log("bar"), "p"]; + } + expect: { + var o = { + get p() { + console.log("foo"); + } + }; + o?.[console.log("bar"), "p"]; + } + expect_stdout: [ + "bar", + "foo", + ] + node_version: ">=14" +} diff --git a/test/compress/preserve_line.js b/test/compress/preserve_line.js new file mode 100644 index 00000000000..7c859ec0d13 --- /dev/null +++ b/test/compress/preserve_line.js @@ -0,0 +1,181 @@ +return_1: { + beautify = { + beautify: false, + preserve_line: true, + } + input: { + console.log(function f() { + return ( + f.toString() != 42 + ); + }()); + } + expect_exact: [ + "console.log(function f(){", + "", + "return 42!=f.toString()}());", + ] + expect_stdout: "true" +} + +return_2: { + beautify = { + beautify: true, + preserve_line: true, + } + input: { + console.log(function f() { + return ( + f.toString() != 42 + ); + }()); + } + expect_exact: [ + "console.log(function f() {", + "", + " return 42 != f.toString();", + "}());", + ] + expect_stdout: "true" +} + +return_3: { + options = {} + beautify = { + beautify: false, + preserve_line: true, + } + input: { + console.log(function f() { + return ( + f.toString() != 42 + ); + }()); + } + expect_exact: [ + "console.log(function f(){", + "", + "return 42!=f.toString()}());", + ] + expect_stdout: "true" +} + +return_4: { + options = {} + beautify = { + beautify: true, + preserve_line: true, + } + input: { + console.log(function f() { + return ( + f.toString() != 42 + ); + }()); + } + expect_exact: [ + "console.log(function f() {", + "", + " return 42 != f.toString();", + "}());", + ] + expect_stdout: "true" +} + +return_5: { + beautify = { + beautify: false, + preserve_line: true, + } + input: { + _is_selected = function(tags, slug) { + var ref; + return (ref = _.find(tags, { + slug: slug + })) != null ? ref.selected : void 0; + }; + } + expect_exact: [ + "_is_selected=function(tags,slug){", + "var ref;", + "", + "", + "return null!=(ref=_.find(tags,{slug:slug}))?ref.selected:void 0};", + ] +} + +return_6: { + beautify = { + beautify: true, + preserve_line: true, + } + input: { + _is_selected = function(tags, slug) { + var ref; + return (ref = _.find(tags, { + slug: slug + })) != null ? ref.selected : void 0; + }; + } + expect_exact: [ + "_is_selected = function(tags, slug) {", + " var ref;", + "", + "", + " return null != (ref = _.find(tags, {", + " slug: slug", + " })) ? ref.selected : void 0;", + "};", + ] +} + +return_7: { + options = {} + mangle = {} + beautify = { + beautify: false, + preserve_line: true, + } + input: { + _is_selected = function(tags, slug) { + var ref; + return (ref = _.find(tags, { + slug: slug + })) != null ? ref.selected : void 0; + }; + } + expect_exact: [ + "_is_selected=function(e,l){", + "var n;", + "", + "", + "return null!=(n=_.find(e,{slug:l}))?n.selected:void 0};", + ] +} + +return_8: { + options = {} + mangle = {} + beautify = { + beautify: true, + preserve_line: true, + } + input: { + _is_selected = function(tags, slug) { + var ref; + return (ref = _.find(tags, { + slug: slug + })) != null ? ref.selected : void 0; + }; + } + expect_exact: [ + "_is_selected = function(e, l) {", + " var n;", + "", + "", + " return null != (n = _.find(e, {", + " slug: l", + " })) ? n.selected : void 0;", + "};", + ] +} diff --git a/test/compress/properties.js b/test/compress/properties.js index 39470738462..cb4f3f6ed31 100644 --- a/test/compress/properties.js +++ b/test/compress/properties.js @@ -1,7 +1,8 @@ keep_properties: { options = { - properties: false - }; + evaluate: true, + properties: false, + } input: { a["foo"] = "bar"; } @@ -12,8 +13,12 @@ keep_properties: { dot_properties: { options = { - properties: true - }; + evaluate: true, + properties: true, + } + beautify = { + ie: true, + } input: { a["foo"] = "bar"; a["if"] = "if"; @@ -34,9 +39,12 @@ dot_properties: { dot_properties_es5: { options = { + evaluate: true, properties: true, - screw_ie8: true - }; + } + beautify = { + ie: false, + } input: { a["foo"] = "bar"; a["if"] = "if"; @@ -53,12 +61,61 @@ dot_properties_es5: { } } -evaluate_length: { +sub_properties: { + options = { + evaluate: true, + properties: true, + } + input: { + a[0] = 0; + a["0"] = 1; + a[3.14] = 2; + a["3" + ".14"] = 3; + a["i" + "f"] = 4; + a["foo" + " bar"] = 5; + a[0 / 0] = 6; + a[null] = 7; + a[undefined] = 8; + } + expect: { + a[0] = 0; + a[0] = 1; + a[3.14] = 2; + a[3.14] = 3; + a.if = 4; + a["foo bar"] = 5; + a.NaN = 6; + a.null = 7; + a[void 0] = 8; + } +} + +evaluate_array_length: { + options = { + evaluate: true, + properties: true, + unsafe: true, + } + input: { + a = [1, 2, 3].length; + a = [1, 2, 3].join()["len" + "gth"]; + a = [1, 2, b].length; + a = [1, 2, 3].join(b).length; + } + expect: { + a = 3; + a = 5; + a = [1, 2, b].length; + a = [1, 2, 3].join(b).length; + } +} + +evaluate_string_length: { options = { + evaluate: true, properties: true, unsafe: true, - evaluate: true - }; + } input: { a = "foo".length; a = ("foo" + "bar")["len" + "gth"]; @@ -72,3 +129,1701 @@ evaluate_length: { a = ("foo" + b).length; } } + +mangle_properties_1: { + mangle = { + properties: { + domprops: true, + keep_quoted: false, + }, + } + input: { + a["foo"] = "bar"; + a.color = "red"; + x = {"bar": 10}; + a.run(x.bar, a.foo); + a['run']({color: "blue", foo: "baz"}); + } + expect: { + a["a"] = "bar"; + a.b = "red"; + x = {o: 10}; + a.run(x.o, a.a); + a['run']({b: "blue", a: "baz"}); + } +} + +mangle_properties_2: { + mangle = { + properties: { + domprops: true, + reserved: [ + "value", + ], + }, + } + input: { + var o = { + prop1: 1, + }; + Object.defineProperty(o, "prop2", { + value: 2, + }); + Object.defineProperties(o, { + prop3: { + value: 3, + }, + }); + console.log("prop1", o.prop1, "prop1" in o); + console.log("prop2", o.prop2, o.hasOwnProperty("prop2")); + console.log("prop3", o.prop3, Object.getOwnPropertyDescriptor(o, "prop3").value); + } + expect: { + var o = { + o: 1, + }; + Object.defineProperty(o, "p", { + value: 2, + }); + Object.defineProperties(o, { + r: { + value: 3, + }, + }); + console.log("prop1", o.o, "o" in o); + console.log("prop2", o.p, o.hasOwnProperty("p")); + console.log("prop3", o.r, Object.getOwnPropertyDescriptor(o, "r").value); + } + expect_stdout: [ + "prop1 1 true", + "prop2 2 true", + "prop3 3 3", + ] +} + +mangle_properties_3: { + mangle = { + properties: true, + } + input: { + console.log({ + [(console, "foo")]: "PASS", + }.foo); + } + expect: { + console.log({ + [(console, "o")]: "PASS", + }.o); + } + expect_stdout: "PASS" + node_version: ">=4" +} + +mangle_unquoted_properties: { + options = { + evaluate: true, + properties: false, + } + mangle = { + properties: { + builtins: true, + domprops: true, + keep_quoted: true, + }, + } + beautify = { + beautify: false, + quote_style: 3, + keep_quoted_props: true, + } + input: { + a.top = 1; + function f1() { + a["foo"] = "bar"; + a.color = "red"; + a.stuff = 2; + x = {"bar": 10, size: 7}; + a.size = 9; + } + function f2() { + a.foo = "bar"; + a['color'] = "red"; + x = {bar: 10, size: 7}; + a.size = 9; + a.stuff = 3; + } + } + expect: { + a.a = 1; + function f1() { + a["foo"] = "bar"; + a.color = "red"; + a.r = 2; + x = {"bar": 10, b: 7}; + a.b = 9; + } + function f2() { + a.foo = "bar"; + a['color'] = "red"; + x = {bar: 10, b: 7}; + a.b = 9; + a.r = 3; + } + } +} + +mangle_debug: { + mangle = { + properties: { + debug: "", + }, + } + input: { + a.foo = "bar"; + x = { baz: "ban" }; + } + expect: { + a._$foo$_ = "bar"; + x = { _$baz$_: "ban" }; + } +} + +mangle_debug_true: { + mangle = { + properties: { + debug: true, + }, + } + input: { + a.foo = "bar"; + x = { baz: "ban" }; + } + expect: { + a._$foo$_ = "bar"; + x = { _$baz$_: "ban" }; + } +} + +mangle_debug_suffix: { + mangle = { + properties: { + debug: "XYZ", + }, + } + input: { + a.foo = "bar"; + x = { baz: "ban" }; + } + expect: { + a._$foo$XYZ_ = "bar"; + x = { _$baz$XYZ_: "ban" }; + } +} + +mangle_debug_suffix_keep_quoted: { + options = { + evaluate: true, + properties: false, + } + mangle = { + properties: { + builtins: true, + debug: "XYZ", + domprops: true, + keep_quoted: true, + reserved: [], + }, + } + beautify = { + beautify: false, + quote_style: 3, + keep_quoted_props: true, + } + input: { + a.top = 1; + function f1() { + a["foo"] = "bar"; + a.color = "red"; + a.stuff = 2; + x = {"bar": 10, size: 7}; + a.size = 9; + } + function f2() { + a.foo = "bar"; + a['color'] = "red"; + x = {bar: 10, size: 7}; + a.size = 9; + a.stuff = 3; + } + } + expect: { + a._$top$XYZ_ = 1; + function f1() { + a["foo"] = "bar"; + a.color = "red"; + a._$stuff$XYZ_ = 2; + x = {"bar": 10, _$size$XYZ_: 7}; + a._$size$XYZ_ = 9; + } + function f2() { + a.foo = "bar"; + a['color'] = "red"; + x = {bar: 10, _$size$XYZ_: 7}; + a._$size$XYZ_ = 9; + a._$stuff$XYZ_ = 3; + } + } +} + +keep_substituted_property: { + options = { + evaluate: true, + properties: true, + reduce_vars: true, + } + mangle = { + properties: { + keep_quoted: true, + }, + } + input: { + var o = { p: [] }; + function f(b) { + return o[b]; + } + function g() { + var a = "p"; + return o[a] === f(a); + } + console.log(g() ? "PASS" : "FAIL"); + } + expect: { + var o = { p: [] }; + function f(n) { + return o[n]; + } + function g() { + var n = "p"; + return o.p === f(n); + } + console.log(g() ? "PASS" : "FAIL"); + } + expect_stdout: "PASS" +} + +keep_substituted_property_quotes: { + options = { + evaluate: true, + properties: true, + reduce_vars: true, + } + beautify = { + keep_quoted_props: true, + } + input: { + function f(o) { + var a = "p"; + return o[a]; + } + console.log(f({ p: "PASS" })); + } + expect: { + function f(o) { + var a = "p"; + return o["p"]; + } + console.log(f({ p: "PASS" })); + } + expect_stdout: "PASS" +} + +first_256_chars_as_properties: { + beautify = { + ascii_only: true, + } + input: { + // Note: some of these unicode character keys are not visible on github.com + var o = { + "\0":0,"":1,"":2,"":3,"":4,"":5,"":6,"":7,"\b":8, + "\t":9,"\n":10,"\v":11,"\f":12,"\r":13,"":14,"":15,"":16,"":17, + "":18,"":19,"":20,"":21,"":22,"":23,"":24,"":25,"":26, + "":27,"":28,"":29,"":30,"":31," ":32,"!":33,'"':34,"#":35, + $:36,"%":37,"&":38,"'":39,"(":40,")":41,"*":42,"+":43,",":44, + "-":45,".":46,"/":47,"0":48,"1":49,"2":50,"3":51,"4":52,"5":53,"6":54,"7":55, + "8":56,"9":57,":":58,";":59,"<":60,"=":61,">":62,"?":63,"@":64,A:65, + B:66,C:67,D:68,E:69,F:70,G:71,H:72,I:73,J:74,K:75,L:76,M:77,N:78, + O:79,P:80,Q:81,R:82,S:83,T:84,U:85,V:86,W:87,X:88,Y:89,Z:90,"[":91, + "\\":92,"]":93,"^":94,_:95,"`":96,a:97,b:98,c:99,d:100,e:101, + f:102,g:103,h:104,i:105,j:106,k:107,l:108,m:109,n:110,o:111,p:112, + q:113,r:114,s:115,t:116,u:117,v:118,w:119,x:120,y:121,z:122,"{":123, + "|":124,"}":125,"~":126,"":127,"€":128,"":129,"‚":130,"ƒ":131, + "„":132,"…":133,"†":134,"‡":135,"ˆ":136,"‰":137,"Š":138,"‹":139, + "Œ":140,"":141,"Ž":142,"":143,"":144,"‘":145,"’":146,"“":147, + "”":148,"•":149,"–":150,"—":151,"˜":152,"™":153,"š":154,"›":155, + "œ":156,"":157,"ž":158,"Ÿ":159," ":160,"¡":161,"¢":162,"£":163, + "¤":164,"¥":165,"¦":166,"§":167,"¨":168,"©":169,"ª":170,"«":171, + "¬":172,"­":173,"®":174,"¯":175,"°":176,"±":177,"²":178,"³":179, + "´":180,"µ":181,"¶":182,"·":183,"¸":184,"¹":185,"º":186,"»":187, + "¼":188,"½":189,"¾":190,"¿":191,"À":192,"Á":193,"Â":194,"Ã":195, + "Ä":196,"Å":197,"Æ":198,"Ç":199,"È":200,"É":201,"Ê":202,"Ë":203, + "Ì":204,"Í":205,"Î":206,"Ï":207,"Ð":208,"Ñ":209,"Ò":210,"Ó":211, + "Ô":212,"Õ":213,"Ö":214,"×":215,"Ø":216,"Ù":217,"Ú":218,"Û":219, + "Ü":220,"Ý":221,"Þ":222,"ß":223,"à":224,"á":225,"â":226,"ã":227, + "ä":228,"å":229,"æ":230,"ç":231,"è":232,"é":233,"ê":234,"ë":235, + "ì":236,"í":237,"î":238,"ï":239,"ð":240,"ñ":241,"ò":242,"ó":243, + "ô":244,"õ":245,"ö":246,"÷":247,"ø":248,"ù":249,"ú":250,"û":251, + "ü":252,"ý":253,"þ":254,"ÿ":255 + }; + } + expect: { + var o = { + "\0":0,"\x01":1,"\x02":2,"\x03":3,"\x04":4,"\x05":5,"\x06":6, + "\x07":7,"\b":8,"\t":9,"\n":10,"\v":11,"\f":12,"\r":13,"\x0e":14, + "\x0f":15,"\x10":16,"\x11":17,"\x12":18,"\x13":19,"\x14":20,"\x15":21, + "\x16":22,"\x17":23,"\x18":24,"\x19":25,"\x1a":26,"\x1b":27,"\x1c":28, + "\x1d":29,"\x1e":30,"\x1f":31," ":32,"!":33,'"':34,"#":35,$:36, + "%":37,"&":38,"'":39,"(":40,")":41,"*":42,"+":43,",":44,"-":45, + ".":46,"/":47,0:48,1:49,2:50,3:51,4:52,5:53,6:54,7:55,8:56,9:57, + ":":58,";":59,"<":60,"=":61,">":62,"?":63,"@":64,A:65,B:66,C:67, + D:68,E:69,F:70,G:71,H:72,I:73,J:74,K:75,L:76,M:77,N:78,O:79,P:80, + Q:81,R:82,S:83,T:84,U:85,V:86,W:87,X:88,Y:89,Z:90,"[":91,"\\":92, + "]":93,"^":94,_:95,"`":96,a:97,b:98,c:99,d:100,e:101,f:102,g:103, + h:104,i:105,j:106,k:107,l:108,m:109,n:110,o:111,p:112,q:113,r:114, + s:115,t:116,u:117,v:118,w:119,x:120,y:121,z:122,"{":123,"|":124, + "}":125,"~":126,"\x7f":127,"\x80":128,"\x81":129,"\x82":130,"\x83":131, + "\x84":132,"\x85":133,"\x86":134,"\x87":135,"\x88":136,"\x89":137, + "\x8a":138,"\x8b":139,"\x8c":140,"\x8d":141,"\x8e":142,"\x8f":143, + "\x90":144,"\x91":145,"\x92":146,"\x93":147,"\x94":148,"\x95":149, + "\x96":150,"\x97":151,"\x98":152,"\x99":153,"\x9a":154,"\x9b":155, + "\x9c":156,"\x9d":157,"\x9e":158,"\x9f":159,"\xa0":160,"\xa1":161, + "\xa2":162,"\xa3":163,"\xa4":164,"\xa5":165,"\xa6":166,"\xa7":167, + "\xa8":168,"\xa9":169,"\xaa":170,"\xab":171,"\xac":172,"\xad":173, + "\xae":174,"\xaf":175,"\xb0":176,"\xb1":177,"\xb2":178,"\xb3":179, + "\xb4":180,"\xb5":181,"\xb6":182,"\xb7":183,"\xb8":184,"\xb9":185, + "\xba":186,"\xbb":187,"\xbc":188,"\xbd":189,"\xbe":190,"\xbf":191, + "\xc0":192,"\xc1":193,"\xc2":194,"\xc3":195,"\xc4":196,"\xc5":197, + "\xc6":198,"\xc7":199,"\xc8":200,"\xc9":201,"\xca":202,"\xcb":203, + "\xcc":204,"\xcd":205,"\xce":206,"\xcf":207,"\xd0":208,"\xd1":209, + "\xd2":210,"\xd3":211,"\xd4":212,"\xd5":213,"\xd6":214,"\xd7":215, + "\xd8":216,"\xd9":217,"\xda":218,"\xdb":219,"\xdc":220,"\xdd":221, + "\xde":222,"\xdf":223,"\xe0":224,"\xe1":225,"\xe2":226,"\xe3":227, + "\xe4":228,"\xe5":229,"\xe6":230,"\xe7":231,"\xe8":232,"\xe9":233, + "\xea":234,"\xeb":235,"\xec":236,"\xed":237,"\xee":238,"\xef":239, + "\xf0":240,"\xf1":241,"\xf2":242,"\xf3":243,"\xf4":244,"\xf5":245, + "\xf6":246,"\xf7":247,"\xf8":248,"\xf9":249,"\xfa":250,"\xfb":251, + "\xfc":252,"\xfd":253,"\xfe":254,"\xff":255 + }; + } +} + +first_256_unicode_chars_as_properties: { + input: { + var o = { + "\u0000": 0, "\u0001": 1, "\u0002": 2, "\u0003": 3, "\u0004": 4, "\u0005": 5, + "\u0006": 6, "\u0007": 7, "\u0008": 8, "\u0009": 9, "\u000A": 10, "\u000B": 11, + "\u000C": 12, "\u000D": 13, "\u000E": 14, "\u000F": 15, "\u0010": 16, "\u0011": 17, + "\u0012": 18, "\u0013": 19, "\u0014": 20, "\u0015": 21, "\u0016": 22, "\u0017": 23, + "\u0018": 24, "\u0019": 25, "\u001A": 26, "\u001B": 27, "\u001C": 28, "\u001D": 29, + "\u001E": 30, "\u001F": 31, "\u0020": 32, "\u0021": 33, "\u0022": 34, "\u0023": 35, + "\u0024": 36, "\u0025": 37, "\u0026": 38, "\u0027": 39, "\u0028": 40, "\u0029": 41, + "\u002A": 42, "\u002B": 43, "\u002C": 44, "\u002D": 45, "\u002E": 46, "\u002F": 47, + "\u0030": 48, "\u0031": 49, "\u0032": 50, "\u0033": 51, "\u0034": 52, "\u0035": 53, + "\u0036": 54, "\u0037": 55, "\u0038": 56, "\u0039": 57, "\u003A": 58, "\u003B": 59, + "\u003C": 60, "\u003D": 61, "\u003E": 62, "\u003F": 63, "\u0040": 64, "\u0041": 65, + "\u0042": 66, "\u0043": 67, "\u0044": 68, "\u0045": 69, "\u0046": 70, "\u0047": 71, + "\u0048": 72, "\u0049": 73, "\u004A": 74, "\u004B": 75, "\u004C": 76, "\u004D": 77, + "\u004E": 78, "\u004F": 79, "\u0050": 80, "\u0051": 81, "\u0052": 82, "\u0053": 83, + "\u0054": 84, "\u0055": 85, "\u0056": 86, "\u0057": 87, "\u0058": 88, "\u0059": 89, + "\u005A": 90, "\u005B": 91, "\u005C": 92, "\u005D": 93, "\u005E": 94, "\u005F": 95, + "\u0060": 96, "\u0061": 97, "\u0062": 98, "\u0063": 99, "\u0064": 100, "\u0065": 101, + "\u0066": 102, "\u0067": 103, "\u0068": 104, "\u0069": 105, "\u006A": 106, "\u006B": 107, + "\u006C": 108, "\u006D": 109, "\u006E": 110, "\u006F": 111, "\u0070": 112, "\u0071": 113, + "\u0072": 114, "\u0073": 115, "\u0074": 116, "\u0075": 117, "\u0076": 118, "\u0077": 119, + "\u0078": 120, "\u0079": 121, "\u007A": 122, "\u007B": 123, "\u007C": 124, "\u007D": 125, + "\u007E": 126, "\u007F": 127, "\u0080": 128, "\u0081": 129, "\u0082": 130, "\u0083": 131, + "\u0084": 132, "\u0085": 133, "\u0086": 134, "\u0087": 135, "\u0088": 136, "\u0089": 137, + "\u008A": 138, "\u008B": 139, "\u008C": 140, "\u008D": 141, "\u008E": 142, "\u008F": 143, + "\u0090": 144, "\u0091": 145, "\u0092": 146, "\u0093": 147, "\u0094": 148, "\u0095": 149, + "\u0096": 150, "\u0097": 151, "\u0098": 152, "\u0099": 153, "\u009A": 154, "\u009B": 155, + "\u009C": 156, "\u009D": 157, "\u009E": 158, "\u009F": 159, "\u00A0": 160, "\u00A1": 161, + "\u00A2": 162, "\u00A3": 163, "\u00A4": 164, "\u00A5": 165, "\u00A6": 166, "\u00A7": 167, + "\u00A8": 168, "\u00A9": 169, "\u00AA": 170, "\u00AB": 171, "\u00AC": 172, "\u00AD": 173, + "\u00AE": 174, "\u00AF": 175, "\u00B0": 176, "\u00B1": 177, "\u00B2": 178, "\u00B3": 179, + "\u00B4": 180, "\u00B5": 181, "\u00B6": 182, "\u00B7": 183, "\u00B8": 184, "\u00B9": 185, + "\u00BA": 186, "\u00BB": 187, "\u00BC": 188, "\u00BD": 189, "\u00BE": 190, "\u00BF": 191, + "\u00C0": 192, "\u00C1": 193, "\u00C2": 194, "\u00C3": 195, "\u00C4": 196, "\u00C5": 197, + "\u00C6": 198, "\u00C7": 199, "\u00C8": 200, "\u00C9": 201, "\u00CA": 202, "\u00CB": 203, + "\u00CC": 204, "\u00CD": 205, "\u00CE": 206, "\u00CF": 207, "\u00D0": 208, "\u00D1": 209, + "\u00D2": 210, "\u00D3": 211, "\u00D4": 212, "\u00D5": 213, "\u00D6": 214, "\u00D7": 215, + "\u00D8": 216, "\u00D9": 217, "\u00DA": 218, "\u00DB": 219, "\u00DC": 220, "\u00DD": 221, + "\u00DE": 222, "\u00DF": 223, "\u00E0": 224, "\u00E1": 225, "\u00E2": 226, "\u00E3": 227, + "\u00E4": 228, "\u00E5": 229, "\u00E6": 230, "\u00E7": 231, "\u00E8": 232, "\u00E9": 233, + "\u00EA": 234, "\u00EB": 235, "\u00EC": 236, "\u00ED": 237, "\u00EE": 238, "\u00EF": 239, + "\u00F0": 240, "\u00F1": 241, "\u00F2": 242, "\u00F3": 243, "\u00F4": 244, "\u00F5": 245, + "\u00F6": 246, "\u00F7": 247, "\u00F8": 248, "\u00F9": 249, "\u00FA": 250, "\u00FB": 251, + "\u00FC": 252, "\u00FD": 253, "\u00FE": 254, "\u00FF": 255 + }; + } + expect: { + var o = { + "\0":0,"\x01":1,"\x02":2,"\x03":3,"\x04":4,"\x05":5,"\x06":6, + "\x07":7,"\b":8,"\t":9,"\n":10,"\v":11,"\f":12,"\r":13,"\x0e":14, + "\x0f":15,"\x10":16,"\x11":17,"\x12":18,"\x13":19,"\x14":20,"\x15":21, + "\x16":22,"\x17":23,"\x18":24,"\x19":25,"\x1a":26,"\x1b":27,"\x1c":28, + "\x1d":29,"\x1e":30,"\x1f":31," ":32,"!":33,'"':34,"#":35,$:36, + "%":37,"&":38,"'":39,"(":40,")":41,"*":42,"+":43,",":44,"-":45, + ".":46,"/":47,0:48,1:49,2:50,3:51,4:52,5:53,6:54,7:55,8:56,9:57, + ":":58,";":59,"<":60,"=":61,">":62,"?":63,"@":64,A:65,B:66,C:67, + D:68,E:69,F:70,G:71,H:72,I:73,J:74,K:75,L:76,M:77,N:78,O:79,P:80, + Q:81,R:82,S:83,T:84,U:85,V:86,W:87,X:88,Y:89,Z:90,"[":91,"\\":92, + "]":93,"^":94,_:95,"`":96,a:97,b:98,c:99,d:100,e:101,f:102,g:103, + h:104,i:105,j:106,k:107,l:108,m:109,n:110,o:111,p:112,q:113,r:114, + s:115,t:116,u:117,v:118,w:119,x:120,y:121,z:122,"{":123,"|":124, + "}":125,"~":126,"\x7f":127,"\x80":128,"\x81":129,"\x82":130,"\x83":131, + "\x84":132,"\x85":133,"\x86":134,"\x87":135,"\x88":136,"\x89":137, + "\x8a":138,"\x8b":139,"\x8c":140,"\x8d":141,"\x8e":142,"\x8f":143, + "\x90":144,"\x91":145,"\x92":146,"\x93":147,"\x94":148,"\x95":149, + "\x96":150,"\x97":151,"\x98":152,"\x99":153,"\x9a":154,"\x9b":155, + "\x9c":156,"\x9d":157,"\x9e":158,"\x9f":159,"\xa0":160,"\xa1":161, + "\xa2":162,"\xa3":163,"\xa4":164,"\xa5":165,"\xa6":166,"\xa7":167, + "\xa8":168,"\xa9":169,"\xaa":170,"\xab":171,"\xac":172,"\xad":173, + "\xae":174,"\xaf":175,"\xb0":176,"\xb1":177,"\xb2":178,"\xb3":179, + "\xb4":180,"\xb5":181,"\xb6":182,"\xb7":183,"\xb8":184,"\xb9":185, + "\xba":186,"\xbb":187,"\xbc":188,"\xbd":189,"\xbe":190,"\xbf":191, + "\xc0":192,"\xc1":193,"\xc2":194,"\xc3":195,"\xc4":196,"\xc5":197, + "\xc6":198,"\xc7":199,"\xc8":200,"\xc9":201,"\xca":202,"\xcb":203, + "\xcc":204,"\xcd":205,"\xce":206,"\xcf":207,"\xd0":208,"\xd1":209, + "\xd2":210,"\xd3":211,"\xd4":212,"\xd5":213,"\xd6":214,"\xd7":215, + "\xd8":216,"\xd9":217,"\xda":218,"\xdb":219,"\xdc":220,"\xdd":221, + "\xde":222,"\xdf":223,"\xe0":224,"\xe1":225,"\xe2":226,"\xe3":227, + "\xe4":228,"\xe5":229,"\xe6":230,"\xe7":231,"\xe8":232,"\xe9":233, + "\xea":234,"\xeb":235,"\xec":236,"\xed":237,"\xee":238,"\xef":239, + "\xf0":240,"\xf1":241,"\xf2":242,"\xf3":243,"\xf4":244,"\xf5":245, + "\xf6":246,"\xf7":247,"\xf8":248,"\xf9":249,"\xfa":250,"\xfb":251, + "\xfc":252,"\xfd":253,"\xfe":254,"\xff":255 + }; + } +} + +first_256_hex_chars_as_properties: { + input: { + var o = { + "\x00": 0, "\x01": 1, "\x02": 2, "\x03": 3, "\x04": 4, "\x05": 5, + "\x06": 6, "\x07": 7, "\x08": 8, "\x09": 9, "\x0A": 10, "\x0B": 11, + "\x0C": 12, "\x0D": 13, "\x0E": 14, "\x0F": 15, "\x10": 16, "\x11": 17, + "\x12": 18, "\x13": 19, "\x14": 20, "\x15": 21, "\x16": 22, "\x17": 23, + "\x18": 24, "\x19": 25, "\x1A": 26, "\x1B": 27, "\x1C": 28, "\x1D": 29, + "\x1E": 30, "\x1F": 31, "\x20": 32, "\x21": 33, "\x22": 34, "\x23": 35, + "\x24": 36, "\x25": 37, "\x26": 38, "\x27": 39, "\x28": 40, "\x29": 41, + "\x2A": 42, "\x2B": 43, "\x2C": 44, "\x2D": 45, "\x2E": 46, "\x2F": 47, + "\x30": 48, "\x31": 49, "\x32": 50, "\x33": 51, "\x34": 52, "\x35": 53, + "\x36": 54, "\x37": 55, "\x38": 56, "\x39": 57, "\x3A": 58, "\x3B": 59, + "\x3C": 60, "\x3D": 61, "\x3E": 62, "\x3F": 63, "\x40": 64, "\x41": 65, + "\x42": 66, "\x43": 67, "\x44": 68, "\x45": 69, "\x46": 70, "\x47": 71, + "\x48": 72, "\x49": 73, "\x4A": 74, "\x4B": 75, "\x4C": 76, "\x4D": 77, + "\x4E": 78, "\x4F": 79, "\x50": 80, "\x51": 81, "\x52": 82, "\x53": 83, + "\x54": 84, "\x55": 85, "\x56": 86, "\x57": 87, "\x58": 88, "\x59": 89, + "\x5A": 90, "\x5B": 91, "\x5C": 92, "\x5D": 93, "\x5E": 94, "\x5F": 95, + "\x60": 96, "\x61": 97, "\x62": 98, "\x63": 99, "\x64": 100, "\x65": 101, + "\x66": 102, "\x67": 103, "\x68": 104, "\x69": 105, "\x6A": 106, "\x6B": 107, + "\x6C": 108, "\x6D": 109, "\x6E": 110, "\x6F": 111, "\x70": 112, "\x71": 113, + "\x72": 114, "\x73": 115, "\x74": 116, "\x75": 117, "\x76": 118, "\x77": 119, + "\x78": 120, "\x79": 121, "\x7A": 122, "\x7B": 123, "\x7C": 124, "\x7D": 125, + "\x7E": 126, "\x7F": 127, "\x80": 128, "\x81": 129, "\x82": 130, "\x83": 131, + "\x84": 132, "\x85": 133, "\x86": 134, "\x87": 135, "\x88": 136, "\x89": 137, + "\x8A": 138, "\x8B": 139, "\x8C": 140, "\x8D": 141, "\x8E": 142, "\x8F": 143, + "\x90": 144, "\x91": 145, "\x92": 146, "\x93": 147, "\x94": 148, "\x95": 149, + "\x96": 150, "\x97": 151, "\x98": 152, "\x99": 153, "\x9A": 154, "\x9B": 155, + "\x9C": 156, "\x9D": 157, "\x9E": 158, "\x9F": 159, "\xA0": 160, "\xA1": 161, + "\xA2": 162, "\xA3": 163, "\xA4": 164, "\xA5": 165, "\xA6": 166, "\xA7": 167, + "\xA8": 168, "\xA9": 169, "\xAA": 170, "\xAB": 171, "\xAC": 172, "\xAD": 173, + "\xAE": 174, "\xAF": 175, "\xB0": 176, "\xB1": 177, "\xB2": 178, "\xB3": 179, + "\xB4": 180, "\xB5": 181, "\xB6": 182, "\xB7": 183, "\xB8": 184, "\xB9": 185, + "\xBA": 186, "\xBB": 187, "\xBC": 188, "\xBD": 189, "\xBE": 190, "\xBF": 191, + "\xC0": 192, "\xC1": 193, "\xC2": 194, "\xC3": 195, "\xC4": 196, "\xC5": 197, + "\xC6": 198, "\xC7": 199, "\xC8": 200, "\xC9": 201, "\xCA": 202, "\xCB": 203, + "\xCC": 204, "\xCD": 205, "\xCE": 206, "\xCF": 207, "\xD0": 208, "\xD1": 209, + "\xD2": 210, "\xD3": 211, "\xD4": 212, "\xD5": 213, "\xD6": 214, "\xD7": 215, + "\xD8": 216, "\xD9": 217, "\xDA": 218, "\xDB": 219, "\xDC": 220, "\xDD": 221, + "\xDE": 222, "\xDF": 223, "\xE0": 224, "\xE1": 225, "\xE2": 226, "\xE3": 227, + "\xE4": 228, "\xE5": 229, "\xE6": 230, "\xE7": 231, "\xE8": 232, "\xE9": 233, + "\xEA": 234, "\xEB": 235, "\xEC": 236, "\xED": 237, "\xEE": 238, "\xEF": 239, + "\xF0": 240, "\xF1": 241, "\xF2": 242, "\xF3": 243, "\xF4": 244, "\xF5": 245, + "\xF6": 246, "\xF7": 247, "\xF8": 248, "\xF9": 249, "\xFA": 250, "\xFB": 251, + "\xFC": 252, "\xFD": 253, "\xFE": 254, "\xFF": 255 + }; + } + expect: { + var o = { + "\0":0,"\x01":1,"\x02":2,"\x03":3,"\x04":4,"\x05":5,"\x06":6, + "\x07":7,"\b":8,"\t":9,"\n":10,"\v":11,"\f":12,"\r":13,"\x0e":14, + "\x0f":15,"\x10":16,"\x11":17,"\x12":18,"\x13":19,"\x14":20,"\x15":21, + "\x16":22,"\x17":23,"\x18":24,"\x19":25,"\x1a":26,"\x1b":27,"\x1c":28, + "\x1d":29,"\x1e":30,"\x1f":31," ":32,"!":33,'"':34,"#":35,$:36, + "%":37,"&":38,"'":39,"(":40,")":41,"*":42,"+":43,",":44,"-":45, + ".":46,"/":47,0:48,1:49,2:50,3:51,4:52,5:53,6:54,7:55,8:56,9:57, + ":":58,";":59,"<":60,"=":61,">":62,"?":63,"@":64,A:65,B:66,C:67, + D:68,E:69,F:70,G:71,H:72,I:73,J:74,K:75,L:76,M:77,N:78,O:79,P:80, + Q:81,R:82,S:83,T:84,U:85,V:86,W:87,X:88,Y:89,Z:90,"[":91,"\\":92, + "]":93,"^":94,_:95,"`":96,a:97,b:98,c:99,d:100,e:101,f:102,g:103, + h:104,i:105,j:106,k:107,l:108,m:109,n:110,o:111,p:112,q:113,r:114, + s:115,t:116,u:117,v:118,w:119,x:120,y:121,z:122,"{":123,"|":124, + "}":125,"~":126,"\x7f":127,"\x80":128,"\x81":129,"\x82":130,"\x83":131, + "\x84":132,"\x85":133,"\x86":134,"\x87":135,"\x88":136,"\x89":137, + "\x8a":138,"\x8b":139,"\x8c":140,"\x8d":141,"\x8e":142,"\x8f":143, + "\x90":144,"\x91":145,"\x92":146,"\x93":147,"\x94":148,"\x95":149, + "\x96":150,"\x97":151,"\x98":152,"\x99":153,"\x9a":154,"\x9b":155, + "\x9c":156,"\x9d":157,"\x9e":158,"\x9f":159,"\xa0":160,"\xa1":161, + "\xa2":162,"\xa3":163,"\xa4":164,"\xa5":165,"\xa6":166,"\xa7":167, + "\xa8":168,"\xa9":169,"\xaa":170,"\xab":171,"\xac":172,"\xad":173, + "\xae":174,"\xaf":175,"\xb0":176,"\xb1":177,"\xb2":178,"\xb3":179, + "\xb4":180,"\xb5":181,"\xb6":182,"\xb7":183,"\xb8":184,"\xb9":185, + "\xba":186,"\xbb":187,"\xbc":188,"\xbd":189,"\xbe":190,"\xbf":191, + "\xc0":192,"\xc1":193,"\xc2":194,"\xc3":195,"\xc4":196,"\xc5":197, + "\xc6":198,"\xc7":199,"\xc8":200,"\xc9":201,"\xca":202,"\xcb":203, + "\xcc":204,"\xcd":205,"\xce":206,"\xcf":207,"\xd0":208,"\xd1":209, + "\xd2":210,"\xd3":211,"\xd4":212,"\xd5":213,"\xd6":214,"\xd7":215, + "\xd8":216,"\xd9":217,"\xda":218,"\xdb":219,"\xdc":220,"\xdd":221, + "\xde":222,"\xdf":223,"\xe0":224,"\xe1":225,"\xe2":226,"\xe3":227, + "\xe4":228,"\xe5":229,"\xe6":230,"\xe7":231,"\xe8":232,"\xe9":233, + "\xea":234,"\xeb":235,"\xec":236,"\xed":237,"\xee":238,"\xef":239, + "\xf0":240,"\xf1":241,"\xf2":242,"\xf3":243,"\xf4":244,"\xf5":245, + "\xf6":246,"\xf7":247,"\xf8":248,"\xf9":249,"\xfa":250,"\xfb":251, + "\xfc":252,"\xfd":253,"\xfe":254,"\xff":255 + }; + } +} + +native_prototype: { + options = { + unsafe_proto: true, + } + input: { + Array.prototype.splice.apply(a, [1, 2, b, c]); + Function.prototype.call.apply(console.log, console, [ "foo" ]); + Number.prototype.toFixed.call(Math.PI, 2); + Object.prototype.hasOwnProperty.call(d, "foo"); + RegExp.prototype.test.call(/foo/, "bar"); + String.prototype.indexOf.call(e, "bar"); + } + expect: { + [].splice.apply(a, [1, 2, b, c]); + (function() {}).call.apply(console.log, console, [ "foo" ]); + 0..toFixed.call(Math.PI, 2); + ({}).hasOwnProperty.call(d, "foo"); + /t/.test.call(/foo/, "bar"); + "".indexOf.call(e, "bar"); + } +} + +native_prototype_lhs: { + options = { + unsafe_proto: true, + } + input: { + console.log(function() { + Function.prototype.bar = "PASS"; + return function() {}; + }().bar); + } + expect: { + console.log(function() { + Function.prototype.bar = "PASS"; + return function() {}; + }().bar); + } + expect_stdout: "PASS" +} + +accessor_boolean: { + input: { + var a = 1; + var b = { + get true() { + return a; + }, + set false(c) { + a = c; + } + }; + console.log(b.true, b.false = 2, b.true); + } + expect_exact: 'var a=1;var b={get true(){return a},set false(c){a=c}};console.log(b.true,b.false=2,b.true);' + expect_stdout: "1 2 2" +} + +accessor_get_set: { + input: { + var a = 1; + var b = { + get set() { + return a; + }, + set get(c) { + a = c; + } + }; + console.log(b.set, b.get = 2, b.set); + } + expect_exact: 'var a=1;var b={get set(){return a},set get(c){a=c}};console.log(b.set,b.get=2,b.set);' + expect_stdout: "1 2 2" +} + +accessor_null_undefined: { + input: { + var a = 1; + var b = { + get null() { + return a; + }, + set undefined(c) { + a = c; + } + }; + console.log(b.null, b.undefined = 2, b.null); + } + expect_exact: 'var a=1;var b={get null(){return a},set undefined(c){a=c}};console.log(b.null,b.undefined=2,b.null);' + expect_stdout: "1 2 2" +} + +accessor_number: { + input: { + var a = 1; + var b = { + get 42() { + return a; + }, + set 42(c) { + a = c; + } + }; + console.log(b[42], b[42] = 2, b[42]); + } + expect_exact: 'var a=1;var b={get 42(){return a},set 42(c){a=c}};console.log(b[42],b[42]=2,b[42]);' + expect_stdout: "1 2 2" +} + +accessor_string: { + input: { + var a = 1; + var b = { + get "a-b"() { + return a; + }, + set "a-b"(c) { + a = c; + } + }; + console.log(b["a-b"], b["a-b"] = 2, b["a-b"]); + } + expect_exact: 'var a=1;var b={get"a-b"(){return a},set"a-b"(c){a=c}};console.log(b["a-b"],b["a-b"]=2,b["a-b"]);' + expect_stdout: "1 2 2" +} + +accessor_this: { + input: { + var a = 1; + var b = { + get this() { + return a; + }, + set this(c) { + a = c; + } + }; + console.log(b.this, b.this = 2, b.this); + } + expect_exact: 'var a=1;var b={get this(){return a},set this(c){a=c}};console.log(b.this,b.this=2,b.this);' + expect_stdout: "1 2 2" +} + +issue_2208_1: { + options = { + inline: true, + properties: true, + side_effects: true, + } + input: { + console.log({ + p: function() { + return 42; + } + }.p()); + } + expect: { + console.log(42); + } + expect_stdout: "42" +} + +issue_2208_2: { + options = { + inline: true, + properties: true, + side_effects: true, + } + input: { + console.log({ + a: 42, + p: function() { + return this.a; + } + }.p()); + } + expect: { + console.log({ + a: 42, + p: function() { + return this.a; + } + }.p()); + } + expect_stdout: "42" +} + +issue_2208_3: { + options = { + inline: true, + properties: true, + side_effects: true, + } + input: { + a = 42; + console.log({ + p: function() { + return function() { + return this.a; + }(); + } + }.p()); + } + expect: { + a = 42; + console.log(function() { + return this.a; + }()); + } + expect_stdout: "42" +} + +issue_2208_4: { + options = { + inline: true, + properties: true, + side_effects: true, + } + input: { + function foo() {} + console.log({ + a: foo(), + p: function() { + return 42; + } + }.p()); + } + expect: { + function foo() {} + console.log((foo(), function() { + return 42; + })()); + } + expect_stdout: "42" +} + +issue_2208_5: { + options = { + inline: true, + properties: true, + side_effects: true, + } + input: { + console.log({ + p: "FAIL", + p: function() { + return 42; + } + }.p()); + } + expect: { + console.log(42); + } + expect_stdout: "42" +} + +issue_2208_6: { + options = { + inline: true, + properties: true, + side_effects: true, + } + input: { + a = 42; + console.log(("FAIL", { + p: function() { + return this.a; + } + }.p)()); + } + expect: { + a = 42; + console.log(function() { + return this.a; + }()); + } + expect_stdout: "42" +} + +issue_2256: { + options = { + side_effects: true, + } + mangle = { + properties: { + keep_quoted: true, + }, + } + input: { + ({ "keep": 42 }); + global.keep = global.change = "PASS"; + console.log(keep); + } + expect: { + global.keep = global.l = "PASS"; + console.log(keep); + } + expect_stdout: "PASS" +} + +lhs_prop_1: { + options = { + evaluate: true, + properties: true, + } + input: { + console.log(++{ + a: 1 + }.a); + } + expect: { + console.log(++{ + a: 1 + }.a); + } + expect_stdout: "2" +} + +lhs_prop_2: { + options = { + evaluate: true, + inline: true, + properties: true, + reduce_funcs: true, + reduce_vars: true, + side_effects: true, + unused: true, + } + input: { + [1][0] = 42; + (function(a) { + a.b = "g"; + })("abc"); + (function(a) { + a[2] = "g"; + })("def"); + (function(a) { + a[""] = "g"; + })("ghi"); + } + expect: { + [1][0] = 42; + "abc".b = "g"; + "def"[2] = "g"; + "ghi"[""] = "g"; + } +} + +literal_duplicate_key_side_effects: { + options = { + properties: true, + side_effects: true, + } + input: { + console.log({ + a: "FAIL", + a: console.log ? "PASS" : "FAIL" + }.a); + } + expect: { + console.log(console.log ? "PASS" : "FAIL"); + } + expect_stdout: "PASS" +} + +prop_side_effects_1: { + options = { + evaluate: true, + inline: true, + properties: true, + reduce_funcs: true, + reduce_vars: true, + side_effects: true, + toplevel: true, + unused: true, + } + input: { + var C = 1; + console.log(C); + var obj = { + bar: function() { + return C + C; + } + }; + console.log(obj.bar()); + } + expect: { + console.log(1); + var obj = { + bar: function() { + return 2; + } + }; + console.log(obj.bar()); + } + expect_stdout: [ + "1", + "2", + ] +} + +prop_side_effects_2: { + options = { + evaluate: true, + inline: true, + passes: 2, + properties: true, + reduce_funcs: true, + reduce_vars: true, + side_effects: true, + toplevel: true, + unused: true, + } + input: { + var C = 1; + console.log(C); + var obj = { + "": function() { + return C + C; + } + }; + console.log(obj[""]()); + } + expect: { + console.log(1); + console.log(2); + } + expect_stdout: [ + "1", + "2", + ] +} + +accessor_1: { + options = { + properties: true, + } + input: { + console.log({ + a: "FAIL", + get a() { + return "PASS"; + } + }.a); + } + expect: { + console.log({ + a: "FAIL", + get a() { + return "PASS"; + } + }.a); + } + expect_stdout: "PASS" + node_version: ">=4" +} + +accessor_2: { + options = { + properties: true, + } + input: { + console.log({ + get a() { + return "PASS"; + }, + set a(v) {}, + a: "FAIL" + }.a); + } + expect: { + console.log({ + get a() { + return "PASS"; + }, + set a(v) {}, + a: "FAIL" + }.a); + } + expect_stdout: true +} + +array_hole: { + options = { + properties: true, + side_effects: true, + } + input: { + Array.prototype[2] = "PASS"; + console.log([ 1, 2, , 3 ][1]); + console.log([ 1, 2, , 3 ][2]); + console.log([ 1, 2, , 3 ][3]); + } + expect: { + Array.prototype[2] = "PASS"; + console.log(2); + console.log([ , , , ][2]); + console.log(3); + } + expect_stdout: [ + "2", + "PASS", + "3", + ] +} + +new_this: { + options = { + properties: true, + side_effects: true, + } + input: { + new { + f: function(a) { + this.a = a; + } + }.f(42); + } + expect: {} +} + +issue_2513: { + options = { + evaluate: true, + properties: true, + } + input: { + !function(Infinity, NaN, undefined) { + console.log("a"[1/0], "b"["Infinity"]); + console.log("c"[0/0], "d"["NaN"]); + console.log("e"[void 0], "f"["undefined"]); + }(0, 0, 0); + } + expect: { + !function(Infinity, NaN, undefined) { + console.log("a"[1/0], "b"[1/0]); + console.log("c".NaN, "d".NaN); + console.log("e"[void 0], "f"[void 0]); + }(0, 0, 0); + } + expect_stdout: [ + "undefined undefined", + "undefined undefined", + "undefined undefined", + ] +} + +const_prop_assign_strict: { + options = { + pure_getters: "strict", + side_effects: true, + } + input: { + function Simulator() { + /abc/.index = 1; + this._aircraft = []; + } + (function() {}).prototype.destroy = x(); + } + expect: { + function Simulator() { + this._aircraft = []; + } + x(); + } +} + +const_prop_assign_pure: { + options = { + pure_getters: true, + side_effects: true, + } + input: { + function Simulator() { + /abc/.index = 1; + this._aircraft = []; + } + (function() {}).prototype.destroy = x(); + } + expect: { + function Simulator() { + this._aircraft = []; + } + x(); + } +} + +issue_869_1: { + mangle = { + properties: { + reserved: [ "get" ] + }, + } + input: { + var o = { p: "FAIL" }; + Object.defineProperty(o, "p", { + get: function() { + return "PASS"; + } + }); + console.log(o.p); + } + expect: { + var o = { o: "FAIL" }; + Object.defineProperty(o, "o", { + get: function() { + return "PASS"; + } + }); + console.log(o.o); + } + expect_stdout: "PASS" +} + +issue_869_2: { + mangle = { + properties: { + reserved: [ "get" ] + }, + } + input: { + var o = { p: "FAIL" }; + Object.defineProperties(o, { + p: { + get: function() { + return "PASS"; + } + } + }); + console.log(o.p); + } + expect: { + var o = { o: "FAIL" }; + Object.defineProperties(o, { + o: { + get: function() { + return "PASS"; + } + } + }); + console.log(o.o); + } + expect_stdout: "PASS" +} + +issue_3188_1: { + options = { + collapse_vars: true, + inline: true, + properties: true, + reduce_vars: true, + side_effects: true, + } + input: { + (function() { + function f() { + console.log(this.p); + } + (function() { + var o = { + p: "PASS", + f: f + }; + o.f(); + })(); + })(); + } + expect: { + (function() { + function f() { + console.log(this.p); + } + ({ + p: "PASS", + f: f + }).f(); + var o; + })(); + } + expect_stdout: "PASS" +} + +issue_3188_2: { + options = { + collapse_vars: true, + inline: true, + properties: true, + reduce_vars: true, + side_effects: true, + unused: true, + } + input: { + (function() { + var f = function() { + console.log(this.p); + }; + function g() { + var o = { + p: "PASS", + f: f + }; + o.f(); + } + g(); + })(); + } + expect: { + ({ + p: "PASS", + f: function() { + console.log(this.p); + } + }).f(); + } + expect_stdout: "PASS" +} + +issue_3188_3: { + options = { + collapse_vars: true, + inline: true, + properties: true, + reduce_vars: true, + side_effects: true, + } + input: { + (function() { + function f() { + console.log(this[0]); + } + (function() { + var o = ["PASS", f]; + o[1](); + })(); + })(); + } + expect: { + (function() { + function f() { + console.log(this[0]); + } + ["PASS", f][1](); + var o; + })(); + } + expect_stdout: "PASS" +} + +issue_3389: { + options = { + evaluate: true, + properties: true, + reduce_vars: true, + unsafe: true, + } + input: { + (function() { + var a = "PASS"; + if (delete b) + b = a[null] = 42; + console.log(a); + })(); + } + expect: { + (function() { + var a = "PASS"; + if (delete b) + b = a.null = 42; + console.log(a); + })(); + } + expect_stdout: "PASS" +} + +object_super: { + options = { + properties: true, + } + input: { + ({ + f(a) { + return a ? console.log("PASS") : super.log("PASS"); + }, + }).f(console); + } + expect: { + ({ + f(a) { + return a ? console.log("PASS") : super.log("PASS"); + }, + }).f(console); + } + expect_stdout: "PASS" + node_version: ">=4" +} + +issue_4831_1: { + options = { + properties: true, + } + input: { + console.log({ + f() { + return arguments; + }, + }.f("PASS")[0]); + } + expect: { + console.log([ + function() { + return arguments; + }, + ][0]("PASS")[0]); + } + expect_stdout: "PASS" + node_version: ">=4" +} + +issue_4831_2: { + options = { + properties: true, + } + input: { + var f = { + f() { + return arguments; + }, + }.f; + console.log(f("PASS")[0]); + } + expect: { + var f = { + f() { + return arguments; + }, + }.f; + console.log(f("PASS")[0]); + } + expect_stdout: "PASS" + node_version: ">=4" +} + +issue_4888: { + options = { + properties: true, + } + input: { + console.log(typeof { + __proto__: 42, + }.__proto__); + } + expect: { + console.log(typeof { + __proto__: 42, + }.__proto__); + } + expect_stdout: "object" +} + +issue_5093: { + beautify = { + keep_quoted_props: true, + } + input: { + console.log({ + a: true, + '42': "PASS", + "null": [], + }[6 * 7]); + } + expect_exact: 'console.log({a:true,"42":"PASS","null":[]}[6*7]);' + expect_stdout: "PASS" +} + +issue_5093_quote_keys: { + beautify = { + keep_quoted_props: true, + quote_keys: true, + } + input: { + console.log({ + a: true, + '42': "PASS", + "null": [], + }[6 * 7]); + } + expect_exact: 'console.log({"a":true,"42":"PASS","null":[]}[6*7]);' + expect_stdout: "PASS" +} + +issue_5093_quote_style: { + beautify = { + keep_quoted_props: true, + quote_style: 3, + } + input: { + console.log({ + a: true, + '42': "PASS", + "null": [], + }[6 * 7]); + } + expect_exact: 'console.log({a:true,\'42\':"PASS","null":[]}[6*7]);' + expect_stdout: "PASS" +} + +object_methods: { + options = { + properties: true, + } + input: { + ({ + p() { + console.log("FAIL 1"); + }, + *q() { + console.log("FAIL 2"); + }, + async r() { + console.log("FAIL 3"); + }, + async *s() { + console.log("PASS"); + }, + }).s().next(); + } + expect: { + [ + () => { + console.log("FAIL 1"); + }, + function*() { + console.log("FAIL 2"); + }, + async () => { + console.log("FAIL 3"); + }, + async function*() { + console.log("PASS"); + }, + ][3]().next(); + } + expect_stdout: "PASS" + node_version: ">=10" +} + +issue_5177: { + options = { + properties: true, + } + input: { + var a = "FAIL"; + var o = { a: "PASS" }; + o.p = { + q() { + return this.a; + }, + }.q; + console.log(o.p()); + } + expect: { + var a = "FAIL"; + var o = { a: "PASS" }; + o.p = { + q() { + return this.a; + }, + }.q; + console.log(o.p()); + } + expect_stdout: "PASS" + node_version: ">=4" +} + +issue_5682_in_1: { + mangle = { + properties: true, + } + input: { + function f(a) { + return "foo" in a; + } + var o = {}; + var p = "foo"; + o[p] = 42; + console.log(f(o) ? "PASS" : "FAIL"); + } + expect: { + function f(o) { + return "foo" in o; + } + var o = {}; + var p = "foo"; + o[p] = 42; + console.log(f(o) ? "PASS" : "FAIL"); + } + expect_stdout: "PASS" +} + +issue_5682_in_2: { + mangle = { + properties: true, + } + input: { + function f(a) { + return "foo" in a; + } + var o = { foo: 42 }; + console.log(f(o) ? "PASS" : "FAIL"); + } + expect: { + function f(o) { + return "o" in o; + } + var o = { o: 42 }; + console.log(f(o) ? "PASS" : "FAIL"); + } + expect_stdout: "PASS" +} + +issue_5682_dot_1: { + mangle = { + properties: true, + } + input: { + function f(a) { + return a.foo; + } + var o = {}; + var p = "foo"; + o[p] = "PASS"; + console.log(f(o)); + } + expect: { + function f(o) { + return o.foo; + } + var o = {}; + var p = "foo"; + o[p] = "PASS"; + console.log(f(o)); + } + expect_stdout: "PASS" +} + +issue_5682_dot_2: { + mangle = { + properties: true, + } + input: { + function f(a) { + return a.foo; + } + var o = { foo: "PASS" }; + console.log(f(o)); + } + expect: { + function f(o) { + return o.o; + } + var o = { o: "PASS" }; + console.log(f(o)); + } + expect_stdout: "PASS" +} + +issue_5682_dot_2_computed: { + mangle = { + properties: true, + } + input: { + function f(a) { + return a.foo; + } + var o = { ["foo"]: "PASS" }; + console.log(f(o)); + } + expect: { + function f(o) { + return o.o; + } + var o = { ["o"]: "PASS" }; + console.log(f(o)); + } + expect_stdout: "PASS" + node_version: ">=4" +} + +issue_5682_sub_1: { + mangle = { + properties: true, + } + input: { + function f(a) { + return a["foo"]; + } + var o = {}; + var p = "foo"; + o[p] = "PASS"; + console.log(f(o)); + } + expect: { + function f(o) { + return o["foo"]; + } + var o = {}; + var p = "foo"; + o[p] = "PASS"; + console.log(f(o)); + } + expect_stdout: "PASS" +} + +issue_5682_sub_2: { + mangle = { + properties: true, + } + input: { + function f(a) { + return a["foo"]; + } + var o = { foo: "PASS" }; + console.log(f(o)); + } + expect: { + function f(o) { + return o["o"]; + } + var o = { o: "PASS" }; + console.log(f(o)); + } + expect_stdout: "PASS" +} diff --git a/test/compress/pure_funcs.js b/test/compress/pure_funcs.js new file mode 100644 index 00000000000..76f57cd9c4e --- /dev/null +++ b/test/compress/pure_funcs.js @@ -0,0 +1,440 @@ +array: { + options = { + pure_funcs: [ "Math.floor" ], + side_effects: true, + } + input: { + var a; + function f(b) { + Math.floor(a / b); + Math.floor(c / b); + } + } + expect: { + var a; + function f(b) { + c; + } + } +} + +func: { + options = { + pure_funcs: function(node) { + return !~node.args[0].print_to_string().indexOf("a"); + }, + side_effects: true, + } + input: { + function f(a, b) { + Math.floor(a / b); + Math.floor(c / b); + } + } + expect: { + function f(a, b) { + Math.floor(c / b); + } + } +} + +side_effects: { + options = { + pure_funcs: [ "console.log" ], + side_effects: true, + } + input: { + function f(a, b) { + console.log(a()); + console.log(b); + } + } + expect: { + function f(a, b) { + a(); + } + } +} + +unused: { + options = { + pure_funcs: [ "pure" ], + side_effects: true, + unused: true, + } + input: { + function foo() { + var u = pure(1); + var x = pure(2); + var y = pure(x); + var z = pure(pure(side_effects())); + return pure(3); + } + } + expect: { + function foo() { + side_effects(); + return pure(3); + } + } +} + +babel: { + options = { + pure_funcs: [ "_classCallCheck" ], + side_effects: true, + unused: true, + } + input: { + function _classCallCheck(instance, Constructor) { + if (!(instance instanceof Constructor)) + throw new TypeError("Cannot call a class as a function"); + } + var Foo = function Foo() { + _classCallCheck(this, Foo); + }; + } + expect: { + function _classCallCheck(instance, Constructor) { + if (!(instance instanceof Constructor)) + throw new TypeError("Cannot call a class as a function"); + } + var Foo = function() { + }; + } +} + +conditional: { + options = { + pure_funcs: [ "pure" ], + side_effects: true, + } + input: { + pure(1 | a() ? 2 & b() : 7 ^ c()); + pure(1 | a() ? 2 & b() : 5); + pure(1 | a() ? 4 : 7 ^ c()); + pure(1 | a() ? 4 : 5); + pure(3 ? 2 & b() : 7 ^ c()); + pure(3 ? 2 & b() : 5); + pure(3 ? 4 : 7 ^ c()); + pure(3 ? 4 : 5); + } + expect: { + 1 | a() ? b() : c(); + 1 | a() && b(); + 1 | a() || c(); + a(); + 3 ? b() : c(); + 3 && b(); + 3 || c(); + } +} + +relational: { + options = { + pure_funcs: [ "foo" ], + side_effects: true, + } + input: { + foo() in new foo(); + foo() instanceof bar(); + foo() < "bar"; + bar() > foo(); + bar() != bar(); + bar() !== "bar"; + "bar" == foo(); + "bar" === bar(); + "bar" >= "bar"; + } + expect: { + [] instanceof bar(); + bar(); + bar(), bar(); + bar(); + bar(); + } +} + +arithmetic: { + options = { + pure_funcs: [ "foo" ], + side_effects: true, + } + input: { + foo() + foo(); + foo() - bar(); + foo() * "bar"; + bar() / foo(); + bar() & bar(); + bar() | "bar"; + "bar" >> foo(); + "bar" << bar(); + "bar" >>> "bar"; + } + expect: { + bar(); + bar(); + bar(), bar(); + bar(); + bar(); + } +} + +boolean_and: { + options = { + pure_funcs: [ "foo" ], + side_effects: true, + } + input: { + foo() && foo(); + foo() && bar(); + foo() && "bar"; + bar() && foo(); + bar() && bar(); + bar() && "bar"; + "bar" && foo(); + "bar" && bar(); + "bar" && "bar"; + } + expect: { + foo() && bar(); + bar(); + bar() && bar(); + bar(); + "bar" && bar(); + } +} + +boolean_or: { + options = { + pure_funcs: [ "foo" ], + side_effects: true, + } + input: { + foo() || foo(); + foo() || bar(); + foo() || "bar"; + bar() || foo(); + bar() || bar(); + bar() || "bar"; + "bar" || foo(); + "bar" || bar(); + "bar" || "bar"; + } + expect: { + foo() || bar(); + bar(); + bar() || bar(); + bar(); + "bar" || bar(); + } +} + +assign: { + options = { + pure_funcs: [ "foo" ], + side_effects: true, + } + input: { + var a; + function f(b) { + a = foo(); + b *= 4 + foo(); + c >>= 0 | foo(); + } + } + expect: { + var a; + function f(b) { + a = foo(); + b *= 4 + foo(); + c >>= 0 | foo(); + } + } +} + +unary: { + options = { + pure_funcs: [ "foo" ], + side_effects: true, + } + input: { + typeof foo(); + typeof bar(); + typeof "bar"; + void foo(); + void bar(); + void "bar"; + delete a[foo()]; + delete a[bar()]; + delete a["bar"]; + a[foo()]++; + a[bar()]++; + a["bar"]++; + --a[foo()]; + --a[bar()]; + --a["bar"]; + ~foo(); + ~bar(); + ~"bar"; + } + expect: { + bar(); + bar(); + delete a[foo()]; + delete a[bar()]; + delete a["bar"]; + a[foo()]++; + a[bar()]++; + a["bar"]++; + --a[foo()]; + --a[bar()]; + --a["bar"]; + bar(); + } +} + +issue_3065_1: { + options = { + inline: true, + pure_funcs: [ "pureFunc" ], + reduce_vars: true, + side_effects: true, + toplevel: true, + unused: true, + } + input: { + function modifyWrapper(a, f, wrapper) { + wrapper.a = a; + wrapper.f = f; + return wrapper; + } + function pureFunc(fun) { + return modifyWrapper(1, fun, function(a) { + return fun(a); + }); + } + var unused = pureFunc(function(x) { + return x; + }); + } + expect: {} +} + +issue_3065_2: { + rename = true + options = { + inline: true, + pure_funcs: [ "pureFunc" ], + reduce_vars: true, + side_effects: true, + toplevel: true, + unused: true, + } + mangle = { + reserved: [ "pureFunc" ], + toplevel: true, + } + input: { + function modifyWrapper(a, f, wrapper) { + wrapper.a = a; + wrapper.f = f; + return wrapper; + } + function pureFunc(fun) { + return modifyWrapper(1, fun, function(a) { + return fun(a); + }); + } + var unused = pureFunc(function(x) { + return x; + }); + } + expect: {} +} + +issue_3065_3: { + options = { + pure_funcs: [ "debug" ], + reduce_vars: true, + side_effects: true, + toplevel: true, + unused: true, + } + input: { + function debug(msg) { + console.log(msg); + } + debug(function() { + console.log("PASS"); + return "FAIL"; + }()); + } + expect: { + (function() { + console.log("PASS"); + })(); + } +} + +issue_3065_4: { + options = { + pure_funcs: [ "debug" ], + reduce_vars: true, + side_effects: true, + toplevel: true, + unused: true, + } + input: { + var debug = function(msg) { + console.log(msg); + }; + debug(function() { + console.log("PASS"); + return "FAIL"; + }()); + } + expect: { + (function() { + console.log("PASS"); + })(); + } +} + +issue_3325_1: { + options = { + pure_funcs: "cb", + side_effects: true, + } + input: { + function cb() { + console.log("PASS"); + } + cb(); + } + expect: { + function cb() { + console.log("PASS"); + } + } +} + +issue_3325_2: { + options = { + pure_funcs: "xxxcbxxx", + side_effects: true, + } + input: { + function cb() { + console.log("PASS"); + } + cb(); + } + expect: { + function cb() { + console.log("PASS"); + } + cb(); + } + expect_stdout: "PASS" +} diff --git a/test/compress/pure_getters.js b/test/compress/pure_getters.js new file mode 100644 index 00000000000..a06570c98e7 --- /dev/null +++ b/test/compress/pure_getters.js @@ -0,0 +1,1689 @@ +strict: { + options = { + pure_getters: "strict", + reduce_funcs: false, + reduce_vars: false, + side_effects: true, + toplevel: true, + } + input: { + var a, b = null, c = {}; + a.prop; + b.prop; + c.prop; + d.prop; + null.prop; + (void 0).prop; + undefined.prop; + } + expect: { + var a, b = null, c = {}; + a.prop; + b.prop; + c.prop; + d.prop; + null.prop; + (void 0).prop; + (void 0).prop; + } +} + +strict_reduce_vars: { + options = { + pure_getters: "strict", + reduce_funcs: true, + reduce_vars: true, + side_effects: true, + toplevel: true, + } + input: { + var a, b = null, c = {}; + a.prop; + b.prop; + c.prop; + d.prop; + null.prop; + (void 0).prop; + undefined.prop; + } + expect: { + var a, b = null, c = {}; + a.prop; + b.prop; + d.prop; + null.prop; + (void 0).prop; + (void 0).prop; + } +} + +unsafe: { + options = { + pure_getters: true, + reduce_funcs: false, + reduce_vars: false, + side_effects: true, + toplevel: true, + } + input: { + var a, b = null, c = {}; + a.prop; + b.prop; + c.prop; + d.prop; + null.prop; + (void 0).prop; + undefined.prop; + } + expect: { + var a, b = null, c = {}; + d; + null.prop; + (void 0).prop; + (void 0).prop; + } +} + +unsafe_reduce_vars: { + options = { + pure_getters: true, + reduce_funcs: true, + reduce_vars: true, + side_effects: true, + toplevel: true, + } + input: { + var a, b = null, c = {}; + a.prop; + b.prop; + c.prop; + d.prop; + null.prop; + (void 0).prop; + undefined.prop; + } + expect: { + var a, b = null, c = {}; + d; + null.prop; + (void 0).prop; + (void 0).prop; + } +} + +chained: { + options = { + pure_getters: "strict", + side_effects: true, + } + input: { + a.b.c; + } + expect: { + a.b.c; + } +} + +impure_getter_1: { + options = { + pure_getters: "strict", + side_effects: true, + } + input: { + ({ + get a() { + console.log(1); + }, + b: 1 + }).a; + ({ + get a() { + console.log(1); + }, + b: 1 + }).b; + } + expect: { + ({ + get a() { + console.log(1); + }, + b: 1 + }).a; + ({ + get a() { + console.log(1); + }, + b: 1 + }).b; + } + expect_stdout: "1" +} + +impure_getter_2: { + options = { + pure_getters: true, + side_effects: true, + } + input: { + // will produce incorrect output because getter is not pure + ({ + get a() { + console.log(1); + }, + b: 1 + }).a; + ({ + get a() { + console.log(1); + }, + b: 1 + }).b; + } + expect: {} +} + +issue_2110_1: { + options = { + collapse_vars: true, + pure_getters: "strict", + reduce_funcs: true, + reduce_vars: true, + sequences: true, + side_effects: true, + unused: true, + } + input: { + function f() { + function f() {} + function g() { + return this; + } + f.g = g; + return f.g(); + } + console.log(typeof f()); + } + expect: { + function f() { + function f() {} + return f.g = function() { + return this; + }, f.g(); + } + console.log(typeof f()); + } + expect_stdout: "function" +} + +issue_2110_2: { + options = { + collapse_vars: true, + pure_getters: "strict", + reduce_funcs: true, + reduce_vars: true, + unused: true, + } + input: { + function f() { + function f() {} + function g() { + return this; + } + f.g = g; + return f.g(); + } + console.log(typeof f()); + } + expect: { + function f() { + function f() {} + f.g = function() { + return this; + }; + return f.g(); + } + console.log(typeof f()); + } + expect_stdout: "function" +} + +issue_2110_3: { + options = { + collapse_vars: true, + pure_getters: "strict", + reduce_vars: true, + } + input: { + function g() { + return this; + } + console.log(typeof function() { + function f() {} + f.g = g; + return f.g(); + }()); + } + expect: { + function g() { + return this; + } + console.log(typeof function() { + function f() {} + f.g = g; + return f.g(); + }()); + } + expect_stdout: "function" +} + +set_immutable_1: { + options = { + collapse_vars: true, + evaluate: true, + pure_getters: "strict", + reduce_funcs: true, + reduce_vars: true, + toplevel: true, + unused: true, + } + input: { + var a = 1; + a.foo += ""; + if (a.foo) console.log("FAIL"); + else console.log("PASS"); + } + expect: { + 1..foo += ""; + if (1..foo) console.log("FAIL"); + else console.log("PASS"); + } + expect_stdout: "PASS" +} + +set_immutable_2: { + options = { + collapse_vars: true, + conditionals: true, + pure_getters: "strict", + reduce_funcs: true, + reduce_vars: true, + sequences: true, + side_effects: true, + toplevel: true, + } + input: { + var a = 1; + a.foo += ""; + if (a.foo) console.log("FAIL"); + else console.log("PASS"); + } + expect: { + var a = 1; + a.foo += "", a.foo ? console.log("FAIL") : console.log("PASS"); + } + expect_stdout: "PASS" +} + +set_immutable_3: { + options = { + collapse_vars: true, + evaluate: true, + pure_getters: "strict", + reduce_funcs: true, + reduce_vars: true, + toplevel: true, + unused: true, + } + input: { + "use strict"; + var a = 1; + a.foo += ""; + if (a.foo) console.log("FAIL"); + else console.log("PASS"); + } + expect: { + "use strict"; + 1..foo += ""; + if (1..foo) console.log("FAIL"); + else console.log("PASS"); + } + expect_stdout: true +} + +set_immutable_4: { + options = { + collapse_vars: true, + conditionals: true, + pure_getters: "strict", + reduce_funcs: true, + reduce_vars: true, + sequences: true, + side_effects: true, + toplevel: true, + } + input: { + "use strict"; + var a = 1; + a.foo += ""; + if (a.foo) console.log("FAIL"); + else console.log("PASS"); + } + expect: { + "use strict"; + var a = 1; + a.foo += "", a.foo ? console.log("FAIL") : console.log("PASS"); + } + expect_stdout: true +} + +set_immutable_5: { + options = { + collapse_vars: true, + conditionals: true, + evaluate: true, + pure_getters: "strict", + reduce_funcs: true, + reduce_vars: true, + side_effects: true, + toplevel: true, + unused: true, + } + input: { + "use strict"; + var a = 1; + a.foo += ""; + if (a.foo) console.log("FAIL"); + else console.log("PASS"); + } + expect: { + "use strict"; + 1..foo += ""; + 1..foo ? console.log("FAIL") : console.log("PASS"); + } + expect_stdout: true +} + +set_immutable_6: { + options = { + collapse_vars: true, + conditionals: true, + evaluate: true, + pure_getters: "strict", + reduce_funcs: true, + reduce_vars: true, + side_effects: true, + toplevel: true, + unused: true, + } + input: { + var a = 1; + a.foo += ""; + if (a.foo) console.log("FAIL"); + else console.log("PASS"); + } + expect: { + 1..foo ? console.log("FAIL") : console.log("PASS"); + } + expect_stdout: true +} + +set_mutable_1: { + options = { + collapse_vars: true, + evaluate: true, + pure_getters: "strict", + reduce_funcs: true, + reduce_vars: true, + unused: true, + } + input: { + !function a() { + a.foo += ""; + if (a.foo) console.log("PASS"); + else console.log("FAIL"); + }(); + } + expect: { + !function a() { + if (a.foo += "") console.log("PASS"); + else console.log("FAIL"); + }(); + } + expect_stdout: "PASS" +} + +set_mutable_2: { + options = { + collapse_vars: true, + conditionals: true, + pure_getters: "strict", + reduce_funcs: true, + reduce_vars: true, + sequences: true, + side_effects: true, + } + input: { + !function a() { + a.foo += ""; + if (a.foo) console.log("PASS"); + else console.log("FAIL"); + }(); + } + expect: { + !function a() { + (a.foo += "") ? console.log("PASS") : console.log("FAIL"); + }(); + } + expect_stdout: "PASS" +} + +issue_2313_1: { + options = { + collapse_vars: true, + conditionals: true, + pure_getters: "strict", + sequences: true, + side_effects: true, + } + input: { + function x() { + console.log(1); + return { + y: function() { + console.log(2); + return { + z: 0 + }; + } + }; + } + x().y().z++; + if (x().y().z) { + console.log(3); + } + } + expect: { + function x() { + return console.log(1), { + y: function() { + return console.log(2), { + z: 0 + }; + } + }; + } + x().y().z++, + x().y().z && console.log(3); + } + expect_stdout: [ + "1", + "2", + "1", + "2", + ] +} + +issue_2313_2: { + options = { + collapse_vars: true, + conditionals: true, + pure_getters: true, + sequences: true, + side_effects: true, + } + input: { + function x() { + console.log(1); + return { + y: function() { + console.log(2); + return { + z: 0 + }; + } + }; + } + x().y().z++; + if (x().y().z) { + console.log(3); + } + } + expect: { + function x() { + return console.log(1), { + y: function() { + return console.log(2), { + z: 0 + }; + } + }; + } + x().y().z++, + x().y().z && console.log(3); + } + expect_stdout: [ + "1", + "2", + "1", + "2", + ] +} + +issue_2313_3: { + options = { + collapse_vars: true, + conditionals: true, + pure_getters: "strict", + } + input: { + function x() { + console.log(1); + return { + y: function() { + console.log(2); + return { + z: 0 + }; + } + }; + } + x().y().z++; + if (x().y().z) { + console.log(3); + } + } + expect: { + function x() { + console.log(1); + return { + y: function() { + console.log(2); + return { + z: 0 + }; + } + }; + } + x().y().z++; + x().y().z && console.log(3); + } + expect_stdout: [ + "1", + "2", + "1", + "2", + ] +} + +issue_2313_4: { + options = { + collapse_vars: true, + conditionals: true, + pure_getters: true, + } + input: { + function x() { + console.log(1); + return { + y: function() { + console.log(2); + return { + z: 0 + }; + } + }; + } + x().y().z++; + if (x().y().z) { + console.log(3); + } + } + expect: { + function x() { + console.log(1); + return { + y: function() { + console.log(2); + return { + z: 0 + }; + } + }; + } + x().y().z++; + x().y().z && console.log(3); + } + expect_stdout: [ + "1", + "2", + "1", + "2", + ] +} + +issue_2313_5: { + options = { + pure_getters: "strict", + side_effects: true, + } + input: { + x().y++; + x().y; + } + expect: { + x().y++; + x().y; + } +} + +issue_2313_6: { + options = { + pure_getters: true, + side_effects: true, + } + input: { + x().y++; + x().y; + } + expect: { + x().y++; + x(); + } +} + +issue_2678: { + options = { + pure_getters: "strict", + side_effects: true, + } + input: { + var a = 1, c = "FAIL"; + (function f() { + (a-- && f()).p; + return { + get p() { + c = "PASS"; + } + }; + })(); + console.log(c); + } + expect: { + var a = 1, c = "FAIL"; + (function f() { + (a-- && f()).p; + return { + get p() { + c = "PASS"; + } + }; + })(); + console.log(c); + } + expect_stdout: "PASS" +} + +issue_2838: { + options = { + pure_getters: true, + side_effects: true, + } + input: { + function f(a, b) { + (a || b).c = "PASS"; + (function() { + return f(a, b); + }).prototype.foo = "bar"; + } + var o = {}; + f(null, o); + console.log(o.c); + } + expect: { + function f(a, b) { + (a || b).c = "PASS"; + } + var o = {}; + f(null, o); + console.log(o.c); + } + expect_stdout: "PASS" +} + +issue_2938_1: { + options = { + pure_getters: true, + unused: true, + } + input: { + function f(a) { + a.b = "PASS"; + } + var o = {}; + f(o); + console.log(o.b); + } + expect: { + function f(a) { + a.b = "PASS"; + } + var o = {}; + f(o); + console.log(o.b); + } + expect_stdout: "PASS" +} + +issue_2938_2: { + options = { + pure_getters: true, + toplevel: true, + unused: true, + } + input: { + var Parser = function Parser() {}; + var p = Parser.prototype; + p.initialContext = function initialContext() { + console.log("PASS"); + }; + p.braceIsBlock = function() {}; + (new Parser).initialContext(); + } + expect: { + var Parser = function() {}; + var p = Parser.prototype; + p.initialContext = function() { + console.log("PASS"); + }; + p.braceIsBlock = function() {}; + (new Parser).initialContext(); + } + expect_stdout: "PASS" +} + +issue_2938_3: { + options = { + pure_getters: true, + side_effects: true, + unused: true, + } + input: { + function f(a) { + var unused = a.a; + a.b = "PASS"; + a.c; + } + var o = {}; + o.d; + f(o); + console.log(o.b); + } + expect: { + function f(a) { + a.b = "PASS"; + } + var o = {}; + f(o); + console.log(o.b); + } + expect_stdout: "PASS" +} + +issue_2938_4: { + options = { + pure_getters: true, + side_effects: true, + toplevel: true, + unused: true, + } + input: { + var Parser = function Parser() {}; + var p = Parser.prototype; + var unused = p.x; + p.initialContext = function initialContext() { + p.y; + console.log("PASS"); + }; + p.braceIsBlock = function() {}; + (new Parser).initialContext(); + } + expect: { + var Parser = function() {}; + var p = Parser.prototype; + p.initialContext = function() { + console.log("PASS"); + }; + p.braceIsBlock = function() {}; + (new Parser).initialContext(); + } + expect_stdout: "PASS" +} + +collapse_vars_1_true: { + options = { + collapse_vars: true, + pure_getters: true, + unused: true, + } + input: { + function f(a, b) { + for (;;) { + var c = a.g(); + var d = b.p; + if (c || d) break; + } + } + } + expect: { + function f(a, b) { + for (;;) + if (a.g() || b.p) break; + } + } +} + +collapse_vars_1_false: { + options = { + collapse_vars: true, + pure_getters: false, + unused: true, + } + input: { + function f(a, b) { + for (;;) { + var c = a.g(); + var d = b.p; + if (c || d) break; + } + } + } + expect: { + function f(a, b) { + for (;;) { + var c = a.g(); + var d = b.p; + if (c || d) break; + } + } + } +} + +collapse_vars_1_strict: { + options = { + collapse_vars: true, + pure_getters: "strict", + unused: true, + } + input: { + function f(a, b) { + for (;;) { + var c = a.g(); + var d = b.p; + if (c || d) break; + } + } + } + expect: { + function f(a, b) { + for (;;) { + var c = a.g(); + var d = b.p; + if (c || d) break; + } + } + } +} + +collapse_vars_2_true: { + options = { + collapse_vars: true, + pure_getters: true, + reduce_vars: true, + } + input: { + function f() { + function g() {} + g.a = function() {}; + g.b = g.a; + return g; + } + } + expect: { + function f() { + function g() {} + g.b = g.a = function() {}; + return g; + } + } +} + +collapse_vars_2_false: { + options = { + collapse_vars: true, + pure_getters: false, + reduce_vars: true, + } + input: { + function f() { + function g() {} + g.a = function() {}; + g.b = g.a; + return g; + } + } + expect: { + function f() { + function g() {} + g.a = function() {}; + g.b = g.a; + return g; + } + } +} + +collapse_vars_2_strict: { + options = { + collapse_vars: true, + pure_getters: "strict", + reduce_vars: true, + } + input: { + function f() { + function g() {} + g.a = function() {}; + g.b = g.a; + return g; + } + } + expect: { + function f() { + function g() {} + g.b = g.a = function() {}; + return g; + } + } +} + +collapse_rhs_true: { + options = { + collapse_vars: true, + evaluate: true, + pure_getters: true, + } + input: { + console.log((42..length = "PASS", "PASS")); + console.log(("foo".length = "PASS", "PASS")); + console.log((false.length = "PASS", "PASS")); + console.log((function() {}.length = "PASS", "PASS")); + console.log(({ + get length() { + return "FAIL"; + } + }.length = "PASS", "PASS")); + } + expect: { + console.log(42..length = "PASS"); + console.log("foo".length = "PASS"); + console.log(false.length = "PASS"); + console.log(function() {}.length = "PASS"); + console.log({ + get length() { + return "FAIL"; + } + }.length = "PASS"); + } + expect_stdout: [ + "PASS", + "PASS", + "PASS", + "PASS", + "PASS", + ] +} + +collapse_rhs_false: { + options = { + collapse_vars: true, + evaluate: true, + pure_getters: false, + } + input: { + console.log((42..length = "PASS", "PASS")); + console.log(("foo".length = "PASS", "PASS")); + console.log((false.length = "PASS", "PASS")); + console.log((function() {}.length = "PASS", "PASS")); + console.log(({ + get length() { + return "FAIL"; + } + }.length = "PASS", "PASS")); + } + expect: { + console.log(42..length = "PASS"); + console.log("foo".length = "PASS"); + console.log(false.length = "PASS"); + console.log(function() {}.length = "PASS"); + console.log({ + get length() { + return "FAIL"; + } + }.length = "PASS"); + } + expect_stdout: [ + "PASS", + "PASS", + "PASS", + "PASS", + "PASS", + ] +} + +collapse_rhs_strict: { + options = { + collapse_vars: true, + evaluate: true, + pure_getters: "strict", + } + input: { + console.log((42..length = "PASS", "PASS")); + console.log(("foo".length = "PASS", "PASS")); + console.log((false.length = "PASS", "PASS")); + console.log((function() {}.length = "PASS", "PASS")); + console.log(({ + get length() { + return "FAIL"; + } + }.length = "PASS", "PASS")); + } + expect: { + console.log(42..length = "PASS"); + console.log("foo".length = "PASS"); + console.log(false.length = "PASS"); + console.log(function() {}.length = "PASS"); + console.log({ + get length() { + return "FAIL"; + } + }.length = "PASS"); + } + expect_stdout: [ + "PASS", + "PASS", + "PASS", + "PASS", + "PASS", + ] +} + +collapse_rhs_setter: { + options = { + collapse_vars: true, + evaluate: true, + pure_getters: "strict", + } + input: { + try { + console.log(({ + set length(v) { + throw "PASS"; + } + }.length = "FAIL", "FAIL")); + } catch (e) { + console.log(e); + } + } + expect: { + try { + console.log({ + set length(v) { + throw "PASS"; + } + }.length = "FAIL"); + } catch (e) { + console.log(e); + } + } + expect_stdout: "PASS" +} + +collapse_rhs_call: { + options = { + collapse_vars: true, + passes: 2, + pure_getters: "strict", + reduce_vars: true, + toplevel: true, + unused: true, + } + input: { + var o = {}; + function f() { + console.log("PASS"); + } + o.f = f; + f(); + } + expect: { + ({}.f = function() { + console.log("PASS"); + })(); + } + expect_stdout: "PASS" +} + +collapse_rhs_lhs: { + options = { + collapse_vars: true, + pure_getters: true, + } + input: { + function f(a, b) { + a.b = b, b += 2; + console.log(a.b, b); + } + f({}, 1); + } + expect: { + function f(a, b) { + a.b = b, b += 2; + console.log(a.b, b); + } + f({}, 1); + } + expect_stdout: "1 3" +} + +drop_arguments: { + options = { + pure_getters: "strict", + side_effects: true, + } + input: { + (function() { + arguments.slice = function() { + console.log("PASS"); + }; + arguments[42]; + arguments.length; + arguments.slice(); + })(); + } + expect: { + (function() { + arguments.slice = function() { + console.log("PASS"); + }; + arguments.slice(); + })(); + } + expect_stdout: "PASS" +} + +lvalues_def: { + options = { + collapse_vars: true, + pure_getters: true, + side_effects: true, + unused: true, + } + input: { + var a = 0, b = 1; + var a = b++, b = +function() {}(); + a && a[a++]; + console.log(a, b); + } + expect: { + var a = 0, b = 1; + a = b++, b = +void 0; + a && a++; + console.log(a, b); + } + expect_stdout: true +} + +side_effects_assign: { + options = { + evaluate: true, + pure_getters: true, + reduce_funcs: true, + reduce_vars: true, + sequences: true, + side_effects: true, + toplevel: true, + } + input: { + var a = typeof void (a && a.in == 1, 0); + console.log(a); + } + expect: { + var a = "undefined"; + console.log(a); + } + expect_stdout: "undefined" +} + +issue_2062: { + options = { + booleans: true, + collapse_vars: true, + conditionals: true, + pure_getters: true, + side_effects: true, + } + input: { + var a = 1; + if ([ a || a++ + a--, a++ + a--, a && a.var ]); + console.log(a); + } + expect: { + var a = 1; + a || (a++, a--), a++, a--; + console.log(a); + } + expect_stdout: "1" +} + +issue_2878: { + options = { + collapse_vars: true, + pure_getters: true, + sequences: true, + side_effects: true, + } + input: { + var c = 0; + (function(a, b) { + function f2() { + if (a) c++; + } + b = f2(); + a = 1; + b && b.b; + f2(); + })(); + console.log(c); + } + expect: { + var c = 0; + (function(a, b) { + function f2() { + if (a) c++; + } + b = f2(), + a = 1, + f2(); + })(), + console.log(c); + } + expect_stdout: "1" +} + +issue_3427: { + options = { + assignments: true, + evaluate: true, + inline: true, + pure_getters: "strict", + reduce_vars: true, + sequences: true, + side_effects: true, + toplevel: true, + unused: true, + } + input: { + var a; + (function(b) { + b.p = 42; + })(a || (a = {})); + } + expect: {} + expect_stdout: true +} + +issue_3490_1: { + options = { + conditionals: true, + dead_code: true, + inline: true, + pure_getters: true, + sequences: true, + side_effects: true, + toplevel: true, + } + input: { + var b = 42, c = "FAIL"; + if ({ + 3: function() { + var a; + return (a && a.p) < this; + }(), + }) c = "PASS"; + if (b) while ("" == typeof d); + console.log(c, b); + } + expect: { + var b = 42, c = "FAIL"; + var a; + if (c = "PASS", b) while ("" == typeof d); + console.log(c, b); + } + expect_stdout: "PASS 42" +} + +issue_4135: { + options = { + evaluate: true, + inline: true, + merge_vars: true, + pure_getters: true, + reduce_vars: true, + side_effects: true, + toplevel: true, + unused: true, + } + input: { + var a = 0, b = 0; + --b; + a++; + if (!a) + var c = function() { + var d = 0; + function f() { + d && d.p; + } + f(); + this; + }(a++); + console.log(a, b, c); + } + expect: { + var a = 0; + 0; + a++; + if (!a) + var c = void a++; + console.log(a, -1, c); + } + expect_stdout: "1 -1 undefined" +} + +issue_4440: { + options = { + pure_getters: "strict", + side_effects: true, + unused: true, + } + input: { + try { + (function() { + arguments = null; + console.log(arguments.p = "FAIL"); + })(); + } catch (e) { + console.log("PASS"); + } + } + expect: { + try { + (function() { + arguments = null; + console.log(arguments.p = "FAIL"); + })(); + } catch (e) { + console.log("PASS"); + } + } + expect_stdout: "PASS" +} + +issue_4730_1: { + options = { + pure_getters: true, + side_effects: true, + } + input: { + var a; + console.log("PASS") + (a && a[a.p]); + } + expect: { + var a; + console.log("PASS"); + } + expect_stdout: "PASS" +} + +issue_4730_2: { + options = { + pure_getters: true, + side_effects: true, + } + input: { + var a; + !console.log("PASS") || a && a[a.p]; + } + expect: { + var a; + console.log("PASS"); + } + expect_stdout: "PASS" +} + +issue_4751: { + options = { + pure_getters: true, + side_effects: true, + } + input: { + var o = { + get p() { + console.log("PASS"); + }, + }; + o && o.p; + } + expect: { + var o = { + get p() { + console.log("PASS"); + }, + }; + } +} + +super_toString: { + options = { + pure_getters: true, + unsafe: true, + } + input: { + console.log({ + f() { + return super.toString(); + }, + }.f()); + } + expect: { + console.log({ + f() { + return super.toString(); + }, + }.f()); + } + expect_stdout: "[object Object]" + node_version: ">=4" +} + +this_toString: { + options = { + pure_getters: true, + unsafe: true, + } + input: { + console.log({ + f() { + return this.toString(); + }, + }.f()); + } + expect: { + console.log({ + f() { + return "" + this; + }, + }.f()); + } + expect_stdout: "[object Object]" + node_version: ">=4" +} + +issue_4803: { + options = { + hoist_vars: true, + join_vars: true, + pure_getters: "strict", + reduce_vars: true, + side_effects: true, + toplevel: true, + unused: true, + } + input: { + var o = { + get f() { + console.log("PASS"); + }, + } || 42; + for (var k in o) + o[k]; + } + expect: { + var k, o = { + get f() { + console.log("PASS"); + }, + } || 42; + for (k in o) + o[k]; + } + expect_stdout: "PASS" +} + +nested_property_assignments_1: { + options = { + pure_getters: "strict", + reduce_vars: true, + sequences: true, + side_effects: true, + toplevel: true, + unused: true, + } + input: { + var f; + ((f = function() { + console.log("FAIL"); + }).p = f).q = console.log("PASS"); + } + expect: { + console.log("PASS"); + } + expect_stdout: "PASS" +} + +nested_property_assignments_2: { + options = { + pure_getters: "strict", + unused: true, + } + input: { + var o = {}; + (function() { + var a; + (o.p = a = {}).q = "PASS"; + })(); + console.log(o.p.q); + } + expect: { + var o = {}; + (function() { + (o.p = {}).q = "PASS"; + })(); + console.log(o.p.q); + } + expect_stdout: "PASS" +} + +nested_property_assignments_3: { + options = { + collapse_vars: true, + pure_getters: true, + side_effects: true, + unused: true, + } + input: { + var o = { p: {} }; + (function(a) { + console && a; + if (console) { + a = a.p; + a.q = a; + } + })(o); + console.log(o.p.q === o.p ? "PASS" : "FAIL"); + } + expect: { + var o = { p: {} }; + (function(a) { + console; + if (console) + (a = a.p).q = a; + })(o); + console.log(o.p.q === o.p ? "PASS" : "FAIL"); + } + expect_stdout: "PASS" +} + +nested_property_assignments_4: { + options = { + pure_getters: true, + side_effects: true, + toplevel: true, + unused: true, + } + input: { + var n, o = { p: { q: { r: "PASS" } } }; + (n = o.p).r = n.q.r; + console.log(o.p.r); + } + expect: { + var n, o = { p: { q: { r: "PASS" } } }; + (n = o.p).r = n.q.r; + console.log(o.p.r); + } + expect_stdout: "PASS" +} + +issue_4939: { + options = { + pure_getters: "strict", + side_effects: true, + } + input: { + ({ + __proto__: { + get p() { + console.log("PASS"); + }, + }, + }).p; + } + expect: { + ({ + __proto__: { + get p() { + console.log("PASS"); + }, + }, + }).p; + } + expect_stdout: "PASS" +} diff --git a/test/compress/reduce_vars.js b/test/compress/reduce_vars.js new file mode 100644 index 00000000000..674d98c312d --- /dev/null +++ b/test/compress/reduce_vars.js @@ -0,0 +1,8224 @@ +reduce_vars: { + options = { + conditionals: true, + evaluate: true, + global_defs: { + C: 0, + }, + inline: true, + passes: 2, + reduce_funcs: true, + reduce_vars: true, + toplevel: true, + unused: true, + } + input: { + var A = 1; + (function f0() { + var a = 2; + console.log(a - 5); + console.log(A - 5); + })(); + (function f1() { + var a = 2; + console.log(a - 5); + eval("console.log(a);"); + })(); + (function f2(eval) { + var a = 2; + console.log(a - 5); + eval("console.log(a);"); + })(eval); + (function f3() { + var b = typeof C !== "undefined"; + var c = 4; + if (b) { + return 'yes'; + } else { + return 'no'; + } + })(); + console.log(A + 1); + } + expect: { + var A = 1; + console.log(-3); + console.log(A - 5); + (function f1() { + var a = 2; + console.log(a - 5); + eval("console.log(a);"); + })(); + (function f2(eval) { + var a = 2; + console.log(a - 5); + eval("console.log(a);"); + })(eval); + true, "yes"; + console.log(A + 1); + } + expect_stdout: true +} + +modified: { + options = { + conditionals: true, + evaluate: true, + reduce_funcs: true, + reduce_vars: true, + unused: true, + } + input: { + function f0() { + var a = 1, b = 2; + b++; + console.log(a + 1); + console.log(b + 1); + } + function f1() { + var a = 1, b = 2; + --b; + console.log(a + 1); + console.log(b + 1); + } + function f2() { + var a = 1, b = 2, c = 3; + b = c; + console.log(a + b); + console.log(b + c); + console.log(a + c); + console.log(a + b + c); + } + function f3() { + var a = 1, b = 2, c = 3; + b *= c; + console.log(a + b); + console.log(b + c); + console.log(a + c); + console.log(a + b + c); + } + function f4() { + var a = 1, b = 2, c = 3; + if (a) { + b = c; + } else { + c = b; + } + console.log(a + b); + console.log(b + c); + console.log(a + c); + console.log(a + b + c); + } + function f5(a) { + B = a; + console.log(typeof A ? "yes" : "no"); + console.log(typeof B ? "yes" : "no"); + } + f0(), f1(), f2(), f3(), f4(), f5(); + } + expect: { + function f0() { + var b = 2; + +b; + console.log(2); + console.log(4); + } + function f1() { + var b = 2; + --b; + console.log(2); + console.log(2); + } + function f2() { + 3; + console.log(4); + console.log(6); + console.log(4); + console.log(7); + } + function f3() { + var b = 2; + b *= 3; + console.log(7); + console.log(9); + console.log(4); + console.log(10); + } + function f4() { + var b = 2, c = 3; + 1, b = c; + console.log(1 + b); + console.log(b + c); + console.log(1 + c); + console.log(1 + b + c); + } + function f5(a) { + B = a; + console.log(typeof A ? "yes" : "no"); + console.log(typeof B ? "yes" : "no"); + } + f0(), f1(), f2(), f3(), f4(), f5(); + } + expect_stdout: [ + "2", + "4", + "2", + "2", + "4", + "6", + "4", + "7", + "7", + "9", + "4", + "10", + "4", + "6", + "4", + "7", + "yes", + "yes", + ] +} + +unsafe_evaluate: { + options = { + evaluate: true, + reduce_funcs: true, + reduce_vars: true, + side_effects: true, + unsafe: true, + unused: true, + } + input: { + function f0() { + var a = { b: 1 }; + console.log(a.b + 3); + } + function f1() { + var a = { + b: { c: 1 }, + d: 2 + }; + console.log(a.b + 3, a.d + 4, a.b.c + 5, a.d.c + 6); + } + f0(); + f1(); + } + expect: { + function f0() { + console.log(4); + } + function f1() { + var a = { + b: { c: 1 }, + d: 2 + }; + console.log(a.b + 3, 6, 6, NaN); + } + f0(); + f1(); + } + expect_stdout: true +} + +unsafe_evaluate_defun: { + options = { + evaluate: true, + reduce_vars: true, + unsafe: true, + unused: true, + } + input: { + console.log(function() { + function f() {} + return ++f; + }()); + } + expect: { + console.log(NaN); + } + expect_stdout: "NaN" +} + +unsafe_evaluate_side_effect_free_1: { + options = { + evaluate: true, + reduce_funcs: true, + reduce_vars: true, + unsafe: true, + unused: true, + } + input: { + console.log(function(){ var o={p:1}; console.log(o.p); return o.p; }()); + console.log(function(){ var o={p:2}; console.log(o.p); return o; }()); + console.log(function(){ var o={p:3}; console.log([o][0].p); return o.p; }()); + } + expect: { + console.log(function(){ console.log(1); return 1; }()); + console.log(function(){ var o={p:2}; console.log(2); return o; }()); + console.log(function(){ console.log(3); return 3; }()); + } + expect_stdout: true +} + +unsafe_evaluate_side_effect_free_2: { + options = { + collapse_vars: true, + evaluate: true, + passes: 2, + pure_getters: "strict", + reduce_funcs: true, + reduce_vars: true, + unsafe: true, + unused: true, + } + input: { + console.log(function(){ var o={p:1},a=[o]; console.log(a[0].p); return o.p; }()); + } + expect: { + console.log(function(){ console.log(1); return 1; }()); + } + expect_stdout: true +} + +unsafe_evaluate_escaped: { + options = { + evaluate: true, + reduce_funcs: true, + reduce_vars: true, + unsafe: true, + unused: true, + } + input: { + console.log(function(){ var o={p:1}; console.log(o, o.p); return o.p; }()); + console.log(function(){ var o={p:2}; console.log(o.p, o); return o.p; }()); + console.log(function(){ var o={p:3},a=[o]; console.log(a[0].p++); return o.p; }()); + } + expect: { + console.log(function(){ var o={p:1}; console.log(o, 1); return o.p; }()); + console.log(function(){ var o={p:2}; console.log(2, o); return o.p; }()); + console.log(function(){ var o={p:3},a=[o]; console.log(a[0].p++); return o.p; }()); + } + expect_stdout: true +} + +unsafe_evaluate_modified: { + options = { + evaluate: true, + reduce_funcs: true, + reduce_vars: true, + unsafe: true, + unused: true, + } + input: { + console.log(function(){ var o={p:1}; o.p++; console.log(o.p); return o.p; }()); + console.log(function(){ var o={p:2}; --o.p; console.log(o.p); return o.p; }()); + console.log(function(){ var o={p:3}; o.p += ""; console.log(o.p); return o.p; }()); + console.log(function(){ var o={p:4}; o = {}; console.log(o.p); return o.p; }()); + console.log(function(){ var o={p:5}; o.p = -9; console.log(o.p); return o.p; }()); + function inc() { this.p++; } + console.log(function(){ var o={p:6}; inc.call(o); console.log(o.p); return o.p; }()); + console.log(function(){ var o={p:7}; console.log([o][0].p++); return o.p; }()); + console.log(function(){ var o={p:8}; console.log({q:o}.q.p++); return o.p; }()); + } + expect: { + console.log(function(){ var o={p:1}; o.p++; console.log(o.p); return o.p; }()); + console.log(function(){ var o={p:2}; --o.p; console.log(o.p); return o.p; }()); + console.log(function(){ var o={p:3}; o.p += ""; console.log(o.p); return o.p; }()); + console.log(function(){ var o; o = {}; console.log(o.p); return o.p; }()); + console.log(function(){ var o={p:5}; o.p = -9; console.log(o.p); return o.p; }()); + function inc() { this.p++; } + console.log(function(){ var o={p:6}; inc.call(o); console.log(o.p); return o.p; }()); + console.log(function(){ var o={p:7}; console.log([o][0].p++); return o.p; }()); + console.log(function(){ var o={p:8}; console.log({q:o}.q.p++); return o.p; }()); + } + expect_stdout: true +} + +unsafe_evaluate_unknown: { + options = { + evaluate: true, + reduce_funcs: true, + reduce_vars: true, + unsafe: true, + unused: true, + } + input: { + console.log(function(){ var o={p:1}; console.log(o.not_present); return o.p; }()); + console.log(function(){ var o={p:2}; console.log(o.prototype); return o.p; }()); + console.log(function(){ var o={p:3}; console.log(o.hasOwnProperty); return o.p; }()); + } + expect: { + console.log(function(){ var o={p:1}; console.log(o.not_present); return o.p; }()); + console.log(function(){ var o={p:2}; console.log(o.prototype); return o.p; }()); + console.log(function(){ var o={p:3}; console.log(o.hasOwnProperty); return o.p; }()); + } + expect_stdout: true +} + +unsafe_evaluate_object_1: { + options = { + evaluate: true, + reduce_funcs: true, + reduce_vars: true, + unsafe: true, + } + input: { + function f0(){ + var a = 1; + var b = {}; + b[a] = 2; + console.log(a + 3); + } + + function f1(){ + var a = { + b:1 + }; + a.b = 2; + console.log(a.b + 3); + } + } + expect: { + function f0(){ + var a = 1; + var b = {}; + b[1] = 2; + console.log(4); + } + + function f1(){ + var a = { + b:1 + }; + a.b = 2; + console.log(a.b + 3); + } + } +} + +unsafe_evaluate_object_2: { + options = { + evaluate: true, + reduce_funcs: true, + reduce_vars: true, + toplevel: true, + unsafe: true, + } + input: { + var obj = { + foo: 1, + bar: 2, + square: function(x) { + return x * x; + }, + cube: function(x) { + return x * x * x; + }, + }; + console.log(obj.foo, obj.bar, obj.square(2), obj.cube); + } + expect: { + var obj = { + foo: 1, + bar: 2, + square: function(x) { + return x * x; + }, + cube: function(x) { + return x * x * x; + }, + }; + console.log(1, 2, obj.square(2), obj.cube); + } + expect_stdout: true +} + +unsafe_evaluate_object_3: { + options = { + evaluate: true, + reduce_funcs: true, + reduce_vars: true, + toplevel: true, + unsafe: true, + } + input: { + var obj = { + get foo() { + return 1; + }, + bar: 2, + square: function(x) { + return x * x; + }, + cube: function(x) { + return x * x * x; + }, + }; + console.log(obj.foo, obj.bar, obj.square(2), obj.cube); + } + expect: { + var obj = { + get foo() { + return 1; + }, + bar: 2, + square: function(x) { + return x * x; + }, + cube: function(x) { + return x * x * x; + }, + }; + console.log(obj.foo, obj.bar, obj.square(2), obj.cube); + } + expect_stdout: true +} + +unsafe_evaluate_array_1: { + options = { + evaluate: true, + reduce_funcs: true, + reduce_vars: true, + unsafe: true, + } + input: { + function f0(){ + var a = 1; + var b = []; + b[a] = 2; + console.log(a + 3); + } + + function f1(){ + var a = [1]; + a[2] = 3; + console.log(a.length); + } + + function f2(){ + var a = [1]; + a.push(2); + console.log(a.length); + } + } + expect: { + function f0(){ + var a = 1; + var b = []; + b[1] = 2; + console.log(4); + } + + function f1(){ + var a = [1]; + a[2] = 3; + console.log(a.length); + } + + function f2(){ + var a = [1]; + a.push(2); + console.log(a.length); + } + } +} + +unsafe_evaluate_array_2: { + options = { + evaluate: true, + reduce_funcs: true, + reduce_vars: true, + toplevel: true, + unsafe: true, + } + input: { + var arr = [ + 1, + 2, + function(x) { + return x * x; + }, + function(x) { + return x * x * x; + }, + ]; + console.log(arr[0], arr[1], arr[2](2), arr[3]); + } + expect: { + var arr = [ + 1, + 2, + function(x) { + return x * x; + }, + function(x) { + return x * x * x; + }, + ]; + console.log(1, 2, arr[2](2), arr[3]); + } + expect_stdout: true +} + +unsafe_evaluate_array_3: { + options = { + evaluate: true, + reduce_funcs: true, + reduce_vars: true, + toplevel: true, + unsafe: true, + } + input: { + var arr = [ + 1, + 2, + function() { + return ++arr[0]; + }, + ]; + console.log(arr[0], arr[1], arr[2](), arr[0]); + } + expect: { + var arr = [ + 1, + 2, + function() { + return ++arr[0]; + }, + ]; + console.log(arr[0], arr[1], arr[2](), arr[0]); + } + expect_stdout: "1 2 2 2" +} + +unsafe_evaluate_array_4: { + options = { + evaluate: true, + reduce_funcs: true, + reduce_vars: true, + toplevel: true, + unsafe: true, + } + input: { + var arr = [ + 1, + 2, + function() { + return ++this[0]; + }, + ]; + console.log(arr[0], arr[1], arr[2], arr[0]); + } + expect: { + var arr = [ + 1, + 2, + function() { + return ++this[0]; + }, + ]; + console.log(1, 2, arr[2], 1); + } + expect_stdout: true +} + +unsafe_evaluate_array_5: { + options = { + evaluate: true, + reduce_funcs: true, + reduce_vars: true, + toplevel: true, + unsafe: true, + } + input: { + var arr = [ + 1, + 2, + function() { + return ++this[0]; + }, + ]; + console.log(arr[0], arr[1], arr[2](), arr[0]); + } + expect: { + var arr = [ + 1, + 2, + function() { + return ++this[0]; + }, + ]; + console.log(arr[0], arr[1], arr[2](), arr[0]); + } + expect_stdout: "1 2 2 2" +} + +unsafe_evaluate_equality_1: { + options = { + evaluate: true, + reduce_funcs: true, + reduce_vars: true, + unsafe: true, + unused: true, + } + input: { + function f0() { + var a = {}; + return a === a; + } + function f1() { + var a = []; + return a === a; + } + console.log(f0(), f1()); + } + expect: { + function f0() { + return true; + } + function f1() { + return true; + } + console.log(f0(), f1()); + } + expect_stdout: true +} + +unsafe_evaluate_equality_2: { + options = { + collapse_vars: true, + evaluate: true, + passes: 2, + reduce_funcs: true, + reduce_vars: true, + unsafe: true, + unused: true, + } + input: { + function f2() { + var a = {a:1, b:2}; + var b = a; + var c = a; + return b === c; + } + function f3() { + var a = [1, 2, 3]; + var b = a; + var c = a; + return b === c; + } + console.log(f2(), f3()); + } + expect: { + function f2() { + return true; + } + function f3() { + return true; + } + console.log(f2(), f3()); + } + expect_stdout: true +} + +passes: { + options = { + conditionals: true, + evaluate: true, + passes: 2, + reduce_funcs: true, + reduce_vars: true, + sequences: true, + side_effects: true, + unused: true, + } + input: { + (function() { + var a = 1, b = 2, c = 3; + if (a) { + b = c; + } else { + c = b; + } + console.log(a + b); + console.log(b + c); + console.log(a + c); + console.log(a + b + c); + })(); + } + expect: { + (function() { + console.log(4), + console.log(6), + console.log(4), + console.log(7); + })(); + } + expect_stdout: [ + "4", + "6", + "4", + "7", + ] +} + +iife: { + options = { + evaluate: true, + reduce_funcs: true, + reduce_vars: true, + } + input: { + !function(a, b, c) { + b++; + console.log(a - 1, b * 1, c + 2); + }(1, 2, 3); + } + expect: { + !function(a, b, c) { + b++; + console.log(0, 3, 5); + }(1, 2, 3); + } + expect_stdout: true +} + +iife_new: { + options = { + evaluate: true, + reduce_funcs: true, + reduce_vars: true, + } + input: { + var A = new function(a, b, c) { + b++; + console.log(a - 1, b * 1, c + 2); + }(1, 2, 3); + } + expect: { + var A = new function(a, b, c) { + b++; + console.log(0, 3, 5); + }(1, 2, 3); + } + expect_stdout: true +} + +multi_def_1: { + options = { + evaluate: true, + reduce_funcs: true, + reduce_vars: true, + } + input: { + function f(a) { + if (a) + var b = 1; + else + var b = 2; + console.log(b + 1); + } + } + expect: { + function f(a) { + if (a) + var b = 1; + else + var b = 2; + console.log(b + 1); + } + } +} + +multi_def_2: { + options = { + evaluate: true, + reduce_funcs: true, + reduce_vars: true, + } + input: { + function f(){ + if (code == 16) + var bitsLength = 2, bitsOffset = 3, what = len; + else if (code == 17) + var bitsLength = 3, bitsOffset = 3, what = (len = 0); + else if (code == 18) + var bitsLength = 7, bitsOffset = 11, what = (len = 0); + var repeatLength = this.getBits(bitsLength) + bitsOffset; + } + } + expect: { + function f(){ + if (16 == code) + var bitsLength = 2, bitsOffset = 3, what = len; + else if (17 == code) + var bitsLength = 3, bitsOffset = 3, what = (len = 0); + else if (18 == code) + var bitsLength = 7, bitsOffset = 11, what = (len = 0); + var repeatLength = this.getBits(bitsLength) + bitsOffset; + } + } +} + +multi_def_3: { + options = { + evaluate: true, + reduce_funcs: true, + reduce_vars: true, + } + input: { + function f(a) { + var b = 2; + if (a) + var b; + else + var b; + console.log(b + 1); + } + } + expect: { + function f(a) { + var b = 2; + if (a) + var b; + else + var b; + console.log(3); + } + } +} + +use_before_var: { + options = { + evaluate: true, + reduce_funcs: true, + reduce_vars: true, + } + input: { + function f(){ + console.log(t); + var t = 1; + } + } + expect: { + function f(){ + console.log(t); + var t = 1; + } + } +} + +inner_var_if: { + options = { + evaluate: true, + reduce_funcs: true, + reduce_vars: true, + } + input: { + function f(a){ + if (a) + var t = 1; + if (!t) + console.log(t); + } + } + expect: { + function f(a){ + if (a) + var t = 1; + if (!t) + console.log(t); + } + } +} + +inner_var_label: { + options = { + evaluate: true, + reduce_funcs: true, + reduce_vars: true, + } + input: { + function f(a){ + l: { + if (a) break l; + var t = 1; + } + console.log(t); + } + } + expect: { + function f(a){ + l: { + if (a) break l; + var t = 1; + } + console.log(t); + } + } +} + +inner_var_for_1: { + options = { + evaluate: true, + reduce_funcs: true, + reduce_vars: true, + } + input: { + function f() { + var a = 1; + x(a, b, d); + for (var b = 2, c = 3; x(a, b, c, d); x(a, b, c, d)) { + var d = 4, e = 5; + x(a, b, c, d, e); + } + x(a, b, c, d, e); + } + } + expect: { + function f() { + var a = 1; + x(1, b, d); + for (var b = 2, c = 3; x(1, 2, 3, d); x(1, 2, 3, d)) { + var d = 4, e = 5; + x(1, 2, 3, d, e); + } + x(1, 2, 3, d, e); + } + } +} + +inner_var_for_2: { + options = { + evaluate: true, + reduce_funcs: true, + reduce_vars: true, + unused: true, + } + input: { + !function() { + var a = 1; + for (var b = 1; --b;) var a = 2; + console.log(a); + }(); + } + expect: { + !function() { + var a = 1; + for (var b = 1; --b;) a = 2; + console.log(a); + }(); + } + expect_stdout: "1" +} + +inner_var_for_in_1: { + options = { + evaluate: true, + reduce_funcs: true, + reduce_vars: true, + } + input: { + function f() { + var a = 1, b = 2; + for (b in (function() { + return x(a, b, c); + })()) { + var c = 3, d = 4; + x(a, b, c, d); + } + x(a, b, c, d); + } + } + expect: { + function f() { + var a = 1, b = 2; + for (b in (function() { + return x(1, b, c); + })()) { + var c = 3, d = 4; + x(1, b, c, d); + } + x(1, b, c, d); + } + } +} + +inner_var_for_in_2: { + options = { + evaluate: true, + reduce_funcs: true, + reduce_vars: true, + } + input: { + function f() { + for (var long_name in {}) + console.log(long_name); + } + } + expect: { + function f() { + for (var long_name in {}) + console.log(long_name); + } + } +} + +inner_var_catch: { + options = { + evaluate: true, + reduce_funcs: true, + reduce_vars: true, + } + input: { + function f() { + try { + a(); + } catch (e) { + var b = 1; + } + console.log(b); + } + } + expect: { + function f() { + try { + a(); + } catch (e) { + var b = 1; + } + console.log(b); + } + } +} + +issue_1533_1: { + options = { + collapse_vars: true, + reduce_funcs: true, + reduce_vars: true, + } + input: { + function f() { + var id = ""; + for (id in {break: "me"}) + console.log(id); + } + } + expect: { + function f() { + var id = ""; + for (id in {break: "me"}) + console.log(id); + } + } +} + +issue_1533_2: { + options = { + evaluate: true, + reduce_funcs: true, + reduce_vars: true, + } + input: { + function f() { + var id = ""; + for (var id in {break: "me"}) + console.log(id); + console.log(id); + } + } + expect: { + function f() { + var id = ""; + for (var id in {break: "me"}) + console.log(id); + console.log(id); + } + } +} + +toplevel_on: { + options = { + evaluate: true, + reduce_funcs: true, + reduce_vars: true, + toplevel: true, + unused: true, + } + input: { + var x = 3; + console.log(x); + } + expect: { + console.log(3); + } + expect_stdout: true +} + +toplevel_off: { + options = { + evaluate: true, + reduce_funcs: true, + reduce_vars: true, + toplevel: false, + unused: true, + } + input: { + var x = 3; + console.log(x); + } + expect: { + var x = 3; + console.log(x); + } + expect_stdout: true +} + +toplevel_on_loops_1: { + options = { + evaluate: true, + loops: true, + reduce_funcs: true, + reduce_vars: true, + toplevel: true, + unused: true, + } + input: { + function bar() { + console.log("bar:", --x); + } + var x = 3; + do + bar(); + while (x); + } + expect: { + var x = 3; + for (;function() { + console.log("bar:", --x); + }(), x;); + } + expect_stdout: true +} + +toplevel_off_loops_1: { + options = { + evaluate: true, + loops: true, + reduce_funcs: true, + reduce_vars: true, + toplevel: false, + unused: true, + } + input: { + function bar() { + console.log("bar:", --x); + } + var x = 3; + do + bar(); + while (x); + } + expect: { + function bar() { + console.log("bar:", --x); + } + var x = 3; + for (;bar(), x;); + } + expect_stdout: true +} + +toplevel_on_loops_2: { + options = { + evaluate: true, + loops: true, + reduce_funcs: true, + reduce_vars: true, + side_effects: true, + toplevel: true, + unused: true, + } + input: { + function bar() { + console.log("bar:"); + } + var x = 3; + do + bar(); + while (x); + } + expect: { + for (;;) (function() { + console.log("bar:"); + })(); + } +} + +toplevel_off_loops_2: { + options = { + evaluate: true, + loops: true, + reduce_funcs: true, + reduce_vars: true, + toplevel: false, + unused: true, + } + input: { + function bar() { + console.log("bar:"); + } + var x = 3; + do + bar(); + while (x); + } + expect: { + function bar() { + console.log("bar:"); + } + var x = 3; + for (;bar(), x;); + } +} + +toplevel_on_loops_3: { + options = { + evaluate: true, + loops: true, + reduce_funcs: true, + reduce_vars: true, + side_effects: true, + toplevel: true, + unused: true, + } + input: { + var x = 3; + while (x) bar(); + } + expect: { + for (;;) bar(); + } +} + +toplevel_off_loops_3: { + options = { + evaluate: true, + loops: true, + reduce_funcs: true, + reduce_vars: true, + toplevel: false, + unused: true, + } + input: { + var x = 3; + while (x) bar(); + } + expect: { + var x = 3; + for (;x;) bar(); + } +} + +defun_reference: { + options = { + evaluate: true, + reduce_funcs: true, + reduce_vars: true, + } + input: { + function f() { + function g() { + x(); + return a; + } + var a = h(); + var b = 2; + return a + b; + function h() { + y(); + return b; + } + } + } + expect: { + function f() { + function g() { + x(); + return a; + } + var a = h(); + var b = 2; + return a + b; + function h() { + y(); + return b; + } + } + } +} + +defun_inline_1: { + options = { + reduce_funcs: true, + reduce_vars: true, + unused: true, + } + input: { + function f() { + return g(2) + h(); + function g(b) { + return b; + } + function h() { + return h(); + } + } + } + expect: { + function f() { + return function(b) { + return b; + }(2) + function h() { + return h(); + }(); + } + } +} + +defun_inline_2: { + options = { + reduce_funcs: true, + reduce_vars: true, + unused: true, + } + input: { + function f() { + function g(b) { + return b; + } + function h() { + return h(); + } + return g(2) + h(); + } + } + expect: { + function f() { + return function(b) { + return b; + }(2) + function h() { + return h(); + }(); + } + } +} + +defun_inline_3: { + options = { + evaluate: true, + inline: true, + passes: 3, + reduce_funcs: true, + reduce_vars: true, + side_effects: true, + unused: true, + } + input: { + function f() { + return g(2); + function g(b) { + return b; + } + } + } + expect: { + function f() { + return 2; + } + } +} + +defun_call: { + options = { + evaluate: true, + reduce_funcs: true, + reduce_vars: true, + unused: true, + } + input: { + console.log(function f() { + return g() + h(1) - h(g(), 2, 3); + function g() { + return 4; + } + function h(a) { + return a; + } + }()); + } + expect: { + console.log(1); + } + expect_stdout: "1" +} + +defun_redefine: { + options = { + evaluate: true, + reduce_funcs: true, + reduce_vars: true, + unused: true, + } + input: { + function f() { + function g() { + return 1; + } + function h() { + return 2; + } + g = function() { + return 3; + }; + return g() + h(); + } + console.log(f()); + } + expect: { + function f() { + (function() { + return 3; + }); + return 5; + } + console.log(f()); + } + expect_stdout: "5" +} + +func_inline: { + options = { + inline: true, + reduce_funcs: true, + reduce_vars: true, + unused: true, + } + input: { + function f() { + var g = function() { + return 1; + }; + console.log(g() + h()); + var h = function() { + return 2; + }; + } + } + expect: { + function f() { + console.log(1 + (void 0)()); + } + } +} + +func_modified: { + options = { + evaluate: true, + reduce_funcs: true, + reduce_vars: true, + unused: true, + } + input: { + function f(a) { + function a() { + return 1; + } + function b() { + return 2; + } + function c() { + return 3; + } + b.inject = []; + c = function() { + return 4; + }; + return a() + b() + c(); + } + console.log(f()); + } + expect: { + function f(a) { + function b() { + return 2; + } + b.inject = []; + (function() { + return 4; + }); + return 7; + } + console.log(f()); + } + expect_stdout: "7" +} + +unused_modified: { + options = { + reduce_vars: true, + unused: true, + } + input: { + console.log(function() { + var b = 1, c = "FAIL"; + if (0 || b--) + c = "PASS"; + b = 1; + return c; + }()); + } + expect: { + console.log(function() { + var b = 1, c = "FAIL"; + if (0 || b--) + c = "PASS"; + b = 1; + return c; + }()); + } + expect_stdout: "PASS" +} + +defun_label: { + options = { + passes: 2, + reduce_funcs: true, + reduce_vars: true, + unused: true, + } + input: { + !function() { + function f(a) { + L: { + if (a) break L; + return 1; + } + } + console.log(f(2)); + }(); + } + expect: { + !function() { + console.log(function(a) { + L: { + if (2) break L; + return 1; + } + }()); + }(); + } + expect_stdout: true +} + +double_reference_1: { + options = { + reduce_funcs: true, + reduce_vars: true, + unused: true, + } + input: { + function f() { + var g = function g() { + g(); + }; + g(); + } + } + expect: { + function f() { + var g = function g() { + g(); + }; + g(); + } + } +} + +double_reference_2: { + options = { + functions: true, + passes: 2, + reduce_funcs: true, + reduce_vars: true, + unused: true, + } + input: { + function f() { + var g = function g() { + g(); + }; + g(); + } + } + expect: { + function f() { + (function g() { + g(); + })(); + } + } +} + +double_reference_3: { + options = { + reduce_funcs: true, + reduce_vars: true, + toplevel: true, + unused: true, + } + input: { + var x = function f() { + return f; + }; + function g() { + return x(); + } + console.log(g() === g()); + } + expect: { + var x = function f() { + return f; + }; + function g() { + return x(); + } + console.log(g() === g()); + } + expect_stdout: "true" +} + +double_reference_4: { + options = { + comparisons: true, + functions: true, + inline: true, + passes: 2, + reduce_funcs: true, + reduce_vars: true, + toplevel: true, + unused: true, + } + input: { + var x = function f() { + return f; + }; + function g() { + return x(); + } + console.log(g() === g()); + } + expect: { + console.log(true); + } + expect_stdout: "true" +} + +iife_arguments_1: { + options = { + reduce_funcs: true, + reduce_vars: true, + unused: true, + } + input: { + (function(x) { + console.log(x() === arguments[0]); + })(function f() { + return f; + }); + } + expect: { + (function(x) { + console.log(x() === arguments[0]); + })(function f() { + return f; + }); + } + expect_stdout: true +} + +iife_arguments_2: { + options = { + reduce_funcs: true, + reduce_vars: true, + unused: true, + } + input: { + (function() { + var x = function f() { + return f; + }; + console.log(x() === arguments[0]); + })(); + } + expect: { + (function() { + var x = function f() { + return f; + }; + console.log(x() === arguments[0]); + })(); + } + expect_stdout: true +} + +iife_arguments_3: { + options = { + functions: true, + passes: 2, + reduce_funcs: true, + reduce_vars: true, + unused: true, + } + input: { + (function() { + var x = function f() { + return f; + }; + console.log(x() === arguments[0]); + })(); + } + expect: { + (function() { + console.log(function x() { + return x; + }() === arguments[0]); + })(); + } + expect_stdout: true +} + +iife_eval_1: { + options = { + reduce_funcs: true, + reduce_vars: true, + unused: true, + } + input: { + (function(x) { + console.log(x() === eval("x")); + })(function f() { + return f; + }); + } + expect: { + (function(x) { + console.log(x() === eval("x")); + })(function f() { + return f; + }); + } + expect_stdout: true +} + +iife_eval_2: { + options = { + reduce_funcs: true, + reduce_vars: true, + unused: true, + } + input: { + (function() { + var x = function f() { + return f; + }; + console.log(x() === eval("x")); + })(); + } + expect: { + (function() { + var x = function f() { + return f; + }; + console.log(x() === eval("x")); + })(); + } + expect_stdout: true +} + +iife_func_side_effects: { + options = { + reduce_funcs: true, + reduce_vars: true, + unused: true, + } + input: { + function x() { + console.log("x"); + } + function y() { + console.log("y"); + } + function z() { + console.log("z"); + } + (function(a, b, c) { + function y() { + console.log("FAIL"); + } + return y + b(); + })(x(), function() { + return y(); + }, z()); + } + expect: { + function x() { + console.log("x"); + } + function y() { + console.log("y"); + } + function z() { + console.log("z"); + } + (function(a, b, c) { + return function() { + console.log("FAIL"); + } + b(); + })(x(), function() { + return y(); + }, z()); + } + expect_stdout: [ + "x", + "z", + "y", + ] +} + +issue_1595_1: { + options = { + evaluate: true, + reduce_funcs: true, + reduce_vars: true, + unused: true, + } + input: { + (function f(a) { + return f(a + 1); + })(2); + } + expect: { + (function f(a) { + return f(a + 1); + })(2); + } +} + +issue_1595_2: { + options = { + evaluate: true, + reduce_funcs: true, + reduce_vars: true, + unused: true, + } + input: { + (function f(a) { + return g(a + 1); + })(2); + } + expect: { + (function(a) { + return g(a + 1); + })(2); + } +} + +issue_1595_3: { + options = { + evaluate: true, + passes: 2, + reduce_funcs: true, + reduce_vars: true, + unused: true, + } + input: { + (function f(a) { + return g(a + 1); + })(2); + } + expect: { + (function(a) { + return g(3); + })(); + } +} + +issue_1595_4: { + options = { + evaluate: true, + reduce_funcs: true, + reduce_vars: true, + unused: true, + } + input: { + (function iife(a, b, c) { + console.log(a, b, c); + if (a) iife(a - 1, b, c); + })(3, 4, 5); + } + expect: { + (function iife(a, b, c) { + console.log(a, b, c); + if (a) iife(a - 1, b, c); + })(3, 4, 5); + } + expect_stdout: true +} + +issue_1606: { + options = { + evaluate: true, + hoist_vars: true, + reduce_funcs: true, + reduce_vars: true, + } + input: { + function f() { + var a; + function g(){}; + var b = 2; + x(b); + } + } + expect: { + function f() { + var a, b; + function g(){}; + b = 2; + x(2); + } + } +} + +issue_1670_1: { + options = { + comparisons: true, + conditionals: true, + dead_code: true, + evaluate: true, + reduce_funcs: true, + reduce_vars: true, + side_effects: true, + switches: true, + typeofs: true, + unused: true, + } + input: { + (function f() { + switch (1) { + case 0: + var a = true; + break; + default: + if (typeof a === "undefined") console.log("PASS"); + else console.log("FAIL"); + } + })(); + } + expect: { + (function() { + var a; + void 0 === a ? console.log("PASS") : console.log("FAIL"); + })(); + } + expect_stdout: "PASS" +} + +issue_1670_2: { + options = { + conditionals: true, + dead_code: true, + evaluate: true, + passes: 2, + reduce_funcs: true, + reduce_vars: true, + side_effects: true, + switches: true, + unused: true, + } + input: { + (function f() { + switch (1) { + case 0: + var a = true; + break; + default: + if (typeof a === "undefined") console.log("PASS"); + else console.log("FAIL"); + } + })(); + } + expect: { + (function() { + console.log("PASS"); + })(); + } + expect_stdout: "PASS" +} + +issue_1670_3: { + options = { + conditionals: true, + dead_code: true, + evaluate: true, + keep_fargs: false, + reduce_funcs: true, + reduce_vars: true, + side_effects: true, + switches: true, + unused: true, + } + input: { + (function(a) { + switch (1) { + case a: + console.log(a); + break; + default: + console.log(2); + break; + } + })(1); + } + expect: { + (function() { + console.log(1); + })(); + } + expect_stdout: "1" +} + +issue_1670_4: { + options = { + conditionals: true, + dead_code: true, + evaluate: true, + keep_fargs: false, + reduce_funcs: true, + reduce_vars: true, + sequences: true, + side_effects: true, + switches: true, + unused: true, + } + input: { + (function(a) { + switch (1) { + case a = 1: + console.log(a); + break; + default: + console.log(2); + break; + } + })(1); + } + expect: { + (function() { + console.log(1); + })(); + } + expect_stdout: "1" +} + +unary_delete: { + options = { + evaluate: true, + reduce_funcs: true, + reduce_vars: true, + unused: true, + } + input: { + var b = 10; + function f() { + var a; + if (delete a) b--; + } + f(); + console.log(b); + } + expect: { + var b = 10; + function f() { + var a; + if (delete a) b--; + } + f(); + console.log(b); + } + expect_stdout: true +} + +redefine_arguments_1: { + options = { + evaluate: true, + keep_fargs: false, + reduce_funcs: true, + reduce_vars: true, + unused: true, + } + input: { + function f() { + var arguments; + return typeof arguments; + } + function g() { + var arguments = 42; + return typeof arguments; + } + function h(x) { + var arguments = x; + return typeof arguments; + } + console.log(f(), g(), h()); + } + expect: { + function f() { + var arguments; + return typeof arguments; + } + function g() { + return "number"; + } + function h(x) { + var arguments = x; + return typeof arguments; + } + console.log(f(), g(), h()); + } + expect_stdout: "object number undefined" +} + +redefine_arguments_2: { + options = { + evaluate: true, + inline: true, + keep_fargs: false, + reduce_funcs: true, + reduce_vars: true, + side_effects: true, + toplevel: true, + unused: true, + } + input: { + function f() { + var arguments; + return typeof arguments; + } + function g() { + var arguments = 42; + return typeof arguments; + } + function h(x) { + var arguments = x; + return typeof arguments; + } + console.log(f(), g(), h()); + } + expect: { + console.log(function() { + var arguments; + return typeof arguments; + }(), "number", function(x) { + var arguments = x; + return typeof arguments; + }()); + } + expect_stdout: "object number undefined" +} + +redefine_arguments_3: { + options = { + evaluate: true, + inline: true, + keep_fargs: false, + passes: 3, + reduce_funcs: true, + reduce_vars: true, + side_effects: true, + toplevel: true, + unused: true, + } + input: { + function f() { + var arguments; + return typeof arguments; + } + function g() { + var arguments = 42; + return typeof arguments; + } + function h(x) { + var arguments = x; + return typeof arguments; + } + console.log(f(), g(), h()); + } + expect: { + console.log(function() { + var arguments; + return typeof arguments; + }(), "number", function(x) { + var arguments = x; + return typeof arguments; + }()); + } + expect_stdout: "object number undefined" +} + +redefine_farg_1: { + options = { + evaluate: true, + keep_fargs: false, + reduce_funcs: true, + reduce_vars: true, + unused: true, + } + input: { + function f(a) { + var a; + return typeof a; + } + function g(a) { + var a = 42; + return typeof a; + } + function h(a, b) { + var a = b; + return typeof a; + } + console.log(f([]), g([]), h([])); + } + expect: { + function f(a) { + return typeof a; + } + function g(a) { + return "number"; + } + function h(a, b) { + a = b; + return typeof a; + } + console.log(f([]), g([]), h([])); + } + expect_stdout: "object number undefined" +} + +redefine_farg_2: { + options = { + evaluate: true, + inline: true, + keep_fargs: false, + reduce_funcs: true, + reduce_vars: true, + side_effects: true, + toplevel: true, + unused: true, + } + input: { + function f(a) { + var a; + return typeof a; + } + function g(a) { + var a = 42; + return typeof a; + } + function h(a, b) { + var a = b; + return typeof a; + } + console.log(f([]), g([]), h([])); + } + expect: { + console.log(typeof [], "number",function(a, b) { + a = b; + return typeof a; + }()); + } + expect_stdout: "object number undefined" +} + +redefine_farg_3: { + options = { + collapse_vars: true, + evaluate: true, + inline: true, + keep_fargs: false, + passes: 3, + reduce_funcs: true, + reduce_vars: true, + sequences: true, + side_effects: true, + toplevel: true, + unused: true, + } + input: { + function f(a) { + var a; + return typeof a; + } + function g(a) { + var a = 42; + return typeof a; + } + function h(a, b) { + var a = b; + return typeof a; + } + console.log(f([]), g([]), h([])); + } + expect: { + console.log(typeof [], "number", "undefined"); + } + expect_stdout: "object number undefined" +} + +delay_def: { + options = { + evaluate: true, + reduce_funcs: true, + reduce_vars: true, + side_effects: true, + unused: true, + } + input: { + function f() { + return a; + var a; + } + function g() { + return a; + var a = 1; + } + console.log(f(), g()); + } + expect: { + function f() { + return; + } + function g() { + return; + } + console.log(f(), g()); + } + expect_stdout: true +} + +delay_def_lhs: { + options = { + evaluate: true, + reduce_vars: true, + } + input: { + console.log(function() { + long_name++; + return long_name; + var long_name; + }()); + } + expect: { + console.log(function() { + long_name++; + return NaN; + var long_name; + }()); + } + expect_stdout: "NaN" +} + +booleans: { + options = { + booleans: true, + evaluate: true, + reduce_funcs: true, + reduce_vars: true, + } + input: { + console.log(function(a) { + if (a != 0); + switch (a) { + case 0: + return "FAIL"; + case false: + return "PASS"; + } + }(false)); + } + expect: { + console.log(function(a) { + if (0); + switch (!1) { + case 0: + return "FAIL"; + case !1: + return "PASS"; + } + }(!1)); + } + expect_stdout: "PASS" +} + +side_effects_assign: { + options = { + evaluate: true, + reduce_funcs: true, + reduce_vars: true, + sequences: true, + side_effects: true, + toplevel: true, + } + input: { + var a = typeof void (a && a.in == 1, 0); + console.log(a); + } + expect: { + var a = "undefined"; + console.log(a); + } + expect_stdout: "undefined" +} + +pure_getters_1: { + options = { + pure_getters: "strict", + reduce_funcs: true, + reduce_vars: true, + side_effects: true, + toplevel: true, + } + input: { + try { + var a = (a.b, 2); + } catch (e) {} + console.log(a); + } + expect: { + try { + var a = (a.b, 2); + } catch (e) {} + console.log(a); + } + expect_stdout: "undefined" +} + +pure_getters_2: { + options = { + pure_getters: "strict", + reduce_funcs: true, + reduce_vars: true, + toplevel: true, + unused: true, + } + input: { + var a; + var a = a && a.b; + } + expect: { + var a; + a && a.b; + } +} + +pure_getters_3: { + options = { + pure_getters: true, + reduce_funcs: true, + reduce_vars: true, + toplevel: true, + unused: true, + } + input: { + var a; + var a = a && a.b; + } + expect: { + } +} + +catch_var: { + options = { + booleans: true, + evaluate: true, + reduce_funcs: true, + reduce_vars: true, + } + input: { + try { + throw {}; + } catch (e) { + var e; + console.log(!!e); + } + } + expect: { + try { + throw {}; + } catch (e) { + var e; + console.log(!!e); + } + } + expect_stdout: "true" +} + +var_assign_1: { + options = { + evaluate: true, + reduce_funcs: true, + reduce_vars: true, + sequences: true, + side_effects: true, + unused: true, + } + input: { + !function() { + var a; + a = 2; + console.log(a); + }(); + } + expect: { + !function() { + console.log(2); + }(); + } + expect_stdout: "2" +} + +var_assign_2: { + options = { + evaluate: true, + reduce_funcs: true, + reduce_vars: true, + sequences: true, + side_effects: true, + unused: true, + } + input: { + !function() { + var a; + if (a = 2) console.log(a); + }(); + } + expect: { + !function() { + if (2) console.log(2); + }(); + } + expect_stdout: "2" +} + +var_assign_3: { + options = { + evaluate: true, + reduce_funcs: true, + reduce_vars: true, + sequences: true, + side_effects: true, + unused: true, + } + input: { + !function() { + var a; + while (a = 2); + console.log(a); + }(); + } + expect: { + !function() { + var a; + while (a = 2); + console.log(a); + }(); + } +} + +var_assign_4: { + options = { + evaluate: true, + reduce_funcs: true, + reduce_vars: true, + sequences: true, + side_effects: true, + unused: true, + } + input: { + !function a() { + a = 2; + console.log(a); + }(); + } + expect: { + !function a() { + a = 2, + console.log(a); + }(); + } +} + +var_assign_5: { + options = { + evaluate: true, + reduce_funcs: true, + reduce_vars: true, + sequences: true, + side_effects: true, + unused: true, + } + input: { + !function() { + var a; + !function(b) { + a = 2; + console.log(a, b); + }(a); + }(); + } + expect: { + !function() { + !function(b) { + console.log(2, void 0); + }(); + }(); + } + expect_stdout: "2 undefined" +} + +var_assign_6: { + options = { + evaluate: true, + reduce_funcs: true, + reduce_vars: true, + unused: true, + } + input: { + !function() { + var a = function(){}(a = 1); + console.log(a); + }(); + } + expect: { + !function() { + (function(){}()); + console.log(void 0); + }(); + } + expect_stdout: "undefined" +} + +immutable: { + options = { + evaluate: true, + reduce_funcs: true, + reduce_vars: true, + unused: true, + } + input: { + !function() { + var a = "test"; + console.log(a.indexOf("e")); + }(); + } + expect: { + !function() { + console.log("test".indexOf("e")); + }(); + } + expect_stdout: "1" +} + +issue_1814_1: { + options = { + evaluate: true, + reduce_funcs: true, + reduce_vars: true, + toplevel: true, + unused: true, + } + input: { + var a = 42; + !function() { + var b = a; + !function(a) { + console.log(a++, b); + }(0); + }(); + } + expect: { + !function() { + !function(a) { + console.log(0, 42); + }(); + }(); + } + expect_stdout: "0 42" +} + +issue_1814_2: { + options = { + evaluate: true, + reduce_funcs: true, + reduce_vars: true, + toplevel: true, + unused: true, + } + input: { + var a = "32"; + !function() { + var b = a + 1; + !function(a) { + console.log(b, a++); + }(0); + }(); + } + expect: { + !function() { + !function(a) { + console.log("321", 0); + }(); + }(); + } + expect_stdout: "321 0" +} + +try_abort: { + options = { + evaluate: true, + reduce_funcs: true, + reduce_vars: true, + unused: true, + } + input: { + !function() { + try { + var a = 1; + throw ""; + var b = 2; + } catch (e) { + } + console.log(a, b); + }(); + } + expect: { + !function() { + try { + var a = 1; + throw ""; + var b = 2; + } catch (e) { + } + console.log(a, b); + }(); + } + expect_stdout: "1 undefined" +} + +boolean_binary_assign: { + options = { + evaluate: true, + reduce_funcs: true, + reduce_vars: true, + unused: true, + } + input: { + !function() { + var a; + void 0 && (a = 1); + console.log(a); + }(); + } + expect: { + !function() { + var a; + void 0; + console.log(a); + }(); + } + expect_stdout: "undefined" +} + +cond_assign: { + options = { + evaluate: true, + reduce_funcs: true, + reduce_vars: true, + unused: true, + } + input: { + !function() { + var a; + void 0 ? (a = 1) : 0; + console.log(a); + }(); + } + expect: { + !function() { + var a; + void 0 ? (a = 1) : 0; + console.log(a); + }(); + } + expect_stdout: "undefined" +} + +iife_assign: { + options = { + evaluate: true, + reduce_funcs: true, + reduce_vars: true, + unused: true, + } + input: { + !function() { + var a = 1, b = 0; + !function() { + b++; + return; + a = 2; + }(); + console.log(a); + }(); + } + expect: { + !function() { + var a = 1, b = 0; + !function() { + b++; + return; + a = 2; + }(); + console.log(a); + }(); + } + expect_stdout: "1" +} + +issue_1850_1: { + options = { + reduce_funcs: true, + reduce_vars: true, + toplevel: false, + unused: true, + } + input: { + function f() { + console.log(a, a, a); + } + var a = 1; + f(); + } + expect: { + function f() { + console.log(a, a, a); + } + var a = 1; + f(); + } + expect_stdout: true +} + +issue_1850_2: { + options = { + reduce_funcs: true, + reduce_vars: true, + toplevel: "funcs", + unused: true, + } + input: { + function f() { + console.log(a, a, a); + } + var a = 1; + f(); + } + expect: { + var a = 1; + (function() { + console.log(a, a, a); + })(); + } + expect_stdout: true +} + +issue_1850_3: { + options = { + reduce_funcs: true, + reduce_vars: true, + toplevel: "vars", + unused: true, + } + input: { + function f() { + console.log(a, a, a); + } + var a = 1; + f(); + } + expect: { + function f() { + console.log(a, a, a); + } + var a = 1; + f(); + } + expect_stdout: true +} + +issue_1850_4: { + options = { + reduce_funcs: true, + reduce_vars: true, + toplevel: true, + unused: true, + } + input: { + function f() { + console.log(a, a, a); + } + var a = 1; + f(); + } + expect: { + var a = 1; + (function() { + console.log(a, a, a); + })(); + } + expect_stdout: true +} + +issue_1865: { + options = { + evaluate: true, + reduce_funcs: true, + reduce_vars: true, + unsafe: true, + } + input: { + function f(some) { + some.thing = false; + } + console.log(function() { + var some = { thing: true }; + f(some); + return some.thing; + }()); + } + expect: { + function f(some) { + some.thing = false; + } + console.log(function() { + var some = { thing: true }; + f(some); + return some.thing; + }()); + } + expect_stdout: true +} + +issue_1922_1: { + options = { + evaluate: true, + reduce_funcs: true, + reduce_vars: true, + unused: true, + } + input: { + console.log(function(a) { + arguments[0] = 2; + return a; + }(1)); + } + expect: { + console.log(function(a) { + arguments[0] = 2; + return a; + }(1)); + } + expect_stdout: "2" +} + +issue_1922_2: { + options = { + evaluate: true, + reduce_funcs: true, + reduce_vars: true, + unused: true, + } + input: { + console.log(function() { + var a; + eval("a = 1"); + return a; + }(1)); + } + expect: { + console.log(function() { + var a; + eval("a = 1"); + return a; + }(1)); + } + expect_stdout: "1" +} + +accessor_1: { + options = { + evaluate: true, + reduce_funcs: true, + reduce_vars: true, + toplevel: true, + } + input: { + var a = 1; + console.log({ + get a() { + a = 2; + return a; + }, + b: 1, + }.b, a); + } + expect: { + var a = 1; + console.log({ + get a() { + a = 2; + return a; + }, + b: 1, + }.b, a); + } + expect_stdout: "1 1" +} + +accessor_2: { + options = { + collapse_vars: true, + evaluate: true, + reduce_funcs: true, + reduce_vars: true, + toplevel: true, + unused: true, + } + input: { + var A = 1; + var B = { + get c() { + console.log(A); + }, + }; + B.c; + } + expect: { + ({ + get c() { + console.log(1); + }, + }).c; + } + expect_stdout: "1" +} + +for_in_prop: { + options = { + reduce_funcs: true, + reduce_vars: true, + } + input: { + var a = { + foo: function() { + for (this.b in [1, 2]); + } + }; + a.foo(); + console.log(a.b); + } + expect: { + var a = { + foo: function() { + for (this.b in [1, 2]); + } + }; + a.foo(); + console.log(a.b); + } + expect_stdout: "1" +} + +obj_var_1: { + options = { + evaluate: true, + passes: 2, + reduce_funcs: true, + reduce_vars: true, + toplevel: true, + unused: true, + } + input: { + var C = 1; + var obj = { + bar: function() { + return C + C; + }, + }; + console.log(obj.bar()); + } + expect: { + console.log({ + bar: function() { + return 2; + }, + }.bar()); + } + expect_stdout: "2" +} + +obj_var_2: { + options = { + evaluate: true, + inline: true, + passes: 2, + properties: true, + reduce_funcs: true, + reduce_vars: true, + side_effects: true, + toplevel: true, + unsafe: true, + unused: true, + } + input: { + var C = 1; + var obj = { + bar: function() { + return C + C; + }, + }; + console.log(obj.bar()); + } + expect: { + console.log(2); + } + expect_stdout: "2" +} + +obj_arg_1: { + options = { + collapse_vars: true, + evaluate: true, + inline: true, + passes: 2, + reduce_funcs: true, + reduce_vars: true, + toplevel: true, + unused: true, + } + input: { + var C = 1; + function f(obj) { + return obj.bar(); + } + console.log(f({ + bar: function() { + return C + C; + } + })); + } + expect: { + console.log({ + bar: function() { + return 2; + } + }.bar()); + } + expect_stdout: "2" +} + +obj_arg_2: { + options = { + collapse_vars: true, + evaluate: true, + inline: true, + passes: 3, + properties: true, + reduce_funcs: true, + reduce_vars: true, + side_effects: true, + toplevel: true, + unused: true, + } + input: { + var C = 1; + function f(obj) { + return obj.bar(); + } + console.log(f({ + bar: function() { + return C + C; + } + })); + } + expect: { + console.log(2); + } + expect_stdout: "2" +} + +func_arg_1: { + options = { + evaluate: true, + inline: true, + passes: 2, + reduce_funcs: true, + reduce_vars: true, + side_effects: true, + toplevel: true, + unused: true, + } + input: { + var a = 42; + !function(a) { + console.log(a()); + }(function() { + return a; + }); + } + expect: { + console.log(42); + } + expect_stdout: "42" +} + +func_arg_2: { + options = { + evaluate: true, + inline: true, + passes: 2, + reduce_funcs: true, + reduce_vars: true, + side_effects: true, + toplevel: true, + unused: true, + } + input: { + var a = 42; + !function(a) { + console.log(a()); + }(function(a) { + return a; + }); + } + expect: { + console.log(void 0); + } + expect_stdout: "undefined" +} + +regex_loop: { + options = { + reduce_funcs: true, + reduce_vars: true, + toplevel: true, + unused: true, + } + input: { + function f(x) { + for (var r, s = "acdabcdeabbb"; r = x().exec(s);) + console.log(r[0]); + } + var a = /ab*/g; + f(function() { + return a; + }); + } + expect: { + var a = /ab*/g; + (function(x) { + for (var r, s = "acdabcdeabbb"; r = x().exec(s);) + console.log(r[0]); + })(function() { + return a; + }); + } + expect_stdout: true +} + +obj_for_1: { + options = { + reduce_funcs: true, + reduce_vars: true, + toplevel: true, + unused: true, + } + input: { + var o = { a: 1 }; + for (var i = o.a--; i; i--) + console.log(i); + } + expect: { + for (var i = { a: 1 }.a--; i; i--) + console.log(i); + } + expect_stdout: "1" +} + +obj_for_2: { + options = { + reduce_funcs: true, + reduce_vars: true, + toplevel: true, + unused: true, + } + input: { + var o = { a: 1 }; + for (var i; i = o.a--;) + console.log(i); + } + expect: { + var o = { a: 1 }; + for (var i; i = o.a--;) + console.log(i); + } + expect_stdout: "1" +} + +array_forin_1: { + options = { + reduce_funcs: true, + reduce_vars: true, + toplevel: true, + unused: true, + } + input: { + var a = [ 1, 2, 3 ]; + for (var b in a) + console.log(b); + } + expect: { + for (var b in [ 1, 2, 3 ]) + console.log(b); + } + expect_stdout: [ + "0", + "1", + "2", + ] +} + +array_forin_2: { + options = { + reduce_funcs: true, + reduce_vars: true, + toplevel: true, + unused: true, + } + input: { + var a = []; + for (var b in [ 1, 2, 3 ]) + a.push(b); + console.log(a.length); + } + expect: { + var a = []; + for (var b in [ 1, 2, 3 ]) + a.push(b); + console.log(a.length); + } + expect_stdout: "3" +} + +const_expr_1: { + options = { + evaluate: true, + reduce_funcs: true, + reduce_vars: true, + toplevel: true, + unsafe: true, + unused: true, + } + input: { + var o = { + a: 1, + b: 2 + }; + o.a++; + console.log(o.a, o.b); + } + expect: { + var o = { + a: 1, + b: 2 + }; + o.a++; + console.log(o.a, o.b); + } + expect_stdout: "2 2" +} + +const_expr_2: { + options = { + evaluate: true, + reduce_funcs: true, + reduce_vars: true, + toplevel: true, + unsafe: true, + unused: true, + } + input: { + Object.prototype.c = function() { + this.a++; + }; + var o = { + a: 1, + b: 2 + }; + o.c(); + console.log(o.a, o.b); + } + expect: { + Object.prototype.c = function() { + this.a++; + }; + var o = { + a: 1, + b: 2 + }; + o.c(); + console.log(o.a, o.b); + } + expect_stdout: "2 2" +} + +escaped_prop_1: { + options = { + collapse_vars: true, + evaluate: true, + inline: true, + pure_getters: "strict", + reduce_funcs: true, + reduce_vars: true, + side_effects: true, + toplevel: true, + unsafe: true, + unused: true, + } + input: { + var obj = { o: { a: 1 } }; + (function(o) { + o.a++; + })(obj.o); + (function(o) { + console.log(o.a); + })(obj.o); + } + expect: { + var obj = { o: { a: 1 } }; + obj.o.a++; + console.log(obj.o.a); + } + expect_stdout: "2" +} + +escaped_prop_2: { + options = { + collapse_vars: true, + evaluate: true, + inline: true, + passes: 2, + pure_getters: "strict", + reduce_funcs: true, + reduce_vars: true, + side_effects: true, + toplevel: true, + unsafe: true, + unused: true, + } + input: { + var obj = { o: { a: 1 } }; + (function(o) { + o.a++; + })(obj.o); + (function(o) { + console.log(o.a); + })(obj.o); + } + expect: { + var obj = { o: { a: 1 } }; + obj.o.a++; + console.log(obj.o.a); + } + expect_stdout: "2" +} + +escaped_prop_3: { + options = { + reduce_funcs: true, + reduce_vars: true, + toplevel: true, + unused: true, + } + input: { + var a; + function f(b) { + if (a) console.log(a === b.c); + a = b.c; + } + function g() {} + function h() { + f({ c: g }); + } + h(); + h(); + } + expect: { + var a; + function g() {} + function h() { + (function(b) { + if (a) console.log(a === b.c); + a = b.c; + })({ c: g }); + } + h(); + h(); + } + expect_stdout: "true" +} + +issue_2420_1: { + options = { + reduce_funcs: true, + reduce_vars: true, + unused: true, + } + input: { + function run() { + var self = this; + if (self.count++) + self.foo(); + else + self.bar(); + } + var o = { + count: 0, + foo: function() { console.log("foo"); }, + bar: function() { console.log("bar"); }, + }; + run.call(o); + run.call(o); + } + expect: { + function run() { + if (this.count++) + this.foo(); + else + this.bar(); + } + var o = { + count: 0, + foo: function() { console.log("foo"); }, + bar: function() { console.log("bar"); }, + }; + run.call(o); + run.call(o); + } + expect_stdout: [ + "bar", + "foo", + ] +} + +issue_2420_2: { + options = { + reduce_funcs: true, + reduce_vars: true, + unused: true, + } + input: { + function f() { + var that = this; + if (that.bar) + that.foo(); + else + !function(that, self) { + console.log(this === that, self === this, that === self); + }(that, this); + } + f.call({ + bar: 1, + foo: function() { console.log("foo", this.bar); }, + }); + f.call({}); + } + expect: { + function f() { + if (this.bar) + this.foo(); + else + !function(that, self) { + console.log(this === that, self === this, that === self); + }(this, this); + } + f.call({ + bar: 1, + foo: function() { console.log("foo", this.bar); }, + }); + f.call({}); + } + expect_stdout: [ + "foo 1", + "false false true", + ] +} + +issue_2423_1: { + options = { + reduce_funcs: true, + reduce_vars: true, + toplevel: true, + unused: true, + } + input: { + function c() { return 1; } + function p() { console.log(c()); } + p(); + p(); + } + expect: { + function p() { console.log(function() { return 1; }()); } + p(); + p(); + } + expect_stdout: [ + "1", + "1", + ] +} + +issue_2423_2: { + options = { + inline: true, + reduce_funcs: true, + reduce_vars: true, + toplevel: true, + unused: true, + } + input: { + function c() { return 1; } + function p() { console.log(c()); } + p(); + p(); + } + expect: { + function p() { console.log(1); } + p(); + p(); + } + expect_stdout: [ + "1", + "1", + ] +} + +issue_2423_3: { + options = { + reduce_funcs: true, + reduce_vars: true, + toplevel: true, + unused: true, + } + input: { + function c() { return 1; } + function p() { console.log(c()); } + p(); + } + expect: { + (function() { console.log(function() { return 1; }()); })(); + } + expect_stdout: "1" +} + +issue_2423_4: { + options = { + inline: true, + reduce_funcs: true, + reduce_vars: true, + side_effects: true, + toplevel: true, + unused: true, + } + input: { + function c() { return 1; } + function p() { console.log(c()); } + p(); + } + expect: { + console.log(1); + } + expect_stdout: "1" +} + +issue_2423_5: { + options = { + inline: true, + passes: 2, + reduce_funcs: true, + reduce_vars: true, + side_effects: true, + toplevel: true, + unused: true, + } + input: { + function x() { + y(); + } + function y() { + console.log(1); + } + function z() { + function y() { + console.log(2); + } + x(); + } + z(); + z(); + } + expect: { + function z() { + console.log(1); + } + z(); + z(); + } + expect_stdout: [ + "1", + "1", + ] +} + +issue_2423_6: { + options = { + inline: true, + passes: 2, + reduce_funcs: true, + reduce_vars: true, + side_effects: true, + toplevel: true, + unused: true, + } + input: { + function x() { + y(); + } + function y() { + console.log(1); + } + function z() { + function y() { + console.log(2); + } + x(); + y(); + } + z(); + z(); + } + expect: { + function z(){ + console.log(1); + console.log(2); + } + z(); + z(); + } + expect_stdout: [ + "1", + "2", + "1", + "2", + ] +} + +issue_2440_eval_1: { + options = { + reduce_funcs: true, + reduce_vars: true, + toplevel: true, + unused: true, + } + input: { + function foo() { + return bar(); + } + baz = { + quux: foo + }; + exec = function() { + return eval("foo()"); + }; + } + expect: { + function foo() { + return bar(); + } + baz = { + quux: foo + }; + exec = function() { + return eval("foo()"); + }; + } +} + +issue_2440_eval_2: { + options = { + reduce_funcs: true, + reduce_vars: true, + toplevel: true, + unused: true, + } + input: { + baz = { + quux: foo + }; + exec = function() { + return eval("foo()"); + }; + function foo() { + return bar(); + } + } + expect: { + baz = { + quux: foo + }; + exec = function() { + return eval("foo()"); + }; + function foo() { + return bar(); + } + } +} + +issue_2440_with_1: { + options = { + reduce_funcs: true, + reduce_vars: true, + toplevel: true, + unused: true, + } + input: { + function foo() { + return bar(); + } + baz = { + quux: foo + }; + with (o) whatever(); + } + expect: { + function foo() { + return bar(); + } + baz = { + quux: foo + }; + with (o) whatever(); + } +} + +issue_2440_with_2: { + options = { + reduce_funcs: true, + reduce_vars: true, + toplevel: true, + unused: true, + } + input: { + baz = { + quux: foo + }; + with (o) whatever(); + function foo() { + return bar(); + } + } + expect: { + baz = { + quux: foo + }; + with (o) whatever(); + function foo() { + return bar(); + } + } +} + +issue_2442: { + options = { + reduce_funcs: true, + reduce_vars: true, + toplevel: true, + unused: true, + } + input: { + function foo() { + foo(); + } + } + expect: {} +} + +recursive_inlining_1: { + options = { + reduce_funcs: true, + reduce_vars: true, + unused: true, + } + input: { + !function() { + function foo() { bar(); } + function bar() { foo(); } + console.log("PASS"); + }(); + } + expect: { + !function() { + console.log("PASS"); + }(); + } + expect_stdout: "PASS" +} + +recursive_inlining_2: { + options = { + reduce_funcs: true, + reduce_vars: true, + unused: true, + } + input: { + !function() { + function foo() { qux(); } + function bar() { foo(); } + function qux() { bar(); } + console.log("PASS"); + }(); + } + expect: { + !function() { + console.log("PASS"); + }(); + } + expect_stdout: "PASS" +} + +recursive_inlining_3: { + options = { + passes: 2, + reduce_funcs: true, + reduce_vars: true, + unused: true, + } + input: { + !function() { + function foo(x) { console.log("foo", x); if (x) bar(x-1); } + function bar(x) { console.log("bar", x); if (x) qux(x-1); } + function qux(x) { console.log("qux", x); if (x) foo(x-1); } + qux(4); + }(); + } + expect: { + !function() { + function qux(x) { + console.log("qux", x); + if (x) (function(x) { + console.log("foo", x); + if (x) (function(x) { + console.log("bar", x); + if (x) qux(x - 1); + })(x - 1); + })(x - 1); + } + qux(4); + }(); + } + expect_stdout: [ + "qux 4", + "foo 3", + "bar 2", + "qux 1", + "foo 0", + ] +} + +recursive_inlining_4: { + options = { + reduce_funcs: true, + reduce_vars: true, + unused: true, + } + input: { + !function() { + function foo(x) { console.log("foo", x); if (x) bar(x-1); } + function bar(x) { console.log("bar", x); if (x) qux(x-1); } + function qux(x) { console.log("qux", x); if (x) foo(x-1); } + qux(4); + bar(5); + }(); + } + expect: { + !function() { + function bar(x) { + console.log("bar", x); + if (x) qux(x - 1); + } + function qux(x) { + console.log("qux", x); + if (x) (function(x) { + console.log("foo", x); + if (x) bar(x - 1); + })(x - 1); + } + qux(4); + bar(5); + }(); + } + expect_stdout: [ + "qux 4", + "foo 3", + "bar 2", + "qux 1", + "foo 0", + "bar 5", + "qux 4", + "foo 3", + "bar 2", + "qux 1", + "foo 0", + ] +} + +recursive_inlining_5: { + options = { + reduce_funcs: true, + reduce_vars: true, + unused: true, + } + input: { + !function() { + function foo(x) { console.log("foo", x); if (x) bar(x-1); } + function bar(x) { console.log("bar", x); if (x) qux(x-1); } + function qux(x) { console.log("qux", x); if (x) foo(x-1); } + qux(4); + bar(5); + foo(3); + }(); + } + expect: { + !function() { + function foo(x) { + console.log("foo", x); + if (x) bar(x - 1); + } + function bar(x) { + console.log("bar", x); + if (x) qux(x - 1); + } + function qux(x) { + console.log("qux", x); + if (x) foo(x - 1); + } + qux(4); + bar(5); + foo(3); + }(); + } + expect_stdout: [ + "qux 4", + "foo 3", + "bar 2", + "qux 1", + "foo 0", + "bar 5", + "qux 4", + "foo 3", + "bar 2", + "qux 1", + "foo 0", + "foo 3", + "bar 2", + "qux 1", + "foo 0", + ] +} + +issue_2450_1: { + options = { + reduce_funcs: true, + reduce_vars: true, + toplevel: true, + unused: true, + } + input: { + function f() {} + function g() { + return f; + } + console.log(g() === g()); + } + expect: { + function f() {} + function g() { + return f; + } + console.log(g() === g()); + } + expect_stdout: "true" +} + +issue_2450_2: { + options = { + reduce_funcs: true, + reduce_vars: true, + toplevel: true, + unused: true, + } + input: { + function g() { + function f() {} + return f; + } + console.log(g() === g()); + } + expect: { + function g() { + return function() {}; + } + console.log(g() === g()); + } + expect_stdout: "false" +} + +issue_2450_3: { + options = { + reduce_funcs: true, + reduce_vars: true, + unused: true, + } + input: { + var x = (function() { + function test() { + return "foo"; + } + return function b() { + return [1, test]; + } + })(); + console.log(x()[1] === x()[1]); + } + expect: { + var x = (function() { + function test() { + return "foo"; + } + return function() { + return [1, test]; + } + })(); + console.log(x()[1] === x()[1]); + } + expect_stdout: "true" +} + +issue_2450_4: { + options = { + reduce_funcs: true, + reduce_vars: true, + toplevel: true, + unused: true, + } + input: { + var a; + function f(b) { + console.log(a === b); + a = b; + } + function g() {} + for (var i = 3; --i >= 0;) + f(g); + } + expect: { + var a; + function g() {} + for (var i = 3; --i >= 0;) + (function(b) { + console.log(a === b); + a = b; + })(g); + } + expect_stdout: [ + "false", + "true", + "true", + ] +} + +issue_2450_5: { + options = { + reduce_funcs: true, + reduce_vars: true, + toplevel: true, + unused: true, + } + input: { + var a; + function f(b) { + console.log(a === b); + a = b; + } + function g() {} + [1, 2, 3].forEach(function() { + f(g); + }); + } + expect: { + var a; + function g() {} + [1, 2, 3].forEach(function() { + (function(b) { + console.log(a === b); + a = b; + })(g); + }); + } + expect_stdout: [ + "false", + "true", + "true", + ] +} + +issue_2449: { + options = { + passes: 10, + reduce_funcs: true, + reduce_vars: true, + toplevel: true, + unused: true, + } + input: { + var a = "PASS"; + function f() { + return a; + } + function g() { + return f(); + } + (function() { + var a = "FAIL"; + if (a == a) console.log(g()); + })(); + } + expect: { + var a = "PASS"; + function g() { + return function() { + return a; + }(); + } + (function() { + var a = "FAIL"; + if (a == a) console.log(g()); + })(); + } + expect_stdout: "PASS" +} + +perf_1: { + options = { + reduce_funcs: true, + reduce_vars: true, + toplevel: true, + unused: true, + } + input: { + function foo(x, y, z) { + return x < y ? x * y + z : x * z - y; + } + function indirect_foo(x, y, z) { + return foo(x, y, z); + } + var sum = 0; + for (var i = 0; i < 100; ++i) { + sum += indirect_foo(i, i + 1, 3 * i); + } + console.log(sum); + } + expect: { + var sum = 0; + for (var i = 0; i < 100; ++i) + sum += function(x, y, z) { + return function(x, y, z) { + return x < y ? x * y + z : x * z - y; + }(x, y, z); + }(i, i + 1, 3 * i); + console.log(sum); + } + expect_stdout: "348150" +} + +perf_2: { + options = { + reduce_funcs: false, + reduce_vars: true, + toplevel: true, + unused: true, + } + input: { + function foo(x, y, z) { + return x < y ? x * y + z : x * z - y; + } + function indirect_foo(x, y, z) { + return foo(x, y, z); + } + var sum = 0; + for (var i = 0; i < 100; ++i) { + sum += indirect_foo(i, i + 1, 3 * i); + } + console.log(sum); + } + expect: { + function foo(x, y, z) { + return x < y ? x * y + z : x * z - y; + } + function indirect_foo(x, y, z) { + return foo(x, y, z); + } + var sum = 0; + for (var i = 0; i < 100; ++i) + sum += indirect_foo(i, i + 1, 3 * i); + console.log(sum); + } + expect_stdout: "348150" +} + +perf_3: { + options = { + passes: 2, + reduce_funcs: true, + reduce_vars: true, + toplevel: true, + unused: true, + } + input: { + var foo = function(x, y, z) { + return x < y ? x * y + z : x * z - y; + }; + var indirect_foo = function(x, y, z) { + return foo(x, y, z); + }; + var sum = 0; + for (var i = 0; i < 100; ++i) + sum += indirect_foo(i, i + 1, 3 * i); + console.log(sum); + } + expect: { + var sum = 0; + for (var i = 0; i < 100; ++i) + sum += function(x, y, z) { + return function(x, y, z) { + return x < y ? x * y + z : x * z - y; + }(x, y, z); + }(i, i + 1, 3 * i); + console.log(sum); + } + expect_stdout: "348150" +} + +perf_4: { + options = { + reduce_funcs: false, + reduce_vars: true, + toplevel: true, + unused: true, + } + input: { + var foo = function(x, y, z) { + return x < y ? x * y + z : x * z - y; + }; + var indirect_foo = function(x, y, z) { + return foo(x, y, z); + }; + var sum = 0; + for (var i = 0; i < 100; ++i) + sum += indirect_foo(i, i + 1, 3 * i); + console.log(sum); + } + expect: { + var foo = function(x, y, z) { + return x < y ? x * y + z : x * z - y; + }; + var indirect_foo = function(x, y, z) { + return foo(x, y, z); + }; + var sum = 0; + for (var i = 0; i < 100; ++i) + sum += indirect_foo(i, i + 1, 3 * i); + console.log(sum); + } + expect_stdout: "348150" +} + +perf_5: { + options = { + passes: 10, + reduce_funcs: true, + reduce_vars: true, + toplevel: true, + unused: true, + } + input: { + function indirect_foo(x, y, z) { + function foo(x, y, z) { + return x < y ? x * y + z : x * z - y; + } + return foo(x, y, z); + } + var sum = 0; + for (var i = 0; i < 100; ++i) { + sum += indirect_foo(i, i + 1, 3 * i); + } + console.log(sum); + } + expect: { + var sum = 0; + for (var i = 0; i < 100; ++i) + sum += function(x, y, z) { + return function(x, y, z) { + return x < y ? x * y + z : x * z - y; + }(x, y, z); + }(i, i + 1, 3 * i); + console.log(sum); + } + expect_stdout: "348150" +} + +perf_6: { + options = { + passes: 10, + reduce_funcs: false, + reduce_vars: true, + toplevel: true, + unused: true, + } + input: { + function indirect_foo(x, y, z) { + function foo(x, y, z) { + return x < y ? x * y + z : x * z - y; + } + return foo(x, y, z); + } + var sum = 0; + for (var i = 0; i < 100; ++i) { + sum += indirect_foo(i, i + 1, 3 * i); + } + console.log(sum); + } + expect: { + function indirect_foo(x, y, z) { + return function(x, y, z) { + return x < y ? x * y + z : x * z - y; + }(x, y, z); + } + var sum = 0; + for (var i = 0; i < 100; ++i) + sum += indirect_foo(i, i + 1, 3 * i); + console.log(sum); + } + expect_stdout: "348150" +} + +perf_7: { + options = { + reduce_funcs: true, + reduce_vars: true, + toplevel: true, + unused: true, + } + input: { + var indirect_foo = function(x, y, z) { + var foo = function(x, y, z) { + return x < y ? x * y + z : x * z - y; + }; + return foo(x, y, z); + }; + var sum = 0; + for (var i = 0; i < 100; ++i) + sum += indirect_foo(i, i + 1, 3 * i); + console.log(sum); + } + expect: { + var sum = 0; + for (var i = 0; i < 100; ++i) + sum += function(x, y, z) { + return function(x, y, z) { + return x < y ? x * y + z : x * z - y; + }(x, y, z); + }(i, i + 1, 3 * i); + console.log(sum); + } + expect_stdout: "348150" +} + +perf_8: { + options = { + reduce_funcs: false, + reduce_vars: true, + toplevel: true, + unused: true, + } + input: { + var indirect_foo = function(x, y, z) { + var foo = function(x, y, z) { + return x < y ? x * y + z : x * z - y; + }; + return foo(x, y, z); + }; + var sum = 0; + for (var i = 0; i < 100; ++i) + sum += indirect_foo(i, i + 1, 3 * i); + console.log(sum); + } + expect: { + var indirect_foo = function(x, y, z) { + return function(x, y, z) { + return x < y ? x * y + z : x * z - y; + }(x, y, z); + }; + var sum = 0; + for (var i = 0; i < 100; ++i) + sum += indirect_foo(i, i + 1, 3 * i); + console.log(sum); + } + expect_stdout: "348150" +} + +issue_2485_1: { + options = { + reduce_funcs: true, + reduce_vars: true, + unused: true, + } + input: { + var foo = function(bar) { + var n = function(a, b) { + return a + b; + }; + var sumAll = function(arg) { + return arg.reduce(n, 0); + }; + var runSumAll = function(arg) { + return sumAll(arg); + }; + bar.baz = function(arg) { + var n = runSumAll(arg); + return (n.get = 1), n; + }; + return bar; + }; + var bar = foo({}); + console.log(bar.baz([1, 2, 3])); + } + expect: { + var foo = function(bar) { + var n = function(a, b) { + return a + b; + }; + var runSumAll = function(arg) { + return function(arg) { + return arg.reduce(n, 0); + }(arg); + }; + bar.baz = function(arg) { + var n = runSumAll(arg); + return (n.get = 1), n; + }; + return bar; + }; + var bar = foo({}); + console.log(bar.baz([1, 2, 3])); + } + expect_stdout: "6" +} + +issue_2485_2: { + options = { + inline: true, + reduce_funcs: true, + reduce_vars: true, + unused: true, + } + input: { + var foo = function(bar) { + var n = function(a, b) { + return a + b; + }; + var sumAll = function(arg) { + return arg.reduce(n, 0); + }; + var runSumAll = function(arg) { + return sumAll(arg); + }; + bar.baz = function(arg) { + var n = runSumAll(arg); + return (n.get = 1), n; + }; + return bar; + }; + var bar = foo({}); + console.log(bar.baz([1, 2, 3])); + } + expect: { + var foo = function(bar) { + var n = function(a, b) { + return a + b; + }; + var runSumAll = function(arg) { + return arg.reduce(n, 0); + }; + bar.baz = function(arg) { + var n = runSumAll(arg); + return (n.get = 1), n; + }; + return bar; + }; + var bar = foo({}); + console.log(bar.baz([1, 2, 3])); + } + expect_stdout: "6" +} + +issue_2455: { + options = { + reduce_vars: true, + unused: true, + } + input: { + function foo() { + var that = this; + for (;;) that.bar(); + } + } + expect: { + function foo() { + for (;;) this.bar(); + } + } +} + +escape_conditional: { + options = { + reduce_funcs: true, + reduce_vars: true, + toplevel: true, + unused: true, + } + input: { + function main() { + var thing = baz(); + if (thing !== (thing = baz())) + console.log("FAIL"); + else + console.log("PASS"); + } + function baz(s) { + return s ? foo : bar; + } + function foo() {} + function bar() {} + main(); + } + expect: { + function baz(s) { + return s ? foo : bar; + } + function foo() {} + function bar() {} + (function() { + var thing = baz(); + if (thing !== baz()) + console.log("FAIL"); + else + console.log("PASS"); + })(); + } + expect_stdout: "PASS" +} + +escape_sequence: { + options = { + reduce_funcs: true, + reduce_vars: true, + toplevel: true, + unused: true, + } + input: { + function main() { + var thing = baz(); + if (thing !== (thing = baz())) + console.log("FAIL"); + else + console.log("PASS"); + } + function baz() { + return foo, bar; + } + function foo() {} + function bar() {} + main(); + } + expect: { + function baz() { + return function() {}, bar; + } + function bar() {} + (function() { + var thing = baz(); + if (thing !== baz()) + console.log("FAIL"); + else + console.log("PASS"); + })(); + } + expect_stdout: "PASS" +} + +escape_throw: { + options = { + reduce_funcs: true, + reduce_vars: true, + toplevel: true, + unused: true, + } + input: { + function main() { + var thing = baz(); + if (thing !== (thing = baz())) + console.log("FAIL"); + else + console.log("PASS"); + } + function baz() { + try { + throw foo; + } catch (bar) { + return bar; + } + } + function foo() {} + main(); + } + expect: { + function baz() { + try { + throw foo; + } catch (bar) { + return bar; + } + } + function foo() {} + (function() { + var thing = baz(); + if (thing !== baz()) + console.log("FAIL"); + else + console.log("PASS"); + })(); + } + expect_stdout: "PASS" +} + +escape_local_conditional: { + options = { + reduce_funcs: true, + reduce_vars: true, + toplevel: true, + unused: true, + } + input: { + function main() { + var thing = baz(); + if (thing !== (thing = baz())) + console.log("PASS"); + else + console.log("FAIL"); + } + function baz(s) { + function foo() {} + function bar() {} + return s ? foo : bar; + } + main(); + } + expect: { + function baz(s) { + return s ? function() {} : function() {}; + } + (function() { + var thing = baz(); + if (thing !== baz()) + console.log("PASS"); + else + console.log("FAIL"); + })(); + } + expect_stdout: "PASS" +} + +escape_local_sequence: { + options = { + reduce_funcs: true, + reduce_vars: true, + toplevel: true, + unused: true, + } + input: { + function main() { + var thing = baz(); + if (thing !== (thing = baz())) + console.log("PASS"); + else + console.log("FAIL"); + } + function baz() { + function foo() {} + function bar() {} + return foo, bar; + } + main(); + } + expect: { + function baz() { + return function() {}, function() {}; + } + (function() { + var thing = baz(); + if (thing !== baz()) + console.log("PASS"); + else + console.log("FAIL"); + })(); + } + expect_stdout: "PASS" +} + +escape_local_throw: { + options = { + reduce_funcs: true, + reduce_vars: true, + toplevel: true, + unused: true, + } + input: { + function main() { + var thing = baz(); + if (thing !== (thing = baz())) + console.log("PASS"); + else + console.log("FAIL"); + } + function baz() { + function foo() {} + try { + throw foo; + } catch (bar) { + return bar; + } + } + main(); + } + expect: { + function baz() { + try { + throw function() {}; + } catch (bar) { + return bar; + } + } + (function() { + var thing = baz(); + if (thing !== baz()) + console.log("PASS"); + else + console.log("FAIL"); + })(); + } + expect_stdout: "PASS" +} + +inverted_var: { + options = { + evaluate: true, + inline: true, + passes: 3, + reduce_vars: true, + side_effects: true, + unused: true, + } + input: { + console.log(function() { + var a = 1; + return a; + }(), function() { + var b; + b = 2; + return b; + }(), function() { + c = 3; + return c; + var c; + }(), function(c) { + c = 4; + return c; + }(), function(c) { + c = 5; + return c; + var c; + }(), function c() { + c = 6; + return c; + }(), function c() { + c = 7; + return c; + var c; + }(), function() { + c = 8; + return c; + var c = "foo"; + }()); + } + expect: { + console.log(1, 2, 3, 4, 5, function c() { + c = 6; + return c; + }(), 7, 8); + } + expect_stdout: true +} + +defun_single_use_loop: { + options = { + reduce_vars: true, + toplevel: true, + unused: true, + } + input: { + for (var x, i = 2; --i >= 0;) { + var y = x; + x = f; + console.log(x === y); + } + function f() {}; + } + expect: { + for (var x, i = 2; --i >= 0;) { + var y = x; + x = f; + console.log(x === y); + } + function f() {}; + } + expect_stdout: [ + "false", + "true", + ] +} + +do_while: { + options = { + evaluate: true, + reduce_vars: true, + } + input: { + function f(a) { + do { + (function() { + a && (c = "PASS"); + })(); + } while (a = 0); + } + var c = "FAIL"; + f(1); + console.log(c); + } + expect: { + function f(a) { + do { + (function() { + a && (c = "PASS"); + })(); + } while (a = 0); + } + var c = "FAIL"; + f(1); + console.log(c); + } + expect_stdout: "PASS" +} + +issue_2598: { + options = { + reduce_funcs: true, + reduce_vars: true, + toplevel: true, + unused: true, + } + input: { + function f() {} + function g(a) { + return a || f; + } + console.log(g(false) === g(null)); + } + expect: { + function f() {} + function g(a) { + return a || f; + } + console.log(g(false) === g(null)); + } + expect_stdout: "true" +} + +var_if: { + options = { + evaluate: true, + reduce_vars: true, + unused: true, + } + input: { + function f() { + if (x()) { + var a; + if (!g) a = true; + if (a) g(); + } + } + } + expect: { + function f() { + if (x()) { + var a; + if (!g) a = true; + if (a) g(); + } + } + } +} + +defun_assign: { + options = { + reduce_vars: true, + toplevel: true, + } + input: { + console.log(typeof a); + a = 42; + console.log(typeof a); + function a() {} + console.log(typeof a); + } + expect: { + console.log(typeof a); + a = 42; + console.log(typeof a); + function a() {} + console.log(typeof a); + } + expect_stdout: [ + "function", + "number", + "number", + ] +} + +defun_var_1: { + options = { + evaluate: true, + reduce_vars: true, + toplevel: true, + typeofs: true, + unused: true, + } + input: { + var a = 42, b; + function a() {} + function b() {} + console.log(typeof a, typeof b); + } + expect: { + console.log("number", "function"); + } + expect_stdout: "number function" +} + +defun_var_2: { + options = { + evaluate: true, + reduce_vars: true, + toplevel: true, + typeofs: true, + unused: true, + } + input: { + function a() {} + function b() {} + var a = 42, b; + console.log(typeof a, typeof b); + } + expect: { + console.log("number", "function"); + } + expect_stdout: "number function" +} + +defun_var_3: { + options = { + evaluate: true, + reduce_vars: true, + toplevel: true, + typeofs: true, + unused: true, + } + input: { + function a() {} + function b() {} + console.log(typeof a, typeof b); + var a = 42, b; + } + expect: { + console.log("function", "function"); + } + expect_stdout: "function function" +} + +defun_catch_1: { + options = { + reduce_vars: true, + toplevel: true, + unused: true, + } + input: { + function a() {} + try { + throw 42; + } catch (a) { + console.log(a); + } + } + expect: { + try { + throw 42; + } catch (a) { + console.log(a); + } + } + expect_stdout: "42" +} + +defun_catch_2: { + options = { + reduce_vars: true, + toplevel: true, + unused: true, + } + input: { + try { + function a() {} + throw 42; + } catch (a) { + console.log(a); + } + } + expect: { + try { + throw 42; + } catch (a) { + console.log(a); + } + } + expect_stdout: "42" +} + +defun_catch_3: { + options = { + reduce_vars: true, + toplevel: true, + unused: true, + } + input: { + try { + throw 42; + function a() {} + } catch (a) { + console.log(a); + } + } + expect: { + try { + throw 42; + } catch (a) { + console.log(a); + } + } + expect_stdout: "42" +} + +defun_catch_4: { + options = { + reduce_vars: true, + toplevel: true, + unused: true, + } + input: { + try { + throw 42; + } catch (a) { + function a() {} + console.log(a); + } + } + expect: { + try { + throw 42; + } catch (a) { + function a() {} + console.log(a); + } + } + expect_stdout: true +} + +defun_catch_5: { + options = { + reduce_vars: true, + toplevel: true, + unused: true, + } + input: { + try { + throw 42; + } catch (a) { + console.log(a); + function a() {} + } + } + expect: { + try { + throw 42; + } catch (a) { + console.log(a); + function a() {} + } + } + expect_stdout: true +} + +defun_catch_6: { + options = { + reduce_vars: true, + toplevel: true, + unused: true, + } + input: { + try { + throw 42; + } catch (a) { + console.log(a); + } + function a() {} + } + expect: { + try { + throw 42; + } catch (a) { + console.log(a); + } + } + expect_stdout: "42" +} + +duplicate_lambda_defun_name_1: { + options = { + reduce_vars: true, + } + input: { + console.log(function f(a) { + function f() {} + return f.length; + }()); + } + expect: { + console.log(function f(a) { + function f() {} + return f.length; + }()); + } + expect_stdout: "0" +} + +duplicate_lambda_defun_name_2: { + options = { + passes: 2, + reduce_vars: true, + unused: true, + } + input: { + console.log(function f(a) { + function f() {} + return f.length; + }()); + } + expect: { + console.log(function(a) { + return function() {}.length; + }()); + } + expect_stdout: "0" +} + +issue_2774: { + options = { + reduce_vars: true, + unused: true, + } + input: { + console.log({ + get a() { + var b; + (b = true) && b.c; + b = void 0; + } + }.a); + } + expect: { + console.log({ + get a() { + var b; + (b = true) && b.c; + void 0; + } + }.a); + } + expect_stdout: "undefined" +} + +issue_2799_1: { + options = { + passes: 2, + reduce_funcs: true, + reduce_vars: true, + unused: true, + } + input: { + console.log(function() { + return f; + function f(n) { + function g(i) { + return i && i + g(i - 1); + } + function h(j) { + return g(j); + } + return h(n); + } + }()(5)); + } + expect: { + console.log(function() { + return function(n) { + return function(j) { + return function g(i) { + return i && i + g(i - 1); + }(j); + }(n); + } + }()(5)); + } + expect_stdout: "15" +} + +issue_2799_2: { + options = { + reduce_vars: true, + unsafe_proto: true, + unused: true, + } + input: { + (function() { + function foo() { + Function.prototype.call.apply(console.log, [ null, "PASS" ]); + } + foo(); + })(); + } + expect: { + (function() { + (function() { + (function() {}).call.apply(console.log, [ null, "PASS" ]); + })(); + })(); + } + expect_stdout: "PASS" +} + +issue_2836: { + options = { + reduce_vars: true, + toplevel: true, + unused: true, + } + input: { + function f() { + return "FAIL"; + } + console.log(f()); + function f() { + return "PASS"; + } + } + expect: { + console.log(function() { + return "PASS"; + }()); + } + expect_stdout: "PASS" +} + +lvalues_def_1: { + options = { + reduce_vars: true, + toplevel: true, + unused: true, + } + input: { + var b = 1; + var a = b++, b = NaN; + console.log(a, b); + } + expect: { + var b = 1; + var a = +b, b = NaN; + console.log(a, b); + } + expect_stdout: "1 NaN" +} + +lvalues_def_2: { + options = { + reduce_vars: true, + toplevel: true, + unused: true, + } + input: { + var b = 1; + var a = b += 1, b = NaN; + console.log(a, b); + } + expect: { + var b = 1; + var a = b += 1, b = NaN; + console.log(a, b); + } + expect_stdout: "2 NaN" +} + +chained_assignments: { + options = { + evaluate: true, + inline: true, + reduce_vars: true, + sequences: true, + side_effects: true, + toplevel: true, + unsafe: true, + unused: true, + } + input: { + function f() { + var a = [0x5e, 0xad, 0xbe, 0xef]; + var b = 0; + b |= a[0]; + b <<= 8; + b |= a[1]; + b <<= 8; + b |= a[2]; + b <<= 8; + b |= a[3]; + return b; + } + console.log(f().toString(16)); + } + expect: { + console.log("5eadbeef"); + } + expect_stdout: "5eadbeef" +} + +issue_2860_1: { + options = { + dead_code: true, + evaluate: true, + reduce_vars: true, + } + input: { + console.log(function(a) { + return a ^= 1; + a ^= 2; + }()); + } + expect: { + console.log(1); + } + expect_stdout: "1" +} + +issue_2860_2: { + options = { + dead_code: true, + evaluate: true, + inline: true, + passes: 2, + reduce_vars: true, + } + input: { + console.log(function(a) { + return a ^= 1; + a ^= 2; + }()); + } + expect: { + console.log(1); + } + expect_stdout: "1" +} + +issue_2869: { + options = { + evaluate: true, + reduce_vars: true, + } + input: { + var c = "FAIL"; + (function f(a) { + var a; + if (!f) a = 0; + if (a) c = "PASS"; + })(1); + console.log(c); + } + expect: { + var c = "FAIL"; + (function f(a) { + var a; + if (!f) a = 0; + if (a) c = "PASS"; + })(1); + console.log(c); + } + expect_stdout: "PASS" +} + +issue_2919: { + options = { + evaluate: true, + reduce_vars: true, + toplevel: true, + unsafe: true, + unused: true, + } + input: { + var arr = [ function() {} ]; + console.log(typeof arr[0]); + } + expect: { + console.log("function"); + } + expect_stdout: "function" +} + +issue_2992: { + options = { + evaluate: true, + reduce_vars: true, + } + input: { + var c = "PASS"; + (function f(b) { + switch (0) { + case 0: + case b = 1: + b && (c = "FAIL"); + } + })(); + console.log(c); + } + expect: { + var c = "PASS"; + (function f(b) { + switch (0) { + case 0: + case b = 1: + b && (c = "FAIL"); + } + })(); + console.log(c); + } + expect_stdout: "PASS" +} + +issue_3042_1: { + options = { + reduce_funcs: true, + reduce_vars: true, + toplevel: true, + unused: true, + } + input: { + function f() {} + var a = [ 1, 2 ].map(function() { + return new f(); + }); + console.log(a[0].constructor === a[1].constructor); + } + expect: { + function f() {} + var a = [ 1, 2 ].map(function() { + return new f(); + }); + console.log(a[0].constructor === a[1].constructor); + } + expect_stdout: "true" +} + +issue_3042_2: { + options = { + reduce_funcs: true, + reduce_vars: true, + toplevel: true, + unused: true, + } + input: { + function Foo() { + this.isFoo = function(o) { + return o instanceof Foo; + }; + } + function FooCollection() { + this.foos = [1, 1].map(function() { + return new Foo(); + }); + } + var fooCollection = new FooCollection(); + console.log(fooCollection.foos[0].isFoo(fooCollection.foos[0])); + console.log(fooCollection.foos[0].isFoo(fooCollection.foos[1])); + console.log(fooCollection.foos[1].isFoo(fooCollection.foos[0])); + console.log(fooCollection.foos[1].isFoo(fooCollection.foos[1])); + } + expect: { + function Foo() { + this.isFoo = function(o) { + return o instanceof Foo; + }; + } + var fooCollection = new function() { + this.foos = [1, 1].map(function() { + return new Foo(); + }); + }(); + console.log(fooCollection.foos[0].isFoo(fooCollection.foos[0])); + console.log(fooCollection.foos[0].isFoo(fooCollection.foos[1])); + console.log(fooCollection.foos[1].isFoo(fooCollection.foos[0])); + console.log(fooCollection.foos[1].isFoo(fooCollection.foos[1])); + } + expect_stdout: [ + "true", + "true", + "true", + "true", + ] +} + +issue_3068_1: { + options = { + evaluate: true, + reduce_vars: true, + } + input: { + (function() { + do { + continue; + var b = "defined"; + } while (b && b.c); + })(); + } + expect: { + (function() { + do { + continue; + var b = "defined"; + } while (b && b.c); + })(); + } + expect_stdout: true +} + +issue_3068_2: { + options = { + evaluate: true, + reduce_vars: true, + } + input: { + (function() { + do { + try { + while ("" == typeof a); + } finally { + continue; + } + var b = "defined"; + } while (b && b.c); + })(); + } + expect: { + (function() { + do { + try { + while ("" == typeof a); + } finally { + continue; + } + var b = "defined"; + } while (b && b.c); + })(); + } + expect_stdout: true +} + +issue_3110_1: { + options = { + conditionals: true, + evaluate: true, + inline: true, + passes: 3, + properties: true, + reduce_vars: true, + sequences: true, + side_effects: true, + unused: true, + } + input: { + (function() { + function foo() { + return isDev ? "foo" : "bar"; + } + var isDev = true; + var obj = { + foo: foo + }; + console.log(foo()); + console.log(obj.foo()); + })(); + } + expect: { + console.log("foo"), + console.log("foo"); + } + expect_stdout: [ + "foo", + "foo", + ] +} + +issue_3110_2: { + options = { + conditionals: true, + evaluate: true, + inline: true, + passes: 4, + properties: true, + reduce_vars: true, + sequences: true, + side_effects: true, + unused: true, + } + input: { + (function() { + function foo() { + return isDev ? "foo" : "bar"; + } + var isDev = true; + console.log(foo()); + var obj = { + foo: foo + }; + console.log(obj.foo()); + })(); + } + expect: { + console.log("foo"), + console.log("foo"); + } + expect_stdout: [ + "foo", + "foo", + ] +} + +issue_3110_3: { + options = { + conditionals: true, + evaluate: true, + inline: true, + properties: true, + reduce_vars: true, + sequences: true, + side_effects: true, + unused: true, + } + input: { + (function() { + function foo() { + return isDev ? "foo" : "bar"; + } + console.log(foo()); + var isDev = true; + var obj = { + foo: foo + }; + console.log(obj.foo()); + })(); + } + expect: { + (function() { + function foo() { + return isDev ? "foo" : "bar"; + } + console.log(foo()); + var isDev = true; + var obj = { + foo: foo + }; + console.log(obj.foo()); + })(); + } + expect_stdout: [ + "bar", + "foo", + ] +} + +issue_3113_1: { + options = { + evaluate: true, + reduce_vars: true, + } + input: { + var c = 0; + (function() { + function f() { + while (g()); + } + var a = f(); + function g() { + a && a[c++]; + } + g(a = 1); + })(); + console.log(c); + } + expect: { + var c = 0; + (function() { + function f() { + while (g()); + } + var a = f(); + function g() { + a && a[c++]; + } + g(a = 1); + })(); + console.log(c); + } + expect_stdout: "1" +} + +issue_3113_2: { + options = { + evaluate: true, + reduce_vars: true, + } + input: { + var c = 0; + (function() { + function f() { + while (g()); + } + var a = f(); + function g() { + a && a[c++]; + } + a = 1; + g(); + })(); + console.log(c); + } + expect: { + var c = 0; + (function() { + function f() { + while (g()); + } + var a = f(); + function g() { + a && a[c++]; + } + a = 1; + g(); + })(); + console.log(c); + } + expect_stdout: "1" +} + +issue_3113_3: { + options = { + evaluate: true, + inline: true, + passes: 2, + pure_getters: "strict", + reduce_vars: true, + side_effects: true, + unused: true, + } + input: { + var c = 0; + (function() { + function f() { + while (g()); + } + var a; + function g() { + a && a[c++]; + } + g(a = 1); + })(); + console.log(c); + } + expect: { + var c = 0; + c++; + console.log(c); + } + expect_stdout: "1" +} + +issue_3113_4: { + options = { + evaluate: true, + reduce_vars: true, + toplevel: true, + } + input: { + var a = 0, b = 0; + function f() { + b += a; + } + f(f(), ++a); + console.log(a, b); + } + expect: { + var a = 0, b = 0; + function f() { + b += a; + } + f(f(), ++a); + console.log(a, b); + } + expect_stdout: "1 1" +} + +issue_3113_5: { + options = { + evaluate: true, + reduce_vars: true, + toplevel: true, + } + input: { + function f() { + console.log(a); + } + function g() { + f(); + } + while (g()); + var a = 1; + f(); + } + expect: { + function f() { + console.log(a); + } + function g() { + f(); + } + while (g()); + var a = 1; + f(); + } + expect_stdout: [ + "undefined", + "1", + ] +} + +conditional_nested_1: { + options = { + evaluate: true, + reduce_vars: true, + } + input: { + var a = 1, b = 0; + (function f(c) { + function g() { + c && (c.a = 0); + c && (c.a = 0); + c && (c[b++] *= 0); + } + g(a-- && f(g(c = 42))); + })(); + console.log(b); + } + expect: { + var a = 1, b = 0; + (function f(c) { + function g() { + c && (c.a = 0); + c && (c.a = 0); + c && (c[b++] *= 0); + } + g(a-- && f(g(c = 42))); + })(); + console.log(b); + } + expect_stdout: "2" +} + +conditional_nested_2: { + options = { + evaluate: true, + reduce_vars: true, + } + input: { + var c = 0; + (function(a) { + function f() { + a && c++; + } + f(!c && f(), a = 1); + })(); + console.log(c); + } + expect: { + var c = 0; + (function(a) { + function f() { + a && c++; + } + f(!c && f(), a = 1); + })(); + console.log(c); + } + expect_stdout: "1" +} + +conditional_nested_3: { + options = { + evaluate: true, + reduce_vars: true, + } + input: { + var n = 2, c = 0; + (function f(a) { + 0 < n-- && g(a = 1); + function g() { + a && c++; + } + g(); + 0 < n-- && f(); + })(); + console.log(c); + } + expect: { + var n = 2, c = 0; + (function f(a) { + 0 < n-- && g(a = 1); + function g() { + a && c++; + } + g(); + 0 < n-- && f(); + })(); + console.log(c); + } + expect_stdout: "2" +} + +issue_2436: { + options = { + evaluate: true, + reduce_vars: true, + toplevel: true, + unsafe: true, + } + input: { + var c; + console.log(((c = { + a: 1, + b: 2 + }).a = 3, { + x: c.a, + y: c.b + })); + } + expect: { + var c; + console.log(((c = { + a: 1, + b: 2 + }).a = 3, { + x: c.a, + y: c.b + })); + } + expect_stdout: true +} + +issue_2916: { + options = { + collapse_vars: true, + evaluate: true, + inline: true, + passes: 2, + reduce_vars: true, + side_effects: true, + unsafe: true, + unused: true, + } + input: { + var c = "FAIL"; + (function(b) { + (function(d) { + d[0] = 1; + })(b); + +b && (c = "PASS"); + })([]); + console.log(c); + } + expect: { + var c = "FAIL"; + (function(b) { + b[0] = 1; + +b && (c = "PASS"); + })([]); + console.log(c); + } + expect_stdout: "PASS" +} + +issue_3125: { + options = { + evaluate: true, + reduce_vars: true, + toplevel: true, + unsafe: true, + } + input: { + var o; + console.log((function() { + this.p++; + }.call(o = { + p: 6 + }), o.p)); + } + expect: { + var o; + console.log((function() { + this.p++; + }.call(o = { + p: 6 + }), o.p)); + } + expect_stdout: "7" +} + +issue_3140_1: { + options = { + reduce_vars: true, + unused: true, + } + input: { + (function() { + var a; + function f() { + } + f.g = function g() { + function h() { + console.log(a ? "PASS" : "FAIL"); + } + a = true; + this(); + a = false; + h.g = g; + return h; + }; + return f; + })().g().g(); + } + expect: { + (function() { + var a; + function f() { + } + f.g = function g() { + function h() { + console.log(a ? "PASS" : "FAIL"); + } + a = true; + this(); + a = false; + h.g = g; + return h; + }; + return f; + })().g().g(); + } + expect_stdout: "PASS" +} + +issue_3140_2: { + options = { + reduce_vars: true, + unused: true, + } + input: { + (function() { + var a; + function f() { + } + f.g = function g() { + var self = this; + function h() { + console.log(a ? "PASS" : "FAIL"); + } + a = true; + self(); + a = false; + h.g = g; + return h; + }; + return f; + })().g().g(); + } + expect: { + (function() { + var a; + function f() { + } + f.g = function g() { + function h() { + console.log(a ? "PASS" : "FAIL"); + } + a = true; + this(); + a = false; + h.g = g; + return h; + }; + return f; + })().g().g(); + } + expect_stdout: "PASS" +} + +issue_3140_3: { + options = { + reduce_vars: true, + unused: true, + } + input: { + (function() { + var a; + function f() { + } + f.g = function g() { + var self = this; + function h() { + console.log(a ? "PASS" : "FAIL"); + } + a = true; + (function() { + return self; + })()(); + a = false; + h.g = g; + return h; + }; + return f; + })().g().g(); + } + expect: { + (function() { + var a; + function f() { + } + f.g = function g() { + var self = this; + function h() { + console.log(a ? "PASS" : "FAIL"); + } + a = true; + (function() { + return self; + })()(); + a = false; + h.g = g; + return h; + }; + return f; + })().g().g(); + } + expect_stdout: "PASS" +} + +issue_3140_4: { + options = { + reduce_vars: true, + unused: true, + } + input: { + (function() { + var a; + function f() { + } + f.g = function g() { + var o = { + p: this + }; + function h() { + console.log(a ? "PASS" : "FAIL"); + } + a = true; + o.p(); + a = false; + h.g = g; + return h; + }; + return f; + })().g().g(); + } + expect: { + (function() { + var a; + function f() { + } + f.g = function g() { + var o = { + p: this + }; + function h() { + console.log(a ? "PASS" : "FAIL"); + } + a = true; + o.p(); + a = false; + h.g = g; + return h; + }; + return f; + })().g().g(); + } + expect_stdout: "PASS" +} + +issue_3140_5: { + options = { + evaluate: true, + reduce_vars: true, + } + input: { + var n = 1, c = 0; + (function(a) { + var b = function() { + this; + n-- && h(); + }(); + function h() { + b && c++; + } + h(b = 1); + })(); + console.log(c); + } + expect: { + var n = 1, c = 0; + (function(a) { + var b = function() { + this; + n-- && h(); + }(); + function h() { + b && c++; + } + h(b = 1); + })(); + console.log(c); + } + expect_stdout: "1" +} + +issue_3240_1: { + options = { + reduce_funcs: true, + reduce_vars: true, + unused: true, + } + input: { + (function() { + f(1); + function f(a) { + console.log(a); + var g = function() { + f(a - 1); + }; + if (a) g(); + } + })(); + } + expect: { + (function() { + (function f(a) { + console.log(a); + var g = function() { + f(a - 1); + }; + if (a) g(); + })(1); + })(); + } + expect_stdout: [ + "1", + "0", + ] +} + +issue_3240_2: { + options = { + passes: 2, + reduce_funcs: true, + reduce_vars: true, + unused: true, + } + input: { + (function() { + f(1); + function f(a) { + console.log(a); + var g = function() { + f(a - 1); + }; + if (a) g(); + } + })(); + } + expect: { + (function() { + (function f(a) { + console.log(a); + if (a) (function() { + f(a - 1); + })(); + })(1); + })(); + } + expect_stdout: [ + "1", + "0", + ] +} + +issue_3240_3: { + options = { + reduce_funcs: true, + reduce_vars: true, + unused: true, + } + input: { + (function() { + f(); + function f(b) { + if (!f.a) f.a = 0; + console.log(f.a.toString()); + var g = function() { + (b ? function() {} : function() { + f.a++; + f(1); + })(); + }; + g(); + } + })(); + } + expect: { + (function() { + f(); + function f(b) { + if (!f.a) f.a = 0; + console.log(f.a.toString()); + (function() { + (b ? function() {} : function() { + f.a++; + f(1); + })(); + })(); + } + })(); + } + expect_stdout: [ + "0", + "1", + ] +} + +issue_3240_4: { + options = { + passes: 2, + reduce_funcs: true, + reduce_vars: true, + unused: true, + } + input: { + (function() { + f(); + function f(b) { + if (!f.a) f.a = 0; + console.log(f.a.toString()); + var g = function() { + (b ? function() {} : function() { + f.a++; + f(1); + })(); + }; + g(); + } + })(); + } + expect: { + (function() { + f(); + function f(b) { + if (!f.a) f.a = 0; + console.log(f.a.toString()); + (function() { + (b ? function() {} : function() { + f.a++; + f(1); + })(); + })(); + } + })(); + } + expect_stdout: [ + "0", + "1", + ] +} + +issues_3267_1: { + options = { + collapse_vars: true, + conditionals: true, + dead_code: true, + evaluate: true, + inline: true, + negate_iife: true, + reduce_vars: true, + sequences: true, + side_effects: true, + unused: true, + } + input: { + (function(x) { + x(); + })(function() { + (function(i) { + if (i) + return console.log("PASS"); + throw "FAIL"; + })(Object()); + }); + } + expect: { + !function(x) { + if (Object()) + return console.log("PASS"); + throw "FAIL"; + }(); + } + expect_stdout: "PASS" +} + +issues_3267_2: { + options = { + collapse_vars: true, + conditionals: true, + dead_code: true, + evaluate: true, + inline: true, + keep_fargs: false, + negate_iife: true, + passes: 2, + reduce_vars: true, + sequences: true, + side_effects: true, + unused: true, + } + input: { + (function(x) { + x(); + })(function() { + (function(i) { + if (i) + return console.log("PASS"); + throw "FAIL"; + })(Object()); + }); + } + expect: { + !function() { + if (Object()) + return console.log("PASS"); + throw "FAIL"; + }(); + } + expect_stdout: "PASS" +} + +issues_3267_3: { + options = { + collapse_vars: true, + conditionals: true, + dead_code: true, + evaluate: true, + inline: true, + keep_fargs: false, + passes: 2, + reduce_vars: true, + sequences: true, + side_effects: true, + unsafe: true, + unused: true, + } + input: { + (function(x) { + x(); + })(function() { + (function(i) { + if (i) + return console.log("PASS"); + throw "FAIL"; + })(Object()); + }); + } + expect: { + console.log("PASS"); + } + expect_stdout: "PASS" +} + +issue_3297: { + options = { + reduce_vars: true, + unused: true, + } + input: { + (function() { + function f() { + var a; + var b = function a() { + console.log(a === b) && f(); + }; + b(); + } + f(); + })(); + } + expect: { + (function() { + (function f() { + var b = function a() { + console.log(a === b) && f(); + }; + b(); + })(); + })(); + } + expect_stdout: "true" +} + +drop_side_effect_free: { + options = { + collapse_vars: true, + evaluate: true, + reduce_vars: true, + side_effects: true, + toplevel: true, + } + input: { + var a = 123; + "" + (a && (a.b = 0) || a); + console.log(a); + } + expect: { + var a = 123; + a.b = 0; + console.log(a); + } + expect_stdout: "123" +} + +issue_3377: { + options = { + reduce_vars: true, + unused: true, + } + input: { + console.log(function f() { + return f[0], (f = 42); + }()); + } + expect: { + console.log(function f() { + return f[0], (f = 42); + }()); + } + expect_stdout: "42" +} + +issue_3509: { + options = { + reduce_vars: true, + toplevel: true, + unused: true, + } + input: { + function a() { + console.log("PASS"); + } + try { + } catch (a) { + var a; + } + a(); + } + expect: { + try { + } catch (a) { + var a; + } + (function() { + console.log("PASS"); + })(); + } + expect_stdout: "PASS" +} + +issue_3622: { + options = { + evaluate: true, + inline: true, + keep_fargs: false, + reduce_vars: true, + sequences: true, + toplevel: true, + unused: true, + } + input: { + var c = "FAIL"; + !function(b, a) { + a && (c = "PASS"); + }(42, this); + console.log(c); + } + expect: { + var c = "FAIL"; + var a; + a = this, + !void (a && (c = "PASS")), + console.log(c); + } + expect_stdout: "PASS" +} + +issue_3631_1: { + options = { + evaluate: true, + reduce_vars: true, + toplevel: true, + } + input: { + var c = 0; + L: do { + for (;;) continue L; + var b = 1; + } while (b && c++); + console.log(c); + } + expect: { + var c = 0; + L: do { + for (;;) continue L; + var b = 1; + } while (b && c++); + console.log(c); + } + expect_stdout: "0" +} + +issue_3631_2: { + options = { + reduce_vars: true, + toplevel: true, + unused: true, + } + input: { + L: for (var a = 1; a--; console.log(b)) { + for (;;) continue L; + var b = "FAIL"; + } + } + expect: { + L: for (var a = 1; a--; console.log(b)) { + for (;;) continue L; + var b = "FAIL"; + } + } + expect_stdout: "undefined" +} + +issue_3666: { + options = { + collapse_vars: true, + passes: 2, + reduce_vars: true, + toplevel: true, + unused: true, + } + input: { + try { + var a = "FAIL"; + } finally { + for (;!a;) + var c = a++; + var a = "PASS", b = c = "PASS"; + } + console.log(a, b); + } + expect: { + try { + var a = "FAIL"; + } finally { + for (;!a;) + a++; + a = "PASS"; + } + console.log(a, "PASS"); + } + expect_stdout: "PASS PASS" +} + +issue_3774: { + options = { + reduce_funcs: true, + reduce_vars: true, + unused: true, + } + input: { + var f = function() { + function g() { + if (!g.p) { + g.p = 1; + console.log("PASS"); + } + } + return function() { + g(); + }; + }(); + f(); + f(); + } + expect: { + var f = function() { + function g() { + if (!g.p) { + g.p = 1; + console.log("PASS"); + } + } + return function() { + g(); + }; + }(); + f(); + f(); + } + expect_stdout: "PASS" +} + +flatten_iife: { + options = { + reduce_vars: true, + side_effects: true, + toplevel: true, + unused: true, + } + input: { + var a = "FAIL"; + while (!console); + a++; + (function() { + while (!console); + a = "PASS"; + })(); + console.log(a); + } + expect: { + var a; + while (!console); + 0; + (function() { + while (!console); + a = "PASS"; + })(); + console.log(a); + } + expect_stdout: "PASS" +} + +issue_3844: { + options = { + evaluate: true, + reduce_vars: true, + toplevel: true, + } + input: { + console.log(function() { + if (!console) switch (A = 0) { + case console.log("FAIL"): + return; + } + return typeof A; + }()); + } + expect: { + console.log(function() { + if (!console) switch (A = 0) { + case console.log("FAIL"): + return; + } + return typeof A; + }()); + } + expect_stdout: "undefined" +} + +issue_3866: { + options = { + dead_code: true, + evaluate: true, + reduce_vars: true, + } + input: { + console.log(function() { + { + return "PASS"; + var a = 0; + } + return --a; + }()); + } + expect: { + console.log("PASS"); + } + expect_stdout: "PASS" +} + +issue_3880: { + options = { + reduce_funcs: true, + reduce_vars: true, + unused: true, + } + input: { + (function(a) { + while (a.var ^= 1); + console.log("PASS"); + })(function() {}); + } + expect: { + (function(a) { + while (a.var ^= 1); + console.log("PASS"); + })(function() {}); + } + expect_stdout: "PASS" +} + +issue_3894: { + options = { + collapse_vars: true, + inline: true, + reduce_vars: true, + unused: true, + } + input: { + function log(msg) { + console.log(msg ? "FAIL" : "PASS"); + } + var a; + (function(b) { + a = 0, + log(b); + })(-0); + } + expect: { + function log(msg) { + console.log(msg ? "FAIL" : "PASS"); + } + var a; + void log(-(a = 0)); + } + expect_stdout: "PASS" +} + +issue_3922: { + options = { + evaluate: true, + keep_fargs: false, + pure_getters: "strict", + reduce_vars: true, + side_effects: true, + unused: true, + } + input: { + (function(a) { + var b; + b && b[c]; + a |= this; + console.log("PASS"); + var c = a.undefined; + })(); + } + expect: { + (function() { + 0; + console.log("PASS"); + })(); + } + expect_stdout: "PASS" +} + +issue_3949_1: { + options = { + evaluate: true, + reduce_vars: true, + } + input: { + (function f(a) { + var a = void (a = 0, g); + function g() { + console.log(typeof a); + } + g(); + })(); + } + expect: { + (function f(a) { + var a = void (a = 0, g); + function g() { + console.log(typeof a); + } + g(); + })(); + } + expect_stdout: "undefined" +} + +issue_3949_2: { + options = { + reduce_vars: true, + unused: true, + } + input: { + (function f(a) { + var a = void (a = 0, g); + function g() { + console.log(typeof a); + } + g(); + })(); + } + expect: { + (function(a) { + a = void (a = 0, g); + function g() { + console.log(typeof a); + } + g(); + })(); + } + expect_stdout: "undefined" +} + +issue_3949_3: { + options = { + evaluate: true, + reduce_vars: true, + toplevel: true, + } + input: { + function f() {} + for (var a, i = 3; 0 <= --i; ) { + a = f; + console.log(a === b); + var b = a; + } + } + expect: { + function f() {} + for (var a, i = 3; 0 <= --i; ) { + a = f; + console.log(a === b); + var b = a; + } + } + expect_stdout: [ + "false", + "true", + "true", + ] +} + +issue_3949_4: { + options = { + reduce_vars: true, + unused: true, + toplevel: true, + } + input: { + function f() {} + for (var a, i = 3; 0 <= --i; ) { + a = f; + console.log(a === b); + var b = a; + } + } + expect: { + function f() {} + for (var a, i = 3; 0 <= --i; ) { + a = f; + console.log(a === b); + var b = a; + } + } + expect_stdout: [ + "false", + "true", + "true", + ] +} + +local_assignment_lambda: { + options = { + evaluate: true, + reduce_vars: true, + sequences: true, + toplevel: true, + unused: true, + } + input: { + var a = "FAIL"; + function f() { + a = "PASS"; + console.log(a); + } + f(); + f(); + } + expect: { + function f() { + console.log("PASS"); + } + f(), + f(); + } + expect_stdout: [ + "PASS", + "PASS", + ] +} + +local_assignment_loop: { + options = { + evaluate: true, + reduce_vars: true, + sequences: true, + toplevel: true, + unused: true, + } + input: { + var a = "FAIL"; + do { + a = "PASS"; + console.log(a); + } while (!console); + } + expect: { + do { + console.log("PASS"); + } while (!console); + } + expect_stdout: "PASS" +} + +local_assignment_modified: { + options = { + evaluate: true, + reduce_vars: true, + side_effects: true, + toplevel: true, + } + input: { + var a; + (a = a || {}).p = 42; + console.log(a.p); + } + expect: { + var a; + (a = {}).p = 42; + console.log(a.p); + } + expect_stdout: "42" +} + +local_declaration: { + options = { + evaluate: true, + reduce_vars: true, + side_effects: true, + toplevel: true, + } + input: { + var a; + a || console.log(a = "PASS"); + } + expect: { + var a; + console.log("PASS"); + } + expect_stdout: "PASS" +} + +local_definition_modified: { + options = { + evaluate: true, + reduce_vars: true, + side_effects: true, + toplevel: true, + } + input: { + var a = a || {}; + a.p = 42; + console.log(a.p); + } + expect: { + var a = {}; + a.p = 42; + console.log(a.p); + } + expect_stdout: "42" +} + +issue_3957_1: { + options = { + evaluate: true, + reduce_vars: true, + } + input: { + function f(a) { + while (a += console.log(a = 0)) + a = 0; + } + f("FAIL"); + } + expect: { + function f(a) { + while (a += console.log(a = 0)) + a = 0; + } + f("FAIL"); + } + expect_stdout: [ + "0", + "0", + ] +} + +issue_3957_2: { + options = { + reduce_vars: true, + unused: true, + } + input: { + function f(a) { + while (a += console.log(a = 0)) + a = 0; + } + f("FAIL"); + } + expect: { + function f(a) { + while (a += console.log(a = 0)) + a = 0; + } + f("FAIL"); + } + expect_stdout: [ + "0", + "0", + ] +} + +issue_3958: { + options = { + evaluate: true, + reduce_vars: true, + side_effects: true, + toplevel: true, + unsafe: true, + unused: true, + } + input: { + var a; + (function(b) { + (function(c) { + console.log(c[0] = 1); + })(a = b); + --a; + })([]); + console.log(a); + } + expect: { + var a; + (function(b) { + (function(c) { + console.log(c[0] = 1); + })(a = []); + --a; + })(); + console.log(a); + } + expect_stdout: [ + "1", + "0", + ] +} + +issue_3974: { + options = { + reduce_vars: true, + toplevel: true, + unused: true, + } + input: { + try { + var a = 0 in 0; + 0 && a; + } catch (e) { + console.log("PASS"); + } + } + expect: { + try { + var a = 0 in 0; + 0 && a; + } catch (e) { + console.log("PASS"); + } + } + expect_stdout: "PASS" +} + +issue_4030: { + options = { + collapse_vars: true, + evaluate: true, + reduce_vars: true, + side_effects: true, + toplevel: true, + unused: true, + } + input: { + var a; + { + delete (a = "PASS"); + A = "PASS"; + } + console.log(A); + } + expect: { + A = "PASS"; + console.log(A); + } + expect_stdout: "PASS" +} + +global_assign: { + options = { + evaluate: true, + reduce_vars: true, + toplevel: true, + unused: true, + } + input: { + A = "FAIL"; + this.A = "PASS"; + console.log(A); + } + expect: { + A = "FAIL"; + this.A = "PASS"; + console.log(A); + } + expect_stdout: "PASS" +} + +issue_4188_1: { + options = { + reduce_vars: true, + unused: true, + } + input: { + (function() { + try { + while (A) + var a = function() {}, b = a; + } catch (a) { + console.log(function() { + return typeof a; + }(), typeof b); + } + })(); + } + expect: { + (function() { + try { + while (A) + var a = function() {}, b = a; + } catch (a) { + console.log(function() { + return typeof a; + }(), typeof b); + } + })(); + } + expect_stdout: "object undefined" +} + +issue_4188_2: { + options = { + reduce_vars: true, + unused: true, + } + input: { + (function() { + try { + throw 42; + } catch (a) { + console.log(function() { + return typeof a; + }(), typeof b); + } + while (!console) + var a = function() {}, b = a; + })(); + } + expect: { + (function() { + try { + throw 42; + } catch (a) { + console.log(function() { + return typeof a; + }(), typeof b); + } + while (!console) + var a = function() {}, b = a; + })(); + } + expect_stdout: "number undefined" +} + +issue_4568: { + options = { + booleans: true, + conditionals: true, + dead_code: true, + evaluate: true, + loops: true, + passes: 2, + reduce_vars: true, + sequences: true, + unused: true, + } + input: { + (function(a) { + a && console.log("FAIL"); + if (1) + do { + if (!console.log("PASS")) break; + } while (1); + })(!(0 !== delete NaN)); + } + expect: { + (function(a) { + for (a && console.log("FAIL"), 1; console.log("PASS"); ) 1; + })(!(0 !== delete NaN)); + } + expect_stdout: "PASS" +} + +issue_4937: { + options = { + reduce_vars: true, + toplevel: true, + unused: true, + } + input: { + function f() { + while (console.log("PASS")); + } + do { + function g() { + f(); + } + } while (!g); + f(); + } + expect: { + function f() { + while (console.log("PASS")); + } + do { + function g() { + f(); + } + } while (!g); + f(); + } + expect_stdout: "PASS" +} + +issue_4943_1: { + options = { + evaluate: true, + reduce_vars: true, + toplevel: true, + } + input: { + var a, b = 1; + (function f() { + a = "foo"; + b-- && f(); + console.log(a); + a = "bar"; + })(); + } + expect: { + var a, b = 1; + (function f() { + a = "foo"; + b-- && f(); + console.log(a); + a = "bar"; + })(); + } + expect_stdout: [ + "foo", + "bar", + ] +} + +issue_4943_2: { + options = { + reduce_vars: true, + toplevel: true, + unused: true, + } + input: { + var a, b = 1; + (function f() { + a = "foo"; + b-- && f(); + console.log(a); + a = "bar"; + })(); + } + expect: { + var a, b = 1; + (function f() { + a = "foo"; + b-- && f(); + console.log(a); + a = "bar"; + })(); + } + expect_stdout: [ + "foo", + "bar", + ] +} + +issue_4949: { + options = { + reduce_vars: true, + unused: true, + } + input: { + (function f(a) { + a = 0; + console.log(a++, arguments[0]); + })(0); + } + expect: { + (function(a) { + a = 0; + console.log(a++, arguments[0]); + })(0); + } + expect_stdout: "0 1" +} + +issue_5048: { + options = { + reduce_funcs: true, + reduce_vars: true, + unused: true, + } + input: { + console.log(function() { + var a = function() { + return a + 42; + }; + }()); + } + expect: { + console.log(function() {}()); + } + expect_stdout: "undefined" +} + +issue_5050: { + options = { + evaluate: true, + reduce_vars: true, + toplevel: true, + } + input: { + function f() { + console.log(a); + } + this; + var a = 1; + f(console.log(2), f(), a = 3); + } + expect: { + function f() { + console.log(a); + } + this; + var a = 1; + f(console.log(2), f(), a = 3); + } + expect_stdout: [ + "2", + "1", + "3", + ] +} + +issue_5055_1: { + options = { + evaluate: true, + reduce_vars: true, + toplevel: true, + } + input: { + var a = "PASS"; + function f() { + console.log(a || "FAIL"); + } + f(0 && (a = 0)(f(this))); + } + expect: { + var a = "PASS"; + function f() { + console.log(a || "FAIL"); + } + f(0); + } + expect_stdout: "PASS" +} + +issue_5055_2: { + options = { + reduce_vars: true, + toplevel: true, + unused: true, + } + input: { + var a = "PASS"; + function f() { + console.log(a || "FAIL"); + } + f(0 && (a = 0)(f(this))); + } + expect: { + var a = "PASS"; + function f() { + console.log(a || "FAIL"); + } + f(0 && (a = 0)(f())); + } + expect_stdout: "PASS" +} + +issue_5324: { + options = { + inline: true, + merge_vars: true, + reduce_vars: true, + toplevel: true, + unused: true, + } + input: { + A = 0; + do { + var a, b = A; + for (a in b) + var c = b; + } while (function() { + var d; + console.log(d *= A); + }()); + } + expect: { + A = 0; + do { + var a, b = A; + for (a in b); + } while (b = void 0, void console.log(b *= A)); + } + expect_stdout: "NaN" +} + +issue_5434: { + options = { + evaluate: true, + reduce_vars: true, + unused: true, + } + input: { + console.log(function(a) { + for (var i = 0; i < 2; i++) { + var b = "FAIL"; + f && f(); + a = b; + var f = function() { + b = "PASS"; + }; + } + return a; + }()); + } + expect: { + console.log(function(a) { + for (var i = 0; i < 2; i++) { + var b = "FAIL"; + f && f(); + a = b; + var f = function() { + b = "PASS"; + }; + } + return a; + }()); + } + expect_stdout: "PASS" +} + +issue_5623: { + options = { + collapse_vars: true, + evaluate: true, + passes: 2, + reduce_vars: true, + side_effects: true, + toplevel: true, + } + input: { + var a = 0; + function f() { + var b = a; + a = b; + } + f && f((a++ && a).toString()); + console.log(a); + } + expect: { + var a = 0; + function f() { + var b; + a = a; + } + f((a++ && a).toString()); + console.log(a); + } + expect_stdout: "1" +} + +issue_5716_1: { + options = { + collapse_vars: true, + inline: true, + merge_vars: true, + reduce_vars: true, + toplevel: true, + unused: true, + } + input: { + var a; + function f() { + var b = [ c, c ], c = function() { + return b++ + (a = b); + }(); + } + f(); + console.log(a); + } + expect: { + c = [ c, c ], + void (c = ++c); + var c; + console.log(c); + } + expect_stdout: "NaN" +} + +issue_5716_2: { + options = { + collapse_vars: true, + inline: true, + merge_vars: true, + reduce_vars: true, + toplevel: true, + unused: true, + } + input: { + var a; + function f() { + var b = [ c, c ], c = function() { + return (b += 4) + (a = b += 2); + }(); + } + f(); + console.log(a); + } + expect: { + void (c = c = (c = [ c, c ]) + 4 + 2); + var c; + console.log(c); + } + expect_stdout: ",42" +} + +issue_5716_3: { + options = { + assignments: true, + collapse_vars: true, + inline: true, + merge_vars: true, + reduce_vars: true, + toplevel: true, + unused: true, + } + input: { + var a; + function f() { + var b = [ c, c ], c = function() { + return (b = b + 4) + (a = b += 2); + }(); + } + f(); + console.log(a); + } + expect: { + void (c = c = (c = [ c, c ]) + 4 + 2); + var c; + console.log(c); + } + expect_stdout: ",42" +} + +issue_5716_4: { + options = { + assignments: true, + collapse_vars: true, + inline: true, + merge_vars: true, + reduce_vars: true, + toplevel: true, + unused: true, + } + input: { + var a; + function f() { + var b = [ c, c ], c = function() { + return (b = true | b) + (a = b *= 42); + }(); + } + f(); + console.log(a); + } + expect: { + void (c = c = ((c = [ c, c ]) | true) * 42); + var c; + console.log(c); + } + expect_stdout: "42" +} + +issue_5716_5: { + options = { + assignments: true, + reduce_vars: true, + } + input: { + console.log(function() { + return 0 || (a = 42 | a); + var a = function() { + return a; + }; + }()); + } + expect: { + console.log(function() { + return 0 || (a |= 42); + var a = function() { + return a; + }; + }()); + } + expect_stdout: "42" +} + +issue_5730_1: { + options = { + evaluate: true, + reduce_vars: true, + toplevel: true, + unused: true, + } + input: { + var a = "PASS"; + L: { + var f = function() { + console.log(a); + }; + } + f(); + a++; + } + expect: { + var a = "PASS"; + var f = function() { + console.log(a); + }; + f(); + a++; + } + expect_stdout: "PASS" +} + +issue_5730_2: { + options = { + evaluate: true, + reduce_vars: true, + toplevel: true, + unused: true, + } + input: { + var a = "PASS"; + try { + var f = function() { + console.log(a); + }; + } finally {} + f(); + a++; + } + expect: { + var a = "PASS"; + try { + var f = function() { + console.log(a); + }; + } finally {} + f(); + a++; + } + expect_stdout: "PASS" +} + +issue_5730_3: { + options = { + evaluate: true, + reduce_vars: true, + toplevel: true, + unused: true, + } + input: { + var f, a = "PASS"; + L: { + f = function() { + console.log(a); + }; + } + f(); + a++; + } + expect: { + var f, a = "PASS"; + f = function() { + console.log(a); + }; + f(); + a++; + } + expect_stdout: "PASS" +} + +issue_5777_1: { + options = { + reduce_vars: true, + unused: true, + } + input: { + function f() { + (function(a) { + function g() { + h(); + } + g(); + a = function() {}; + function h() { + console.log(a); + } + })("PASS"); + } + f(); + } + expect: { + function f() { + (function(a) { + (function() { + h(); + })(); + a = function() {}; + function h() { + console.log(a); + } + })("PASS"); + } + f(); + } + expect_stdout: "PASS" +} + +issue_5777_2: { + options = { + reduce_vars: true, + toplevel: true, + unused: true, + } + input: { + function f(a) { + (function() { + function g() { + h(); + } + g(); + a = function() {}; + function h() { + console.log(a); + } + })(); + } + f("PASS"); + } + expect: { + (function(a) { + (function() { + (function() { + h(); + })(); + a = function() {}; + function h() { + console.log(a); + } + })(); + })("PASS"); + } + expect_stdout: "PASS" +} diff --git a/test/compress/regexp.js b/test/compress/regexp.js new file mode 100644 index 00000000000..3324a0c8e35 --- /dev/null +++ b/test/compress/regexp.js @@ -0,0 +1,491 @@ +regexp_simple: { + input: { + /rx/ig + } + expect_exact: "/rx/gi;" +} + +regexp_slashes: { + input: { + /\\\/rx\/\\/ig + } + expect_exact: "/\\\\\\/rx\\/\\\\/gi;" +} + +regexp_1: { + input: { + console.log(JSON.stringify("COMPASS? Overpass.".match(/([Sap]+)/ig))); + } + expect: { + console.log(JSON.stringify("COMPASS? Overpass.".match(/([Sap]+)/gi))); + } + expect_stdout: '["PASS","pass"]' +} + +regexp_2: { + options = { + evaluate: true, + unsafe: true, + } + input: { + console.log(JSON.stringify("COMPASS? Overpass.".match(new RegExp("([Sap]+)", "ig")))); + } + expect: { + console.log(JSON.stringify("COMPASS? Overpass.".match(/([Sap]+)/gi))); + } + expect_stdout: '["PASS","pass"]' +} + +regexp_properties: { + options = { + evaluate: true, + unsafe: true, + } + input: { + console.log(/abc/g.source, /abc/g.global, /abc/g.ignoreCase, /abc/g.lastIndex, /abc/g.multiline); + } + expect: { + console.log("abc", true, false, /abc/g.lastIndex, false); + } + expect_stdout: "abc true false 0 false" +} + +instanceof_1: { + input: { + console.log(/foo/ instanceof RegExp); + } + expect_exact: "console.log(/foo/ instanceof RegExp);" + expect_stdout: "true" +} + +instanceof_2: { + input: { + console.log(42 + /foo/ instanceof Object); + } + expect_exact: "console.log(42+/foo/ instanceof Object);" + expect_stdout: "false" +} + +issue_3434_1: { + options = { + evaluate: true, + unsafe: true, + } + beautify = { + beautify: true, + } + input: { + var o = { + "\n": RegExp("\n"), + "\r": RegExp("\r"), + "\t": RegExp("\t"), + "\b": RegExp("\b"), + "\f": RegExp("\f"), + "\0": RegExp("\0"), + "\x0B": RegExp("\x0B"), + "\u2028": RegExp("\u2028"), + "\u2029": RegExp("\u2029"), + }; + for (var c in o) + console.log(o[c].test("\\"), o[c].test(c)); + } + expect_exact: [ + "var o = {", + ' "\\n": /\\n/,', + ' "\\r": /\\r/,', + ' "\\t": /\t/,', + ' "\\b": /\b/,', + ' "\\f": /\f/,', + ' "\\0": /\0/,', + ' "\\v": /\v/,', + ' "\\u2028": /\\u2028/,', + ' "\\u2029": /\\u2029/', + "};", + "", + 'for (var c in o) console.log(o[c].test("\\\\"), o[c].test(c));', + ] + expect_stdout: [ + "false true", + "false true", + "false true", + "false true", + "false true", + "false true", + "false true", + "false true", + "false true", + ] +} + +issue_3434_2: { + options = { + evaluate: true, + unsafe: true, + } + beautify = { + beautify: true, + } + input: { + var o = { + "\n": RegExp("\\\n"), + "\r": RegExp("\\\r"), + "\t": RegExp("\\\t"), + "\b": RegExp("\\\b"), + "\f": RegExp("\\\f"), + "\0": RegExp("\\\0"), + "\x0B": RegExp("\\\x0B"), + "\u2028": RegExp("\\\u2028"), + "\u2029": RegExp("\\\u2029"), + }; + for (var c in o) + console.log(o[c].test("\\"), o[c].test(c)); + } + expect_exact: [ + "var o = {", + ' "\\n": /\\n/,', + ' "\\r": /\\r/,', + ' "\\t": /\t/,', + ' "\\b": /\b/,', + ' "\\f": /\f/,', + ' "\\0": /\0/,', + ' "\\v": /\v/,', + ' "\\u2028": /\\u2028/,', + ' "\\u2029": /\\u2029/', + "};", + "", + 'for (var c in o) console.log(o[c].test("\\\\"), o[c].test(c));', + ] + expect_stdout: [ + "false true", + "false true", + "false true", + "false true", + "false true", + "false true", + "false true", + "false true", + "false true", + ] +} + +issue_3434_3: { + options = { + evaluate: true, + unsafe: true, + } + input: { + RegExp("\n"); + RegExp("\r"); + RegExp("\\n"); + RegExp("\\\n"); + RegExp("\\\\n"); + RegExp("\\\\\n"); + RegExp("\\\\\\n"); + RegExp("\\\\\\\n"); + RegExp("\u2028"); + RegExp("\u2029"); + RegExp("\n\r\u2028\u2029"); + RegExp("\\\nfo\n[\n]o\\bbb"); + } + expect: { + /\n/; + /\r/; + /\n/; + /\n/; + /\\n/; + /\\\n/; + /\\\n/; + /\\\n/; + /\u2028/; + /\u2029/; + /\n\r\u2028\u2029/; + /\nfo\n[\n]o\bbb/; + } +} + +issue_3434_4: { + options = { + evaluate: true, + unsafe: true, + } + input: { + [ + [ "", RegExp("") ], + [ "/", RegExp("/") ], + [ "//", RegExp("//") ], + [ "\/", RegExp("\\/") ], + [ "///", RegExp("///") ], + [ "/\/", RegExp("/\\/") ], + [ "\//", RegExp("\\//") ], + [ "\\/", RegExp("\\\\/") ], + [ "////", RegExp("////") ], + [ "//\/", RegExp("//\\/") ], + [ "/\//", RegExp("/\\//") ], + [ "/\\/", RegExp("/\\\\/") ], + [ "\///", RegExp("\\///") ], + [ "\/\/", RegExp("\\/\\/") ], + [ "\\//", RegExp("\\\\//") ], + [ "\\\/", RegExp("\\\\\\/") ], + ].forEach(function(test) { + console.log(test[1].test("\\"), test[1].test(test[0])); + }); + } + expect: { + [ + [ "", /(?:)/ ], + [ "/", /\// ], + [ "//", /\/\// ], + [ "/", /\// ], + [ "///", /\/\/\// ], + [ "//", /\/\// ], + [ "//", /\/\// ], + [ "\\/", /\\\// ], + [ "////", /\/\/\/\// ], + [ "///", /\/\/\// ], + [ "///", /\/\/\// ], + [ "/\\/", /\/\\\// ], + [ "///", /\/\/\// ], + [ "//", /\/\// ], + [ "\\//", /\\\/\// ], + [ "\\/", /\\\// ], + ].forEach(function(test) { + console.log(test[1].test("\\"), test[1].test(test[0])); + }); + } + expect_stdout: [ + "true true", + "false true", + "false true", + "false true", + "false true", + "false true", + "false true", + "false true", + "false true", + "false true", + "false true", + "false true", + "false true", + "false true", + "false true", + "false true", + ] +} + +exec: { + options = { + evaluate: true, + loops: true, + unsafe: true, + } + input: { + while (/a/.exec("AAA")) + console.log("FAIL"); + console.log("PASS"); + } + expect: { + for (;null;) + console.log("FAIL"); + console.log("PASS"); + } + expect_stdout: "PASS" +} + +exec_global: { + options = { + evaluate: true, + loops: true, + unsafe: true, + } + input: { + while (/a/g.exec("AAA")) + console.log("FAIL"); + console.log("PASS"); + } + expect: { + for (;null;) + console.log("FAIL"); + console.log("PASS"); + } + expect_stdout: "PASS" +} + +test: { + options = { + evaluate: true, + unsafe: true, + } + input: { + while (/a/.test("AAA")) + console.log("FAIL"); + console.log("PASS"); + } + expect: { + while (false) + console.log("FAIL"); + console.log("PASS"); + } + expect_stdout: "PASS" +} + +test_global: { + options = { + evaluate: true, + unsafe: true, + } + input: { + while (/a/g.test("AAA")) + console.log("FAIL"); + console.log("PASS"); + } + expect: { + while (false) + console.log("FAIL"); + console.log("PASS"); + } + expect_stdout: "PASS" +} + +var_exec: { + options = { + evaluate: true, + loops: true, + reduce_vars: true, + toplevel: true, + unsafe: true, + } + input: { + var r = /a/; + while (r.exec("AAA")) + console.log("FAIL"); + console.log("PASS"); + } + expect: { + var r = /a/; + for (;null;) + console.log("FAIL"); + console.log("PASS"); + } + expect_stdout: "PASS" +} + +var_exec_global: { + options = { + evaluate: true, + loops: true, + reduce_vars: true, + toplevel: true, + unsafe: true, + } + input: { + var r = /a/g; + while (r.exec("aaa")) + console.log("PASS"); + } + expect: { + var r = /a/g; + for (;r.exec("aaa");) + console.log("PASS"); + } + expect_stdout: [ + "PASS", + "PASS", + "PASS", + ] +} + +var_test: { + options = { + evaluate: true, + reduce_vars: true, + toplevel: true, + unsafe: true, + } + input: { + var r = /a/; + while (r.test("AAA")) + console.log("FAIL"); + console.log("PASS"); + } + expect: { + var r = /a/; + while (false) + console.log("FAIL"); + console.log("PASS"); + } + expect_stdout: "PASS" +} + +var_test_global: { + options = { + evaluate: true, + reduce_vars: true, + toplevel: true, + unsafe: true, + } + input: { + var r = /a/g; + while (r.test("aaa")) + console.log("PASS"); + } + expect: { + var r = /a/g; + while (r.test("aaa")) + console.log("PASS"); + } + expect_stdout: [ + "PASS", + "PASS", + "PASS", + ] +} + +lazy_boolean: { + options = { + evaluate: true, + passes: 2, + side_effects: true, + unsafe: true, + } + input: { + /b/.exec({}) && console.log("PASS"); + /b/.test({}) && console.log("PASS"); + /b/g.exec({}) && console.log("PASS"); + /b/g.test({}) && console.log("PASS"); + } + expect: { + console.log("PASS"); + console.log("PASS"); + console.log("PASS"); + console.log("PASS"); + } + expect_stdout: [ + "PASS", + "PASS", + "PASS", + "PASS", + ] +} + +reset_state_between_evaluate: { + options = { + evaluate: true, + passes: 2, + unsafe: true, + } + input: { + console.log(function() { + for (var a in /[abc4]/g.exec("a")) + return "PASS"; + return "FAIL"; + }()); + } + expect: { + console.log(function() { + for (var a in /[abc4]/g.exec("a")) + return "PASS"; + return "FAIL"; + }()); + } + expect_stdout: "PASS" +} diff --git a/test/compress/rename.js b/test/compress/rename.js new file mode 100644 index 00000000000..1d553f060b0 --- /dev/null +++ b/test/compress/rename.js @@ -0,0 +1,741 @@ +mangle_catch: { + rename = true + options = { + ie: false, + toplevel: false, + } + mangle = { + ie: false, + toplevel: false, + } + input: { + var a = "FAIL"; + try { + throw 1; + } catch (args) { + a = "PASS"; + } + console.log(a); + } + expect_exact: 'var a="FAIL";try{throw 1}catch(o){a="PASS"}console.log(a);' + expect_stdout: "PASS" +} + +mangle_catch_ie8: { + rename = true + options = { + ie: true, + toplevel: false, + } + mangle = { + ie: true, + toplevel: false, + } + input: { + var a = "FAIL"; + try { + throw 1; + } catch (args) { + a = "PASS"; + } + console.log(a); + } + expect_exact: 'var a="FAIL";try{throw 1}catch(args){a="PASS"}console.log(a);' + expect_stdout: "PASS" +} + +mangle_catch_var: { + rename = true + options = { + ie: false, + toplevel: false, + } + mangle = { + ie: false, + toplevel: false, + } + input: { + var a = "FAIL"; + try { + throw 1; + } catch (args) { + var a = "PASS"; + } + console.log(a); + } + expect_exact: 'var a="FAIL";try{throw 1}catch(o){var a="PASS"}console.log(a);' + expect_stdout: "PASS" +} + +mangle_catch_var_ie8: { + rename = true + options = { + ie: true, + toplevel: false, + } + mangle = { + ie: true, + toplevel: false, + } + input: { + var a = "FAIL"; + try { + throw 1; + } catch (args) { + var a = "PASS"; + } + console.log(a); + } + expect_exact: 'var a="FAIL";try{throw 1}catch(args){var a="PASS"}console.log(a);' + expect_stdout: "PASS" +} + +mangle_catch_toplevel: { + rename = true + options = { + ie: false, + toplevel: true, + } + mangle = { + ie: false, + toplevel: true, + } + input: { + var a = "FAIL"; + try { + throw 1; + } catch (args) { + a = "PASS"; + } + console.log(a); + } + expect_exact: 'var c="FAIL";try{throw 1}catch(o){c="PASS"}console.log(c);' + expect_stdout: "PASS" +} + +mangle_catch_ie8_toplevel: { + rename = true + options = { + ie: true, + toplevel: true, + } + mangle = { + ie: true, + toplevel: true, + } + input: { + var a = "FAIL"; + try { + throw 1; + } catch (args) { + a = "PASS"; + } + console.log(a); + } + expect_exact: 'var o="FAIL";try{throw 1}catch(c){o="PASS"}console.log(o);' + expect_stdout: "PASS" +} + +mangle_catch_var_toplevel: { + rename = true + options = { + ie: false, + toplevel: true, + } + mangle = { + ie: false, + toplevel: true, + } + input: { + var a = "FAIL"; + try { + throw 1; + } catch (args) { + var a = "PASS"; + } + console.log(a); + } + expect_exact: 'var r="FAIL";try{throw 1}catch(o){var r="PASS"}console.log(r);' + expect_stdout: "PASS" +} + +mangle_catch_var_ie8_toplevel: { + rename = true + options = { + ie: true, + toplevel: true, + } + mangle = { + ie: true, + toplevel: true, + } + input: { + var a = "FAIL"; + try { + throw 1; + } catch (args) { + var a = "PASS"; + } + console.log(a); + } + expect_exact: 'var o="FAIL";try{throw 1}catch(r){var o="PASS"}console.log(o);' + expect_stdout: "PASS" +} + +mangle_catch_redef_1: { + rename = true + options = { + ie: false, + toplevel: false, + } + mangle = { + ie: false, + toplevel: false, + } + input: { + var a = "PASS"; + try { + throw "FAIL1"; + } catch (a) { + var a = "FAIL2"; + } + console.log(a); + } + expect_exact: 'var a="PASS";try{throw"FAIL1"}catch(a){var a="FAIL2"}console.log(a);' + expect_stdout: "PASS" +} + +mangle_catch_redef_1_ie8: { + rename = true + options = { + ie: true, + toplevel: false, + } + mangle = { + ie: true, + toplevel: false, + } + input: { + var a = "PASS"; + try { + throw "FAIL1"; + } catch (a) { + var a = "FAIL2"; + } + console.log(a); + } + expect_exact: 'var a="PASS";try{throw"FAIL1"}catch(a){var a="FAIL2"}console.log(a);' + expect_stdout: "PASS" +} + +mangle_catch_redef_1_toplevel: { + rename = true + options = { + ie: false, + toplevel: true, + } + mangle = { + ie: false, + toplevel: true, + } + input: { + var a = "PASS"; + try { + throw "FAIL1"; + } catch (a) { + var a = "FAIL2"; + } + console.log(a); + } + expect_exact: 'var o="PASS";try{throw"FAIL1"}catch(o){var o="FAIL2"}console.log(o);' + expect_stdout: "PASS" +} + +mangle_catch_redef_1_ie8_toplevel: { + rename = true + options = { + ie: true, + toplevel: true, + } + mangle = { + ie: true, + toplevel: true, + } + input: { + var a = "PASS"; + try { + throw "FAIL1"; + } catch (a) { + var a = "FAIL2"; + } + console.log(a); + } + expect_exact: 'var o="PASS";try{throw"FAIL1"}catch(o){var o="FAIL2"}console.log(o);' + expect_stdout: "PASS" +} + +mangle_catch_redef_2: { + rename = true + options = { + ie: false, + toplevel: false, + } + mangle = { + ie: false, + toplevel: false, + } + input: { + try { + throw "FAIL1"; + } catch (a) { + var a = "FAIL2"; + } + console.log(a); + } + expect_exact: 'try{throw"FAIL1"}catch(a){var a="FAIL2"}console.log(a);' + expect_stdout: "undefined" +} + +mangle_catch_redef_2_ie8: { + rename = true + options = { + ie: true, + toplevel: false, + } + mangle = { + ie: true, + toplevel: false, + } + input: { + try { + throw "FAIL1"; + } catch (a) { + var a = "FAIL2"; + } + console.log(a); + } + expect_exact: 'try{throw"FAIL1"}catch(a){var a="FAIL2"}console.log(a);' + expect_stdout: "undefined" +} + +mangle_catch_redef_2_toplevel: { + rename = true + options = { + ie: false, + toplevel: true, + } + mangle = { + ie: false, + toplevel: true, + } + input: { + try { + throw "FAIL1"; + } catch (a) { + var a = "FAIL2"; + } + console.log(a); + } + expect_exact: 'try{throw"FAIL1"}catch(o){var o="FAIL2"}console.log(o);' + expect_stdout: "undefined" +} + +mangle_catch_redef_2_ie8_toplevel: { + rename = true + options = { + ie: true, + toplevel: true, + } + mangle = { + ie: true, + toplevel: true, + } + input: { + try { + throw "FAIL1"; + } catch (a) { + var a = "FAIL2"; + } + console.log(a); + } + expect_exact: 'try{throw"FAIL1"}catch(o){var o="FAIL2"}console.log(o);' + expect_stdout: "undefined" +} + +issue_2120_1: { + rename = true + mangle = { + ie: false, + } + input: { + "aaaaaaaa"; + var a = 1, b = "FAIL"; + try { + throw 1; + } catch (c) { + try { + throw 0; + } catch (a) { + if (c) b = "PASS"; + } + } + console.log(b); + } + expect: { + "aaaaaaaa"; + var a = 1, b = "FAIL"; + try { + throw 1; + } catch (t) { + try { + throw 0; + } catch (a) { + if (t) b = "PASS"; + } + } + console.log(b); + } + expect_stdout: "PASS" +} + +issue_2120_2: { + rename = true + mangle = { + ie: true, + } + input: { + "aaaaaaaa"; + var a = 1, b = "FAIL"; + try { + throw 1; + } catch (c) { + try { + throw 0; + } catch (a) { + if (c) b = "PASS"; + } + } + console.log(b); + } + expect: { + "aaaaaaaa"; + var a = 1, b = "FAIL"; + try { + throw 1; + } catch (c) { + try { + throw 0; + } catch (a) { + if (c) b = "PASS"; + } + } + console.log(b); + } + expect_stdout: "PASS" +} +function_iife_catch: { + rename = true + mangle = { + ie: false, + } + input: { + function f(n) { + !function() { + try { + throw 0; + } catch (n) { + var a = 1; + console.log(n, a); + } + }(); + } + f(); + } + expect_exact: "function f(o){!function(){try{throw 0}catch(o){var c=1;console.log(o,c)}}()}f();" + expect_stdout: "0 1" +} + +function_iife_catch_ie8: { + rename = true + mangle = { + ie: true, + } + input: { + function f(n) { + !function() { + try { + throw 0; + } catch (n) { + var a = 1; + console.log(n, a); + } + }(); + } + f(); + } + expect_exact: "function f(c){!function(){try{throw 0}catch(c){var o=1;console.log(c,o)}}()}f();" + expect_stdout: "0 1" +} + +function_catch_catch: { + rename = true + mangle = { + ie: false, + } + input: { + var o = 0; + function f() { + try { + throw 1; + } catch (c) { + try { + throw 2; + } catch (o) { + var o = 3; + console.log(o); + } + } + console.log(o); + } + f(); + } + expect_exact: "var o=0;function f(){try{throw 1}catch(o){try{throw 2}catch(c){var c=3;console.log(c)}}console.log(c)}f();" + expect_stdout: [ + "3", + "undefined", + ] +} + +function_catch_catch_ie8: { + rename = true + mangle = { + ie: true, + } + input: { + var o = 0; + function f() { + try { + throw 1; + } catch (c) { + try { + throw 2; + } catch (o) { + var o = 3; + console.log(o); + } + } + console.log(o); + } + f(); + } + expect_exact: "var o=0;function f(){try{throw 1}catch(c){try{throw 2}catch(o){var o=3;console.log(o)}}console.log(o)}f();" + expect_stdout: [ + "3", + "undefined", + ] +} + +function_do_catch_ie8: { + rename = true + options = { + ie: true, + side_effects: true, + unused: true, + } + mangle = { + ie: true, + toplevel: true, + } + input: { + var a = 1, b = 1, c = 0; + function d(e) { + var f, g, h, i; + do { + try { + try { + var j = function q(){}(); + } catch (r) { + --a && w("ddddddddeeeeeeegggggggggiiiiilllllllnnnnntuuuuuuuuyyyyyyy"); + var k, l, m, n, o; + --m; + --n; + --o; + } + try { + i[1]; + } catch (s) { + var p; + switch (function t() { + c++; + }()) { + case j + --p: + } + } + } catch (u) {} + } while (--i); + b--; + } + d(); + console.log(b, c); + } + expect: { + var u = 1, y = 1, a = 0; + function c(c) { + var d; + do { + try { + try { + var e = void 0; + } catch (i) { + --u && w("ddddddddeeeeeeegggggggggiiiiilllllllnnnnntuuuuuuuuyyyyyyy"); + 0; + 0; + 0; + } + try { + d[1]; + } catch (l) { + var g; + switch (function n() { + a++; + }()) { + case e + --g: + } + } + } catch (t) {} + } while (--d); + y--; + } + c(); + console.log(y, a); + } + expect_stdout: "0 1" +} + +issue_3480: { + rename = true, + mangle = { + ie: false, + toplevel: false, + } + input: { + var d, a, b, c = "FAIL"; + (function b() { + (function() { + try { + c = "PASS"; + } catch (b) { + } + })(); + })(); + console.log(c); + } + expect: { + var d, a, b, c = "FAIL"; + (function n() { + (function() { + try { + c = "PASS"; + } catch (c) {} + })(); + })(); + console.log(c); + } + expect_stdout: "PASS" +} + +issue_3480_ie8: { + rename = true, + mangle = { + ie: true, + toplevel: false, + } + input: { + var d, a, b, c = "FAIL"; + (function b() { + (function() { + try { + c = "PASS"; + } catch (b) { + } + })(); + })(); + console.log(c); + } + expect: { + var d, a, b, c = "FAIL"; + (function b() { + (function() { + try { + c = "PASS"; + } catch (b) {} + })(); + })(); + console.log(c); + } + expect_stdout: "PASS" +} + +issue_3480_toplevel: { + rename = true, + mangle = { + ie: false, + toplevel: true, + } + input: { + var d, a, b, c = "FAIL"; + (function b() { + (function() { + try { + c = "PASS"; + } catch (b) { + } + })(); + })(); + console.log(c); + } + expect: { + var c, n, o, t = "FAIL"; + (function c() { + (function() { + try { + t = "PASS"; + } catch (c) {} + })(); + })(); + console.log(t); + } + expect_stdout: "PASS" +} + +issue_3480_ie8_toplevel: { + rename = true, + mangle = { + ie: true, + toplevel: true, + } + input: { + var d, a, b, c = "FAIL"; + (function b() { + (function() { + try { + c = "PASS"; + } catch (b) { + } + })(); + })(); + console.log(c); + } + expect: { + var c, n, o, t = "FAIL"; + (function o() { + (function() { + try { + t = "PASS"; + } catch (o) {} + })(); + })(); + console.log(t); + } + expect_stdout: "PASS" +} diff --git a/test/compress/rests.js b/test/compress/rests.js new file mode 100644 index 00000000000..4d020d23eb9 --- /dev/null +++ b/test/compress/rests.js @@ -0,0 +1,1644 @@ +arrow_1: { + input: { + console.log.apply(console, ((...a) => a)("PASS", 42)); + } + expect_exact: 'console.log.apply(console,((...a)=>a)("PASS",42));' + expect_stdout: "PASS 42" + node_version: ">=6" +} + +arrow_2: { + input: { + console.log.apply(console, ((a, ...b) => b)("FAIL", "PASS", 42)); + } + expect_exact: 'console.log.apply(console,((a,...b)=>b)("FAIL","PASS",42));' + expect_stdout: "PASS 42" + node_version: ">=6" +} + +arrow_destructured_array_1: { + input: { + console.log.apply(console, (([ ...a ]) => a)("PASS")); + } + expect_exact: 'console.log.apply(console,(([...a])=>a)("PASS"));' + expect_stdout: "P A S S" + node_version: ">=6" +} + +arrow_destructured_array_2: { + input: { + console.log.apply(console, (([ a, ...b ]) => b)([ "FAIL", "PASS", 42 ])); + } + expect_exact: 'console.log.apply(console,(([a,...b])=>b)(["FAIL","PASS",42]));' + expect_stdout: "PASS 42" + node_version: ">=6" +} + +arrow_destructured_array_3: { + input: { + console.log((([ [ ...a ] = "FAIL" ]) => a)([ "PASS" ]).join("|")); + } + expect_exact: 'console.log((([[...a]="FAIL"])=>a)(["PASS"]).join("|"));' + expect_stdout: "P|A|S|S" + node_version: ">=6" +} + +arrow_destructured_object_1: { + input: { + var f = ({ ...a }) => a, o = f({ PASS: 42 }); + for (var k in o) + console.log(k, o[k]); + } + expect_exact: "var f=({...a})=>a,o=f({PASS:42});for(var k in o)console.log(k,o[k]);" + expect_stdout: "PASS 42" + node_version: ">=8.3.0" +} + +arrow_destructured_object_2: { + input: { + var f = ({ FAIL: a, ...b }) => b, o = f({ PASS: 42, FAIL: null }); + for (var k in o) + console.log(k, o[k]); + } + expect_exact: "var f=({FAIL:a,...b})=>b,o=f({PASS:42,FAIL:null});for(var k in o)console.log(k,o[k]);" + expect_stdout: "PASS 42" + node_version: ">=8.3.0" +} + +arrow_destructured_object_3: { + input: { + var f = ([ { ...a } = [ "FAIL" ] ]) => a; + var o = f([ "PASS" ]); + for (var k in o) + console.log(k, o[k]); + } + expect_exact: 'var f=([{...a}=["FAIL"]])=>a;var o=f(["PASS"]);for(var k in o)console.log(k,o[k]);' + expect_stdout: [ + "0 P", + "1 A", + "2 S", + "3 S", + ] + node_version: ">=8.3.0" +} + +funarg_1: { + input: { + console.log.apply(console, function(...a) { + return a; + }("PASS", 42)); + } + expect_exact: 'console.log.apply(console,function(...a){return a}("PASS",42));' + expect_stdout: "PASS 42" + node_version: ">=6" +} + +funarg_2: { + input: { + console.log.apply(console, function(a, ...b) { + return b; + }("FAIL", "PASS", 42)); + } + expect_exact: 'console.log.apply(console,function(a,...b){return b}("FAIL","PASS",42));' + expect_stdout: "PASS 42" + node_version: ">=6" +} + +destructured_array_1: { + input: { + var [ ...a ] = [ "PASS", 42 ]; + console.log.apply(console, a); + } + expect_exact: 'var[...a]=["PASS",42];console.log.apply(console,a);' + expect_stdout: "PASS 42" + node_version: ">=6" +} + +destructured_array_2: { + input: { + var [ a, ...b ] = [ "FAIL", "PASS", 42 ]; + console.log.apply(console, b); + } + expect_exact: 'var[a,...b]=["FAIL","PASS",42];console.log.apply(console,b);' + expect_stdout: "PASS 42" + node_version: ">=6" +} + +destructured_object_1: { + input: { + var { ...a } = [ "FAIL", "PASS", 42 ]; + console.log(a[1], a[2]); + } + expect_exact: 'var{...a}=["FAIL","PASS",42];console.log(a[1],a[2]);' + expect_stdout: "PASS 42" + node_version: ">=8.3.0" +} + +destructured_object_2: { + input: { + var { 0: a, ...b } = [ "FAIL", "PASS", 42 ]; + console.log(b[1], b[2]); + } + expect_exact: 'var{0:a,...b}=["FAIL","PASS",42];console.log(b[1],b[2]);' + expect_stdout: "PASS 42" + node_version: ">=8.3.0" +} + +drop_fargs: { + options = { + keep_fargs: false, + rests: true, + unused: true, + } + input: { + console.log(function(a, ...b) { + return b[0]; + }("FAIL", "PASS")); + } + expect: { + console.log(function(b) { + return b[0]; + }([ "PASS" ])); + } + expect_stdout: "PASS" + node_version: ">=6" +} + +inline: { + options = { + inline: true, + toplevel: true, + } + input: { + console.log(function(a, ...[ b, c ]) { + return c + b + a; + }("SS", "A", "P")); + } + expect: { + console.log(([ a, ...[ b, c ] ] = [ "SS", "A", "P" ], c + b + a)); + var a, b, c; + } + expect_stdout: "PASS" + node_version: ">=6" +} + +retain_var: { + options = { + unused: true, + } + input: { + var [ ...a ] = [ "PASS" ]; + console.log(a[0]); + } + expect: { + var [ ...a ] = [ "PASS" ]; + console.log(a[0]); + } + expect_stdout: "PASS" + node_version: ">=6" +} + +reduce_destructured_array: { + options = { + reduce_vars: true, + rests: true, + toplevel: true, + unused: true, + } + input: { + var [ ...a ] = [ "PASS" ]; + console.log(a[0]); + } + expect: { + console.log([ "PASS" ][0]); + } + expect_stdout: "PASS" + node_version: ">=6" +} + +reduce_destructured_object: { + options = { + reduce_vars: true, + toplevel: true, + unused: true, + } + input: { + var { ...a } = [ "PASS" ]; + console.log(a[0]); + } + expect: { + var { ...a } = [ "PASS" ]; + console.log(a[0]); + } + expect_stdout: "PASS" + node_version: ">=8.3.0" +} + +retain_destructured_array: { + options = { + toplevel: true, + unused: true, + } + input: { + var [ a, ...b ] = [ "FAIL", "PASS", 42 ]; + console.log.apply(console, b); + } + expect: { + var [ ...b ] = [ "PASS", 42 ]; + console.log.apply(console, b); + } + expect_stdout: "PASS 42" + node_version: ">=6" +} + +retain_destructured_object_1: { + options = { + toplevel: true, + unused: true, + } + input: { + var { 0: a, ...b } = [ "FAIL", "PASS", 42 ]; + for (var k in b) + console.log(k, b[k]); + } + expect: { + var { 0: a, ...b } = [ "FAIL", "PASS", 42 ]; + for (var k in b) + console.log(k, b[k]); + } + expect_stdout: [ + "1 PASS", + "2 42", + ] + node_version: ">=8.3.0" +} + +retain_destructured_object_2: { + options = { + toplevel: true, + unused: true, + } + input: { + var { foo: [ a ], ...b } = { foo: [ "FAIL" ], bar: "PASS", baz: 42 }; + for (var k in b) + console.log(k, b[k]); + } + expect: { + var { foo: {}, ...b } = { foo: 0, bar: "PASS", baz: 42 }; + for (var k in b) + console.log(k, b[k]); + } + expect_stdout: [ + "bar PASS", + "baz 42", + ] + node_version: ">=8.3.0" +} + +retain_funarg_destructured_array_1: { + options = { + inline: true, + keep_fargs: false, + pure_getters: "strict", + unused: true, + } + input: { + console.log((([ ...a ]) => a)([ "PASS" ])[0]); + } + expect: { + console.log((([ ...a ]) => a)([ "PASS" ])[0]); + } + expect_stdout: "PASS" + node_version: ">=6" +} + +retain_funarg_destructured_array_2: { + options = { + unused: true, + } + input: { + console.log(function([ a, ...b ]) { + return b; + }("bar")[1]); + } + expect: { + console.log(function([ , ...b ]) { + return b; + }("bar")[1]); + } + expect_stdout: "r" + node_version: ">=6" +} + +retain_funarg_destructured_object_1: { + options = { + inline: true, + keep_fargs: false, + pure_getters: "strict", + unused: true, + } + input: { + console.log((({ ...a }) => a)([ "PASS" ])[0]); + } + expect: { + console.log((({ ...a }) => a)([ "PASS" ])[0]); + } + expect_stdout: "PASS" + node_version: ">=8.3.0" +} + +retain_funarg_destructured_object_2: { + options = { + keep_fargs: false, + unused: true, + } + input: { + console.log(function({ p: a, ... b }) { + return b; + }({ p: "FAIL" }).p || "PASS"); + } + expect: { + console.log(function({ p: a, ... b }) { + return b; + }({}).p || "PASS"); + } + expect_stdout: "PASS" + node_version: ">=8.3.0" +} + +drop_unused_call_args_1: { + options = { + rests: true, + unused: true, + } + input: { + (function(...a) { + console.log(a[0]); + })(42, console.log("PASS")); + } + expect: { + (function(a) { + console.log(a[0]); + })([ 42, console.log("PASS") ]); + } + expect_stdout: [ + "PASS", + "42", + ] + node_version: ">=6" +} + +drop_unused_call_args_2: { + options = { + keep_fargs: false, + rests: true, + unused: true, + } + input: { + console.log(function(a, ...b) { + return b; + }(console).length); + } + expect: { + console.log(function(b) { + return b; + }((console, [])).length); + } + expect_stdout: "0" + node_version: ">=6" +} + +maintain_position: { + options = { + unused: true, + } + input: { + A = "FAIL"; + var [ , ...a ] = [ A, "PASS" ]; + console.log(a[0]); + } + expect: { + A = "FAIL"; + var [ , ...a ] = [ A, "PASS" ]; + console.log(a[0]); + } + expect_stdout: "PASS" + node_version: ">=6" +} + +merge_funarg: { + options = { + merge_vars: true, + } + input: { + (function(...a) { + var b = a.length; + console.log(b); + })(); + } + expect: { + (function(...a) { + var a = a.length; + console.log(a); + })(); + } + expect_stdout: "0" + node_version: ">=6" +} + +merge_funarg_destructured_array: { + options = { + merge_vars: true, + } + input: { + (function([ ...a ]) { + var b = a.length; + console.log(b); + })([]); + } + expect: { + (function([ ...a ]) { + var a = a.length; + console.log(a); + })([]); + } + expect_stdout: "0" + node_version: ">=6" +} + +merge_funarg_destructured_object: { + options = { + merge_vars: true, + } + input: { + (function({ ...a }) { + var b = a[0]; + console.log(b); + })([ "PASS" ]); + } + expect: { + (function({ ...a }) { + var a = a[0]; + console.log(a); + })([ "PASS" ]); + } + expect_stdout: "PASS" + node_version: ">=8.3.0" +} + +keep_arguments: { + options = { + arguments: true, + keep_fargs: false, + } + input: { + (function(...[ {} ]) { + console.log(arguments[0]); + })("PASS"); + } + expect: { + (function(...[ {} ]) { + console.log(arguments[0]); + })("PASS"); + } + expect_stdout: "PASS" + node_version: ">=6" +} + +drop_rest_array: { + options = { + rests: true, + } + input: { + var [ ...[ a ] ] = [ "PASS" ]; + console.log(a); + } + expect: { + var [ a ] = [ "PASS" ]; + console.log(a); + } + expect_stdout: "PASS" + node_version: ">=6" +} + +drop_rest_arrow: { + options = { + arrows: true, + keep_fargs: false, + reduce_vars: true, + rests: true, + } + input: { + console.log(((...[ a ]) => a)("PASS")); + } + expect: { + console.log((a => a)("PASS")); + } + expect_stdout: "PASS" + node_version: ">=6" +} + +drop_rest_lambda: { + options = { + keep_fargs: false, + reduce_vars: true, + rests: true, + toplevel: true, + } + input: { + function f(...[ a ]) { + return a; + } + console.log(f("PASS"), f(42)); + } + expect: { + function f(a) { + return a; + } + console.log(f("PASS"), f(42)); + } + expect_stdout: "PASS 42" + node_version: ">=6" +} + +keep_rest_array: { + options = { + rests: true, + } + input: { + var [ ...[ ...a ] ] = "PASS"; + console.log(a.join("")); + } + expect: { + var [ ...a ] = "PASS"; + console.log(a.join("")); + } + expect_stdout: "PASS" + node_version: ">=6" +} + +keep_rest_arrow: { + options = { + arrows: true, + keep_fargs: false, + reduce_vars: true, + rests: true, + } + input: { + console.log(((...[ ...a ]) => a.join(""))("PASS")); + } + expect: { + console.log(((...a) => a.join(""))("PASS")); + } + expect_stdout: "PASS" + node_version: ">=6" +} + +keep_rest_lambda_1: { + options = { + keep_fargs: false, + reduce_vars: true, + rests: true, + toplevel: true, + } + input: { + function f(...[ ...a ]) { + return a.join(""); + } + console.log(f("PASS"), f([ 42 ])); + } + expect: { + function f(...a) { + return a.join(""); + } + console.log(f("PASS"), f([ 42 ])); + } + expect_stdout: "PASS 42" + node_version: ">=6" +} + +keep_rest_lambda_2: { + options = { + unused: true, + } + input: { + function f(...[ ...a ]) { + return a.join(""); + } + console.log(f("PASS"), f([ 42 ])); + } + expect: { + function f(...[ ...a ]) { + return a.join(""); + } + console.log(f("PASS"), f([ 42 ])); + } + expect_stdout: "PASS 42" + node_version: ">=6" +} + +drop_new_function: { + options = { + side_effects: true, + } + input: { + new function(...{ + [console.log("PASS")]: a, + }) {}(); + } + expect: { + void ([ ... { + [console.log("PASS")]: [][0], + }] = []); + } + expect_stdout: "PASS" + node_version: ">=6" +} + +issue_4525_1: { + options = { + arguments: true, + } + input: { + console.log(function(a, ...[]) { + a = "FAIL"; + return arguments[0]; + }("PASS")); + } + expect: { + console.log(function(a, ...[]) { + a = "FAIL"; + return arguments[0]; + }("PASS")); + } + expect_stdout: "PASS" + node_version: ">=6" +} + +issue_4525_2: { + options = { + unused: true, + } + input: { + console.log(function(a, ...[]) { + a = "FAIL"; + return arguments[0]; + }("PASS")); + } + expect: { + console.log(function(a, ...[]) { + a = "FAIL"; + return arguments[0]; + }("PASS")); + } + expect_stdout: "PASS" + node_version: ">=6" +} + +issue_4538: { + options = { + rests: true, + unused: true, + } + input: { + console.log(typeof function f(...a) { + return a.p, f; + }()()); + } + expect: { + console.log(typeof function f(...a) { + return a.p, f; + }()()); + } + expect_stdout: "function" + node_version: ">=6" +} + +issue_4544_1: { + options = { + keep_fnames: true, + side_effects: true, + } + input: { + try { + (function f(...[ {} ]) {})(); + } catch (e) { + console.log("PASS"); + } + } + expect: { + try { + [ ...[ {} ] ] = []; + } catch (e) { + console.log("PASS"); + } + } + expect_stdout: "PASS" + node_version: ">=6" +} + +issue_4544_2: { + options = { + keep_fnames: true, + side_effects: true, + } + input: { + try { + (function f(a, ...[ {} ]) {})([]); + } catch (e) { + console.log("PASS"); + } + } + expect: { + try { + [ , ...[ {} ] ] = [ [] ]; + } catch (e) { + console.log("PASS"); + } + } + expect_stdout: "PASS" + node_version: ">=6" +} + +issue_4560_1: { + options = { + evaluate: true, + reduce_vars: true, + toplevel: true, + } + input: { + var a = 0; + (function(...{ + [a++]: {}, + }) {})(2); + console.log(a); + } + expect: { + var a = 0; + (function(...{ + [a++]: {}, + }) {})(2); + console.log(a); + } + expect_stdout: "1" + node_version: ">=6" +} + +issue_4560_2: { + options = { + reduce_vars: true, + toplevel: true, + unused: true, + } + input: { + var a = 0; + (function(...{ + [a++]: {}, + }) {})(2); + console.log(a); + } + expect: { + var a = 0; + (function(...{ + [a++]: {}, + }) {})(2); + console.log(a); + } + expect_stdout: "1" + node_version: ">=6" +} + +issue_4560_3: { + options = { + collapse_vars: true, + reduce_vars: true, + toplevel: true, + } + input: { + var a = 0, b; + [ ...{ + [a++]: b, + } ] = [ "PASS" ]; + console.log(b); + } + expect: { + var a = 0, b; + [ ...{ + [a++]: b, + } ] = [ "PASS" ]; + console.log(b); + } + expect_stdout: "PASS" + node_version: ">=6" +} + +issue_4562: { + options = { + evaluate: true, + reduce_vars: true, + rests: true, + unsafe: true, + } + input: { + console.log((([ ...[ a ] ]) => a)("foo")); + } + expect: { + console.log((([ a ]) => a)("foo")); + } + expect_stdout: "f" + node_version: ">=6" +} + +issue_4575: { + options = { + collapse_vars: true, + ie: true, + reduce_vars: true, + rests: true, + unused: true, + } + input: { + (function(a) { + var b = a; + var c = function a(...d) { + console.log(d.length); + }(); + })(); + } + expect: { + (function(a) { + (function(d) { + console.log(d.length); + })([]); + })(); + } + expect_stdout: "0" + node_version: ">=6" +} + +issue_4621: { + options = { + side_effects: true, + } + input: { + (function f(a, ...{ + [console.log(a)]: b, + }) {})("PASS"); + } + expect: { + (function f(a, ...{ + [console.log(a)]: b, + }) {})("PASS"); + } + expect_stdout: "PASS" + node_version: ">=6" +} + +issue_4644_1: { + options = { + evaluate: true, + } + input: { + var a = "FAIL"; + (function f(b, ...{ + [a = "PASS"]: c, + }) { + return b; + })(); + console.log(a); + } + expect: { + var a = "FAIL"; + (function f(b, ...{ + [a = "PASS"]: c, + }) { + return b; + })(); + console.log(a); + } + expect_stdout: "PASS" + node_version: ">=6" +} + +issue_4644_2: { + options = { + evaluate: true, + unsafe: true, + } + input: { + console.log(function(...a) { + return a[1]; + }("FAIL", "PASS"), function(...b) { + return b.length; + }(), function(c, ...d) { + return d[0]; + }("FAIL")); + } + expect: { + console.log("PASS", 0, function(c, ...d) { + return d[0]; + }("FAIL")); + } + expect_stdout: "PASS 0 undefined" + node_version: ">=6" +} + +issue_4666: { + options = { + evaluate: true, + reduce_vars: true, + rests: true, + toplevel: true, + unsafe: true, + unused: true, + } + input: { + var a = 0, b = 0; + var o = ((...c) => a++ + c)(b); + for (var k in o) + b++; + console.log(a, b); + } + expect: { + var a = 0, b = 0; + var o = (c => +a + c)([ b ]); + for (var k in o) + b++; + console.log(1, b); + } + expect_stdout: "1 2" + node_version: ">=6" +} + +issue_5089_1: { + options = { + unused: true, + } + input: { + var { + p: [] = 42, + ...o + } = { + p: [], + }; + console.log(o.p); + } + expect: { + var { + p: {}, + ...o + } = { + p: 0, + }; + console.log(o.p); + } + expect_stdout: "undefined" + node_version: ">=8.3.0" +} + +issue_5089_2: { + options = { + pure_getters: "strict", + unused: true, + } + input: { + var { + p: {} = null, + ...o + } = { + p: {}, + }; + console.log(o.p); + } + expect: { + var { + p: {}, + ...o + } = { + p: 0, + }; + console.log(o.p); + } + expect_stdout: "undefined" + node_version: ">=8.3.0" +} + +issue_5100_1: { + options = { + passes: 2, + pure_getters: "strict", + side_effects: true, + unused: true, + } + input: { + var a; + [ { + p: {}, + ...a + } ] = [ { + p: { + q: a, + } = 42, + r: "PASS", + } ]; + console.log(a.r); + } + expect: { + var a; + ({ + p: {}, + ...a + } = [ { + p: { + q: a, + } = 42, + r: "PASS", + } ][0]); + console.log(a.r); + } + expect_stdout: "PASS" + node_version: ">=8.3.0" +} + +issue_5100_2: { + options = { + passes: 2, + pure_getters: "strict", + side_effects: true, + unused: true, + } + input: { + var a; + [ { + p: {}, + ...a + } ] = [ { + p: (console.log("PASS"), { + q: a, + } = 42), + } ]; + } + expect: { + var a; + ({ + p: {}, + ...a + } = [ { + p: (console.log("PASS"), { + q: a, + } = 42), + } ][0]); + } + expect_stdout: "PASS" + node_version: ">=10.22.0" +} + +issue_5108: { + options = { + evaluate: true, + keep_fargs: false, + reduce_vars: true, + rests: true, + unsafe: true, + unused: true, + } + input: { + console.log(function([ ...[ a ] ]) { + return a; + }([ "PASS", "FAIL" ])); + } + expect: { + console.log("PASS"); + } + expect_stdout: "PASS" + node_version: ">=6" +} + +issue_5128_1: { + options = { + inline: true, + } + input: { + console.log(function() { + return function f(...[ a ]) { + return a; + }("PASS"); + }()); + } + expect: { + console.log(function f(...[ a ]) { + return a; + }("PASS")); + } + expect_stdout: "PASS" + node_version: ">=6" +} + +issue_5128_2: { + options = { + inline: true, + keep_fnames: true, + unused: true, + } + input: { + console.log(function() { + return function f(...[ a ]) { + return a; + }("PASS"); + }()); + } + expect: { + console.log(function f(...[ a ]) { + return a; + }("PASS")); + } + expect_stdout: "PASS" + node_version: ">=6" +} + +issue_5165_1: { + options = { + conditionals: true, + dead_code: true, + evaluate: true, + reduce_vars: true, + rests: true, + side_effects: true, + switches: true, + unsafe: true, + } + input: { + console.log(function([ ...a ]) { + switch (a) { + case a: + return "PASS"; + } + }([])); + } + expect: { + console.log(function([ ...a ]) { + return "PASS"; + }([])); + } + expect_stdout: "PASS" + node_version: ">=6" +} + +issue_5165_2: { + options = { + conditionals: true, + dead_code: true, + evaluate: true, + reduce_vars: true, + rests: true, + side_effects: true, + switches: true, + unsafe: true, + } + input: { + console.log(function(...a) { + switch (a) { + case a: + return "PASS"; + } + }()); + } + expect: { + console.log("PASS"); + } + expect_stdout: "PASS" + node_version: ">=6" +} + +issue_5246_1: { + options = { + keep_fargs: false, + reduce_vars: true, + rests: true, + unused: true, + } + input: { + console.log(typeof function([ , ...a ]) { + return this && a; + }([ , function(){} ])[0]); + } + expect: { + console.log(typeof function() { + return this && [ function(){} ]; + }()[0]); + } + expect_stdout: "function" + node_version: ">=6" +} + +issue_5246_2: { + options = { + keep_fargs: false, + reduce_vars: true, + rests: true, + toplevel: true, + unused: true, + } + input: { + A = [ , "PASS", "FAIL" ]; + var [ , ...a ] = [ ... A ]; + console.log(a[0]); + } + expect: { + A = [ , "PASS", "FAIL" ]; + var [ , ...a ] = [ ... A ]; + console.log(a[0]); + } + expect_stdout: "PASS" + node_version: ">=6" +} + +issue_5246_3: { + options = { + keep_fargs: false, + unused: true, + } + input: { + (function f(...[ [ a ] ]) { + console.log(a); + })([ "PASS" ]); + } + expect: { + (function(...[ a ]) { + console.log(a); + })([ "PASS" ][0]); + } + expect_stdout: "PASS" + node_version: ">=6" +} + +issue_5360: { + options = { + keep_fargs: false, + pure_getters: "strict", + unused: true, + } + input: { + var a; + console.log(function({ p: {}, ...b }) { + return b.q; + }({ + p: ~a && ([ a ] = []), + q: "PASS", + })); + } + expect: { + var a; + console.log(function({ p: {}, ...b }) { + return b.q; + }({ + p: ~a && ([ a ] = []), + q: "PASS", + })); + } + expect_stdout: "PASS" + node_version: ">=8.3.0" +} + +issue_5370: { + options = { + dead_code: true, + ie: true, + unused: true, + } + input: { + console.log(function arguments(...a) { + return arguments; + try {} catch (e) { + var arguments; + } + }()); + } + expect: { + console.log(function arguments(...a) { + return arguments; + var arguments; + }()); + } + expect_stdout: true + node_version: ">=6" +} + +issue_5391: { + options = { + evaluate: true, + keep_fargs: false, + objects: true, + pure_getters: "strict", + reduce_vars: true, + side_effects: true, + toplevel: true, + unused: true, + } + input: { + var a, b = function f({ + p: {}, + ...c + }) { + while (c.q); + }({ + p: { + r: a++, + r: 0, + } + }); + console.log(a); + } + expect: { + (function({ + p: {}, + ...c + }) { + while (c.q); + })({ + p: 0, + }); + console.log(NaN); + } + expect_stdout: "NaN" + node_version: ">=8.3.0" +} + +issue_5533_1_keep_fargs: { + options = { + evaluate: true, + inline: true, + join_vars: true, + keep_fargs: true, + loops: true, + side_effects: true, + unused: true, + } + input: { + "use strict"; + try { + (function() { + var a; + for (; 1;) + a = function() { + (function f(...b) { + b; + throw "PASS"; + })(); + }(); + })(); + } catch (e) { + console.log(e); + } + } + expect: { + "use strict"; + try { + (function() { + for (;;) + throw "PASS"; + })(); + } catch (e) { + console.log(e); + } + } + expect_stdout: "PASS" + node_version: ">=6" +} + +issue_5533_1_drop_fargs: { + options = { + evaluate: true, + inline: true, + join_vars: true, + keep_fargs: false, + loops: true, + side_effects: true, + unused: true, + } + input: { + "use strict"; + try { + (function() { + var a; + for (; 1;) + a = function() { + (function f(...b) { + b; + throw "PASS"; + })(); + }(); + })(); + } catch (e) { + console.log(e); + } + } + expect: { + "use strict"; + try { + (function() { + for (;;) + throw "PASS"; + })(); + } catch (e) { + console.log(e); + } + } + expect_stdout: "PASS" + node_version: ">=6" +} + +issue_5533_2_keep_fargs: { + options = { + evaluate: true, + inline: true, + join_vars: true, + keep_fargs: true, + loops: true, + side_effects: true, + unused: true, + } + input: { + "use strict"; + try { + (function() { + var a; + for (; 1;) + a = function() { + (function f(...[ b ]) { + b; + throw "PASS"; + })(); + }(); + })(); + } catch (e) { + console.log(e); + } + } + expect: { + "use strict"; + try { + (function() { + for (;;) + throw "PASS"; + })(); + } catch (e) { + console.log(e); + } + } + expect_stdout: "PASS" + node_version: ">=6" +} + +issue_5533_2_drop_fargs: { + options = { + evaluate: true, + inline: true, + join_vars: true, + keep_fargs: false, + loops: true, + side_effects: true, + unused: true, + } + input: { + "use strict"; + try { + (function() { + var a; + for (; 1;) + a = function() { + (function f(...[ b ]) { + b; + throw "PASS"; + })(); + }(); + })(); + } catch (e) { + console.log(e); + } + } + expect: { + "use strict"; + try { + (function() { + for (;;) + throw "PASS"; + })(); + } catch (e) { + console.log(e); + } + } + expect_stdout: "PASS" + node_version: ">=6" +} + +issue_5552_1: { + options = { + collapse_vars: true, + reduce_vars: true, + toplevel: true, + } + input: { + var log = console.log; + var a = f, b = log(); + function f(...[ c = a = "PASS" ]) {} + f((a = "FAIL", b)); + log(a); + } + expect: { + var log = console.log; + var a = f, b = log(); + function f(...[ c = a = "PASS" ]) {} + f((a = "FAIL", b)); + log(a); + } + expect_stdout: [ + "", + "PASS", + ] + node_version: ">=6" +} + +issue_5552_2: { + options = { + collapse_vars: true, + reduce_vars: true, + toplevel: true, + } + input: { + var log = console.log; + var a = f; + function f(...{ [a = "PASS"]: b }) {} + f((a = "FAIL", 42)); + log(a); + } + expect: { + var log = console.log; + var a = f; + function f(...{ [a = "PASS"]: b }) {} + f((a = "FAIL", 42)); + log(a); + } + expect_stdout: "PASS" + node_version: ">=6" +} + +issue_5552_3: { + options = { + collapse_vars: true, + unused: true, + } + input: { + var a = [ "FAIL", "PASS" ]; + console.log(function(b, ...[ c = a.pop() ]) { + return b; + }(a.pop())); + } + expect: { + var a = [ "FAIL", "PASS" ]; + console.log(function(b, ...[ c = a.pop() ]) { + return b; + }(a.pop())); + } + expect_stdout: "PASS" + node_version: ">=6" +} + +issue_5552_4: { + options = { + collapse_vars: true, + unused: true, + } + input: { + var a = [ "FAIL", "PASS" ]; + console.log(function(b, ...{ [a.pop()]: c }) { + return b; + }(a.pop())); + } + expect: { + var a = [ "FAIL", "PASS" ]; + console.log(function(b, ...{ [a.pop()]: c }) { + return b; + }(a.pop())); + } + expect_stdout: "PASS" + node_version: ">=6" +} + +issue_5705: { + options = { + reduce_vars: true, + rests: true, + unused: true, + } + input: { + (function(...a) { + var b = { ...a }; + })(console.log("PASS")); + } + expect: { + (function() {})(console.log("PASS")); + } + expect_stdout: "PASS" + node_version: ">=8.3.0" +} diff --git a/test/compress/return_undefined.js b/test/compress/return_undefined.js new file mode 100644 index 00000000000..0f21a2f7499 --- /dev/null +++ b/test/compress/return_undefined.js @@ -0,0 +1,145 @@ +return_undefined: { + options = { + booleans: true, + comparisons: true, + conditionals: true, + dead_code: true, + drop_debugger: true, + evaluate: true, + hoist_funs: true, + hoist_vars: true, + if_return: true, + join_vars: true, + keep_fargs: true, + keep_fnames: false, + loops: true, + negate_iife: true, + properties: true, + sequences: false, + side_effects: true, + unused: true, + } + input: { + function f0() { + } + function f1() { + return undefined; + } + function f2() { + return void 0; + } + function f3() { + return void 123; + } + function f4() { + return; + } + function f5(a, b) { + console.log(a, b); + baz(a); + return; + } + function f6(a, b) { + console.log(a, b); + if (a) { + foo(b); + baz(a); + return a + b; + } + return undefined; + } + function f7(a, b) { + console.log(a, b); + if (a) { + foo(b); + baz(a); + return void 0; + } + return a + b; + } + function f8(a, b) { + foo(a); + bar(b); + return void 0; + } + function f9(a, b) { + foo(a); + bar(b); + return undefined; + } + function f10() { + return false; + } + function f11() { + return null; + } + function f12() { + return 0; + } + } + expect: { + function f0() {} + function f1() {} + function f2() {} + function f3() {} + function f4() {} + function f5(a, b) { + console.log(a, b); + baz(a); + } + function f6(a, b) { + console.log(a, b); + if (a) { + foo(b); + baz(a); + return a + b; + } + } + function f7(a, b) { + console.log(a, b); + if (!a) + return a + b; + foo(b); + baz(a); + } + function f8(a, b) { + foo(a); + bar(b); + } + function f9(a, b) { + foo(a); + bar(b); + } + function f10() { + return !1; + } + function f11() { + return null; + } + function f12() { + return 0; + } + } +} + +return_void: { + options = { + if_return: true, + inline: true, + reduce_vars: true, + unused: true, + } + input: { + function f() { + function g() { + h(); + } + return g(); + } + } + expect: { + function f() { + h(); + } + } +} diff --git a/test/compress/sandbox.js b/test/compress/sandbox.js new file mode 100644 index 00000000000..60d56311fa6 --- /dev/null +++ b/test/compress/sandbox.js @@ -0,0 +1,217 @@ +console_log: { + input: { + console.log("%% %s"); + console.log("%% %s", "%s"); + } + expect: { + console.log("%% %s"); + console.log("%% %s", "%s"); + } + expect_stdout: [ + "%% %s", + "% %s", + ] +} + +console_log_console: { + input: { + var log = console.log; + log(console); + log(typeof console.log); + } + expect: { + var log = console.log; + log(console); + log(typeof console.log); + } + expect_stdout: [ + "{ log: 'function(){}' }", + "function", + ] +} + +typeof_arguments: { + options = { + evaluate: true, + reduce_vars: true, + toplevel: true, + unused: true, + } + input: { + var arguments; + console.log((typeof arguments).length); + } + expect: { + var arguments; + console.log((typeof arguments).length); + } + expect_stdout: "6" +} + +typeof_arguments_assigned: { + options = { + evaluate: true, + reduce_vars: true, + toplevel: true, + unused: true, + } + input: { + var arguments = void 0; + console.log((typeof arguments).length); + } + expect: { + console.log("undefined".length); + } + expect_stdout: "9" +} + +toplevel_Infinity_NaN_undefined: { + options = { + evaluate: true, + reduce_vars: true, + toplevel: true, + unused: true, + } + input: { + var Infinity = "foo"; + var NaN = 42; + var undefined = null; + console.log(Infinity, NaN, undefined); + } + expect: { + console.log("foo", 42, null); + } + expect_stdout: "foo 42 null" +} + +log_global: { + input: { + console.log(function() { + return this; + }()); + } + expect: { + console.log(function() { + return this; + }()); + } + expect_stdout: "[object global]" +} + +log_nested: { + options = { + unused: true, + } + input: { + var o = { p: 42 }; + for (var i = 0; i < 10; i++) + o = { + p: o, + q: function foo() {}, + }; + console.log(o); + } + expect: { + var o = { p: 42 }; + for (var i = 0; i < 10; i++) + o = { + p: o, + q: function() {}, + }; + console.log(o); + } + expect_stdout: true +} + +timers: { + options = { + evaluate: true, + reduce_vars: true, + toplevel: true, + unused: true, + } + input: { + var count = 0, interval = 1000, duration = 3210; + var timer = setInterval(function() { + if (!count++) setTimeout(function() { + clearInterval(timer); + console.log(count <= 4 ? "PASS" : "FAIL"); + }, duration); + }, interval); + } + expect: { + var count = 0; + var timer = setInterval(function() { + if (!count++) setTimeout(function() { + clearInterval(timer); + console.log(count <= 4 ? "PASS" : "FAIL"); + }, 3210); + }, 1000); + } + expect_stdout: "PASS" + node_version: ">=0.12" +} + +issue_4054: { + input: { + console.log({ + set p(v) { + throw "FAIL"; + }, + }); + } + expect: { + console.log({ + set p(v) { + throw "FAIL"; + }, + }); + } + expect_stdout: "{ p: [Setter] }" +} + +issue_4811_1: { + input: { + for (var PASS in this); + console.log(PASS, this, {} < this); + } + expect: { + for (var PASS in this); + console.log(PASS, this, {} < this); + } + expect_stdout: "PASS [object global] true" +} + +issue_4811_2: { + options = { + side_effects: true, + } + input: { + (async function() {}); + for (var PASS in this); + console.log(PASS, this, {} < this); + } + expect: { + for (var PASS in this); + console.log(PASS, this, {} < this); + } + expect_stdout: "PASS [object global] true" + node_version: ">=8" +} + +issue_5197: { + rename = true + input: { + function f(async) { + async(")=>{}"); + } + console.log("" + this.__proto__); + } + expect: { + function f(a) { + a(")=>{}"); + } + console.log("" + this.__proto__); + } + expect_stdout: "[object global]" +} diff --git a/test/compress/sequences.js b/test/compress/sequences.js index 0e3319abbb9..93675364fa9 100644 --- a/test/compress/sequences.js +++ b/test/compress/sequences.js @@ -1,7 +1,7 @@ make_sequences_1: { options = { - sequences: true - }; + sequences: true, + } input: { foo(); bar(); @@ -14,8 +14,8 @@ make_sequences_1: { make_sequences_2: { options = { - sequences: true - }; + sequences: true, + } input: { if (boo) { foo(); @@ -35,8 +35,8 @@ make_sequences_2: { make_sequences_3: { options = { - sequences: true - }; + sequences: true, + } input: { function f() { foo(); @@ -61,8 +61,8 @@ make_sequences_3: { make_sequences_4: { options = { - sequences: true - }; + sequences: true, + } input: { x = 5; if (y) z(); @@ -86,10 +86,13 @@ make_sequences_4: { switch (x = 5, y) {} with (x = 5, obj); } + expect_stdout: true } lift_sequences_1: { - options = { sequences: true }; + options = { + sequences: true, + } input: { var foo, x, y, bar; foo = !(x(), y(), bar()); @@ -101,21 +104,30 @@ lift_sequences_1: { } lift_sequences_2: { - options = { sequences: true, evaluate: true }; + options = { + evaluate: true, + sequences: true, + } input: { - var foo, bar; + var foo = 1, bar; foo.x = (foo = {}, 10); bar = (bar = {}, 10); + console.log(foo, bar); } expect: { - var foo, bar; + var foo = 1, bar; foo.x = (foo = {}, 10), - bar = {}, bar = 10; + bar = {}, bar = 10, + console.log(foo, bar); } + expect_stdout: true } lift_sequences_3: { - options = { sequences: true, conditionals: true }; + options = { + conditionals: true, + sequences: true, + } input: { var x, foo, bar, baz; x = (foo(), bar(), baz()) ? 10 : 20; @@ -127,7 +139,9 @@ lift_sequences_3: { } lift_sequences_4: { - options = { side_effects: true }; + options = { + side_effects: true, + } input: { var x, foo, bar, baz; x = (foo, bar, baz); @@ -138,8 +152,27 @@ lift_sequences_4: { } } +lift_sequences_5: { + options = { + sequences: true, + } + input: { + var a = 2, b; + a *= (b, a = 4, 3); + console.log(a); + } + expect: { + var a = 2, b; + b, a *= (a = 4, 3), + console.log(a); + } + expect_stdout: "6" +} + for_sequences: { - options = { sequences: true }; + options = { + sequences: true, + } input: { // 1 foo(); @@ -155,6 +188,11 @@ for_sequences: { // 4 x = (foo in bar); for (y = 5; false;); + // 5 + x = function() { + foo in bar; + }; + for (y = 5; false;); } expect: { // 1 @@ -167,5 +205,1173 @@ for_sequences: { // 4 x = (foo in bar); for (y = 5; false;); + // 5 + for (x = function() { + foo in bar; + }, y = 5; false;); + } +} + +limit_1: { + options = { + sequences: 3, + } + input: { + a; + b; + c; + d; + e; + f; + g; + h; + i; + j; + k; + } + expect: { + a, b, c; + d, e, f; + g, h, i; + j, k; + } +} + +limit_2: { + options = { + sequences: 3, + } + input: { + a, b; + c, d; + e, f; + g, h; + i, j; + k; + } + expect: { + a, b, c, d; + e, f, g, h; + i, j, k; + } +} + +negate_iife_for: { + options = { + negate_iife: true, + sequences: true, + } + input: { + (function() {})(); + for (i = 0; i < 5; i++) console.log(i); + (function() {})(); + for (; i < 10; i++) console.log(i); + } + expect: { + for (!function() {}(), i = 0; i < 5; i++) console.log(i); + for (!function() {}(); i < 10; i++) console.log(i); + } + expect_stdout: true +} + +iife: { + options = { + sequences: true, + } + input: { + x = 42; + (function a() {})(); + !function b() {}(); + ~function c() {}(); + +function d() {}(); + -function e() {}(); + void function f() {}(); + typeof function g() {}(); + } + expect: { + x = 42, + function a() {}(), + !function b() {}(), + ~function c() {}(), + +function d() {}(), + -function e() {}(), + void function f() {}(), + typeof function g() {}(); + } +} + +iife_drop_side_effect_free: { + options = { + sequences: true, + side_effects: true, + } + input: { + x = 42; + (function a() {})(); + !function b() {}(); + ~function c() {}(); + +function d() {}(); + -function e() {}(); + void function f() {}(); + typeof function g() {}(); + } + expect: { + x = 42; + } +} + +unsafe_undefined: { + options = { + conditionals: true, + if_return: true, + sequences: true, + side_effects: true, + unsafe_undefined: true, + } + input: { + function f(undefined) { + if (a) + return b; + if (c) + return d; + } + function g(undefined) { + if (a) + return b; + if (c) + return d; + e(); + } + } + expect: { + function f(undefined) { + return a ? b : c ? d : undefined; + } + function g(undefined) { + return a ? b : c ? d : void e(); + } + } +} + +issue_1685: { + options = { + collapse_vars: true, + side_effects: true, + } + input: { + var a = 100, b = 10; + function f() { + var a = (a--, delete a && --b); + } + f(); + console.log(a, b); + } + expect: { + var a = 100, b = 10; + function f() { + var a = (a--, delete a && --b); + } + f(); + console.log(a, b); + } + expect_stdout: true +} + +func_def_1: { + options = { + collapse_vars: true, + side_effects: true, + } + input: { + function f() { + return f = 0, !!f; + } + console.log(f()); + } + expect: { + function f() { + return !!(f = 0); + } + console.log(f()); + } + expect_stdout: "false" +} + +func_def_2: { + options = { + collapse_vars: true, + side_effects: true, + } + input: { + console.log(function f() { + return f = 0, !!f; + }()); + } + expect: { + console.log(function f() { + return f = 0, !!f; + }()); + } + expect_stdout: "true" +} + +func_def_3: { + options = { + collapse_vars: true, + side_effects: true, + } + input: { + function f() { + function g() {} + return g = 0, !!g; + } + console.log(f()); + } + expect: { + function f() { + function g() {} + return !!(g = 0); + } + console.log(f()); + } + expect_stdout: "false" +} + +func_def_4: { + options = { + collapse_vars: true, + side_effects: true, + } + input: { + function f() { + function g() { + return g = 0, !!g; + } + return g(); + } + console.log(f()); + } + expect: { + function f() { + function g() { + return !!(g = 0); + } + return g(); + } + console.log(f()); + } + expect_stdout: "false" +} + +func_def_5: { + options = { + collapse_vars: true, + side_effects: true, + } + input: { + function f() { + return function g(){ + return g = 0, !!g; + }(); + } + console.log(f()); + } + expect: { + function f() { + return function g(){ + return g = 0, !!g; + }(); + } + console.log(f()); + } + expect_stdout: "true" +} + +issue_1758: { + options = { + sequences: true, + side_effects: true, + } + input: { + console.log(function(c) { + var undefined = 42; + return function() { + c--; + c--, c.toString(); + return; + }(); + }()); + } + expect: { + console.log(function(c) { + var undefined = 42; + return function() { + return c--, c--, void c.toString(); + }(); + }()); + } + expect_stdout: "undefined" +} + +delete_seq_1: { + options = { + booleans: true, + evaluate: true, + side_effects: true, + } + input: { + console.log(delete (1, undefined)); + console.log(delete (1, void 0)); + console.log(delete (1, Infinity)); + console.log(delete (1, 1 / 0)); + console.log(delete (1, NaN)); + console.log(delete (1, 0 / 0)); + } + expect: { + console.log(!0); + console.log(!0); + console.log(!0); + console.log(!0); + console.log(!0); + console.log(!0); + } + expect_stdout: true +} + +delete_seq_2: { + options = { + booleans: true, + evaluate: true, + side_effects: true, + } + input: { + console.log(delete (1, 2, undefined)); + console.log(delete (1, 2, void 0)); + console.log(delete (1, 2, Infinity)); + console.log(delete (1, 2, 1 / 0)); + console.log(delete (1, 2, NaN)); + console.log(delete (1, 2, 0 / 0)); + } + expect: { + console.log(!0); + console.log(!0); + console.log(!0); + console.log(!0); + console.log(!0); + console.log(!0); + } + expect_stdout: true +} + +delete_seq_3: { + options = { + booleans: true, + evaluate: true, + keep_infinity: true, + side_effects: true, + } + input: { + console.log(delete (1, 2, undefined)); + console.log(delete (1, 2, void 0)); + console.log(delete (1, 2, Infinity)); + console.log(delete (1, 2, 1 / 0)); + console.log(delete (1, 2, NaN)); + console.log(delete (1, 2, 0 / 0)); + } + expect: { + console.log(!0); + console.log(!0); + console.log(!0); + console.log(!0); + console.log(!0); + console.log(!0); + } + expect_stdout: true +} + +delete_seq_4: { + options = { + booleans: true, + evaluate: false, + sequences: true, + side_effects: true, + } + input: { + function f() {} + console.log(delete (f(), undefined)); + console.log(delete (f(), void 0)); + console.log(delete (f(), Infinity)); + console.log(delete (f(), 1 / 0)); + console.log(delete (f(), NaN)); + console.log(delete (f(), 0 / 0)); + } + expect: { + function f() {} + console.log(delete void f()), + console.log(delete void f()), + console.log((f(), delete (1 / 0))), + console.log((f(), delete (1 / 0))), + console.log(delete (f(), NaN)), + console.log((f(), delete(0 / 0))); + } + expect_stdout: true +} + +delete_seq_4_evaluate: { + options = { + booleans: true, + evaluate: true, + sequences: true, + side_effects: true, + } + input: { + function f() {} + console.log(delete (f(), undefined)); + console.log(delete (f(), void 0)); + console.log(delete (f(), Infinity)); + console.log(delete (f(), 1 / 0)); + console.log(delete (f(), NaN)); + console.log(delete (f(), 0 / 0)); + } + expect: { + function f() {} + console.log((f(), !0)), + console.log((f(), !0)), + console.log((f(), !0)), + console.log((f(), !0)), + console.log((f(), !0)), + console.log((f(), !0)); + } + expect_stdout: true +} + +delete_seq_5: { + options = { + booleans: true, + evaluate: false, + keep_infinity: true, + sequences: true, + side_effects: true, + } + input: { + function f() {} + console.log(delete (f(), undefined)); + console.log(delete (f(), void 0)); + console.log(delete (f(), Infinity)); + console.log(delete (f(), 1 / 0)); + console.log(delete (f(), NaN)); + console.log(delete (f(), 0 / 0)); + } + expect: { + function f() {} + console.log(delete void f()), + console.log(delete void f()), + console.log(delete (f(), Infinity)), + console.log((f(), delete (1 / 0))), + console.log(delete (f(), NaN)), + console.log((f(), delete (0 / 0))); + } + expect_stdout: true +} + +delete_seq_5_evaluate: { + options = { + booleans: true, + evaluate: true, + keep_infinity: true, + sequences: true, + side_effects: true, + } + input: { + function f() {} + console.log(delete (f(), undefined)); + console.log(delete (f(), void 0)); + console.log(delete (f(), Infinity)); + console.log(delete (f(), 1 / 0)); + console.log(delete (f(), NaN)); + console.log(delete (f(), 0 / 0)); + } + expect: { + function f() {} + console.log((f(), !0)), + console.log((f(), !0)), + console.log((f(), !0)), + console.log((f(), !0)), + console.log((f(), !0)), + console.log((f(), !0)); + } + expect_stdout: true +} + +delete_seq_6: { + options = { + booleans: true, + evaluate: true, + side_effects: true, + } + input: { + var a; + console.log(delete (1, a)); + } + expect: { + var a; + console.log(!0); + } + expect_stdout: true +} + +side_effects: { + options = { + sequences: true, + side_effects: true, + } + input: { + 0, a(), 1, b(), 2, c(), 3; + } + expect: { + a(), b(), c(); + } +} + +side_effects_cascade_1: { + options = { + collapse_vars: true, + conditionals: true, + sequences: true, + side_effects: true, + } + input: { + function f(a, b) { + a -= 42; + if (a < 0) a = 0; + b.a = a; + } + var m = {}, n = {}; + f(13, m); + f("foo", n); + console.log(m.a, n.a); + } + expect: { + function f(a, b) { + b.a = a = (a -= 42) < 0 ? 0 : a; + } + var m = {}, n = {}; + f(13, m), + f("foo", n), + console.log(m.a, n.a); + } + expect_stdout: "0 NaN" +} + +side_effects_cascade_2: { + options = { + collapse_vars: true, + side_effects: true, + } + input: { + function f(a, b) { + b = a, + !a + (b += a) || (b += a), + b = a, + b; + } + } + expect: { + function f(a, b) { + !(b = a) + (b += a) || (b += a), + b = a; + } + } +} + +side_effects_cascade_3: { + options = { + collapse_vars: true, + conditionals: true, + side_effects: true, + } + input: { + function f(a, b) { + "foo" ^ (b += a), + b ? false : (b = a) ? -1 : (b -= a) - (b ^= a), + a-- || !a, + a; + } + } + expect: { + function f(a, b) { + (b += a) || (b = a) || (b = b - a ^ a), + a--; + } + } +} + +issue_27: { + options = { + collapse_vars: true, + passes: 2, + sequences: true, + side_effects: true, + unused: true, + } + input: { + (function(jQuery) { + var $; + $ = jQuery; + $("body").addClass("foo"); + })(jQuery); + } + expect: { + (function(jQuery) { + jQuery("body").addClass("foo"); + })(jQuery); + } +} + +issue_2062: { + options = { + booleans: true, + collapse_vars: true, + conditionals: true, + side_effects: true, + } + input: { + var a = 1; + if ([ a || a++ + a--, a++ + a--, a && a.var ]); + console.log(a); + } + expect: { + var a = 1; + a || (a++, a--), a++, --a && a.var; + console.log(a); + } + expect_stdout: "1" +} + +issue_2313: { + options = { + collapse_vars: true, + sequences: true, + side_effects: true, + } + input: { + var a = 0, b = 0; + var foo = { + get c() { + a++; + return 42; + }, + set c(c) { + b++; + }, + d: function() { + this.c++; + if (this.c) console.log(a, b); + } + } + foo.d(); + } + expect: { + var a = 0, b = 0; + var foo = { + get c() { + return a++, 42; + }, + set c(c) { + b++; + }, + d: function() { + if (this.c++, this.c) console.log(a, b); + } + } + foo.d(); + } + expect_stdout: "2 1" +} + +cascade_assignment_in_return: { + options = { + collapse_vars: true, + unused: true, + } + input: { + function f(a, b) { + return a = x(), b(a); + } + } + expect: { + function f(a, b) { + return b(x()); + } + } +} + +hoist_defun: { + options = { + join_vars: true, + sequences: true, + } + input: { + x(); + function f() {} + y(); + } + expect: { + function f() {} + x(), y(); + } +} + +hoist_decl: { + options = { + join_vars: true, + sequences: true, + } + input: { + var a; + w(); + var b = x(); + y(); + for (var c; 0;) z(); + var d; + } + expect: { + var a, b = (w(), x()), c, d; + for (y(); 0;) z(); + } +} + +for_init_var: { + options = { + join_vars: true, + unused: false, + } + input: { + var a = "PASS"; + (function() { + var b = 42; + for (var c = 5; c > 0;) c--; + a = "FAIL"; + var a; + })(); + console.log(a); + } + expect: { + var a = "PASS"; + (function() { + for (var b = 42, c = 5, a; c > 0;) c--; + a = "FAIL"; + })(); + console.log(a); + } + expect_stdout: "PASS" +} + +forin_1: { + options = { + sequences: true, + } + input: { + var o = []; + o.push("PASS"); + for (var a in o) + console.log(o[a]); + } + expect: { + var o = []; + for (var a in o.push("PASS"), o) + console.log(o[a]); + } + expect_stdout: "PASS" +} + +forin_2: { + options = { + evaluate: true, + inline: true, + reduce_vars: true, + sequences: true, + toplevel: true, + unused: true, + } + input: { + var o = { + p: 1, + q: 2, + }; + var k = "k"; + for ((console.log("exp"), o)[function() { + console.log("prop"); + return k; + }()] in function() { + console.log("obj"); + return o; + }()) + console.log(o.k, o[o.k]); + } + expect: { + var o = { + p: 1, + q: 2, + }; + for ((console.log("exp"), o)[console.log("prop"), "k"] in console.log("obj"), o) + console.log(o.k, o[o.k]); + } + expect_stdout: [ + "obj", + "exp", + "prop", + "p 1", + "exp", + "prop", + "q 2", + ] +} + +call: { + options = { + sequences: true, + } + input: { + var a = function() { + return this; + }(); + function b() { + console.log("foo"); + } + b.c = function() { + console.log(this === b ? "bar" : "baz"); + }; + (a, b)(); + (a, b).c(); + (a, b.c)(); + (a, b)["c"](); + (a, b["c"])(); + (a, function() { + console.log(this === a); + })(); + new (a, b)(); + new (a, b).c(); + new (a, b.c)(); + new (a, b)["c"](); + new (a, b["c"])(); + new (a, function() { + console.log(this === a); + })(); + console.log(typeof (a, b).c); + console.log(typeof (a, b)["c"]); + } + expect: { + var a = function() { + return this; + }(); + function b() { + console.log("foo"); + } + b.c = function() { + console.log(this === b ? "bar" : "baz"); + }, + a, + b(), + a, + b.c(), + (a, b.c)(), + a, + b["c"](), + (a, b["c"])(), + a, + function() { + console.log(this === a); + }(), + a, + new b(), + a, + new b.c(), + a, + new b.c(), + a, + new b["c"](), + a, + new b["c"](), + a, + new function() { + console.log(this === a); + }(), + console.log((a, typeof b.c)), + console.log((a, typeof b["c"])); + } + expect_stdout: [ + "foo", + "bar", + "baz", + "bar", + "baz", + "true", + "foo", + "baz", + "baz", + "baz", + "baz", + "false", + "function", + "function", + ] +} + +call_drop_side_effect_free: { + options = { + sequences: true, + side_effects: true, + } + input: { + var a = function() { + return this; + }(); + function b() { + console.log("foo"); + } + b.c = function() { + console.log(this === b ? "bar" : "baz"); + }; + (a, b)(); + (a, b).c(); + (a, b.c)(); + (a, b)["c"](); + (a, b["c"])(); + (a, function() { + console.log(this === a); + })(); + new (a, b)(); + new (a, b).c(); + new (a, b.c)(); + new (a, b)["c"](); + new (a, b["c"])(); + new (a, function() { + console.log(this === a); + })(); + console.log(typeof (a, b).c); + console.log(typeof (a, b)["c"]); + } + expect: { + var a = function() { + return this; + }(); + function b() { + console.log("foo"); + } + b.c = function() { + console.log(this === b ? "bar" : "baz"); + }, + b(), + b.c(), + (0, b.c)(), + b["c"](), + (0, b["c"])(), + function() { + console.log(this === a); + }(), + new b(), + new b.c(), + new b.c(), + new b["c"](), + new b["c"](), + new function() { + console.log(this === a); + }(), + console.log(typeof b.c), + console.log(typeof b["c"]); + } + expect_stdout: [ + "foo", + "bar", + "baz", + "bar", + "baz", + "true", + "foo", + "baz", + "baz", + "baz", + "baz", + "false", + "function", + "function", + ] +} + +missing_link: { + options = { + conditionals: true, + evaluate: true, + sequences: true, + } + input: { + var a = 100; + a; + a++ + (0 ? 2 : 1); + console.log(a); + } + expect: { + var a = 100; + a, + a++ + (0, 1), + console.log(a); + } +} + +missing_link_drop_side_effect_free: { + options = { + conditionals: true, + evaluate: true, + sequences: true, + side_effects: true, + } + input: { + var a = 100; + a; + a++ + (0 ? 2 : 1); + console.log(a); + } + expect: { + var a = 100; + a++, + console.log(a); + } +} + +angularjs_chain: { + options = { + conditionals: true, + sequences: true, + side_effects: true, + } + input: { + function nonComputedMember(left, right, context, create) { + var lhs = left(); + if (create && create !== 1) { + if (lhs && lhs[right] == null) { + lhs[right] = {}; + } + } + var value = lhs != null ? lhs[right] : undefined; + if (context) { + return { context: lhs, name: right, value: value }; + } else { + return value; + } + } + } + expect: { + function nonComputedMember(left, right, context, create) { + var lhs = left(); + create && 1 !== create && lhs && null == lhs[right] && (lhs[right] = {}); + var value = null != lhs ? lhs[right] : void 0; + return context ? { + context: lhs, + name: right, + value: value + } : value; + } + } +} + +issue_3490_1: { + options = { + conditionals: true, + dead_code: true, + inline: true, + sequences: true, + side_effects: true, + toplevel: true, + } + input: { + var b = 42, c = "FAIL"; + if ({ + 3: function() { + var a; + return (a && a.p) < this; + }(), + }) c = "PASS"; + if (b) while ("" == typeof d); + console.log(c, b); + } + expect: { + var b = 42, c = "FAIL"; + var a; + if (a && a.p, c = "PASS", b) while ("" == typeof d); + console.log(c, b); + } + expect_stdout: "PASS 42" +} + +issue_3490_2: { + options = { + conditionals: true, + dead_code: true, + evaluate: true, + inline: true, + reduce_vars: true, + sequences: true, + side_effects: true, + toplevel: true, + } + input: { + var b = 42, c = "FAIL"; + if ({ + 3: function() { + var a; + return (a && a.p) < this; + }(), + }) c = "PASS"; + if (b) for (; "" == typeof d;); + console.log(c, b); + } + expect: { + var b = 42, c = "FAIL"; + var a; + for (c = "PASS"; "" == typeof d;); + console.log(c, b); + } + expect_stdout: "PASS 42" +} + +issue_3703: { + options = { + evaluate: true, + sequences: true, + unsafe: true, + } + input: { + var a = "FAIL"; + while ((a = "PASS", 0).foo = 0); + console.log(a); + } + expect: { + var a = "FAIL"; + while (a = "PASS", (0).foo = 0); + console.log(a); + } + expect_stdout: "PASS" +} + +issue_4079: { + options = { + sequences: true, + side_effects: true, + } + input: { + try { + typeof (0, A); + } catch (e) { + console.log("PASS"); + } + } + expect: { + try { + A; + } catch (e) { + console.log("PASS"); + } } + expect_stdout: "PASS" } diff --git a/test/compress/side_effects.js b/test/compress/side_effects.js new file mode 100644 index 00000000000..090a6566207 --- /dev/null +++ b/test/compress/side_effects.js @@ -0,0 +1,726 @@ +accessor: { + options = { + side_effects: true, + } + input: { + ({ + get a() {}, + set a(v){ + this.b = 2; + }, + b: 1 + }); + } + expect: {} +} + +issue_2233_1: { + options = { + pure_getters: "strict", + side_effects: true, + unsafe: true, + } + input: { + Array.isArray; + Boolean; + console.log; + Date; + decodeURI; + decodeURIComponent; + encodeURI; + encodeURIComponent; + Error.name; + escape; + eval; + EvalError; + Function.length; + isFinite; + isNaN; + JSON; + Math.random; + Number.isNaN; + parseFloat; + parseInt; + RegExp; + Object.defineProperty; + String.fromCharCode; + RangeError; + ReferenceError; + SyntaxError; + TypeError; + unescape; + URIError; + } + expect: {} + expect_stdout: true +} + +global_timeout_and_interval_symbols: { + options = { + pure_getters: "strict", + side_effects: true, + unsafe: true, + } + input: { + // These global symbols do not exist in the test sandbox + // and must be tested separately. + clearInterval; + clearTimeout; + setInterval; + setTimeout; + } + expect: {} +} + +issue_2233_2: { + options = { + pure_getters: "strict", + reduce_funcs: true, + reduce_vars: true, + side_effects: true, + unsafe: true, + unused: true, + } + input: { + var RegExp; + Array.isArray; + RegExp; + UndeclaredGlobal; + function foo() { + var Number; + AnotherUndeclaredGlobal; + Math.sin; + Number.isNaN; + } + } + expect: { + var RegExp; + UndeclaredGlobal; + function foo() { + AnotherUndeclaredGlobal; + (void 0).isNaN; + } + } +} + +issue_2233_3: { + options = { + pure_getters: "strict", + reduce_funcs: true, + reduce_vars: true, + side_effects: true, + toplevel: true, + unsafe: true, + unused: true, + } + input: { + var RegExp; + Array.isArray; + RegExp; + UndeclaredGlobal; + function foo() { + var Number; + AnotherUndeclaredGlobal; + Math.sin; + Number.isNaN; + } + } + expect: { + UndeclaredGlobal; + } +} + +global_fns: { + options = { + side_effects: true, + unsafe: true, + } + input: { + Boolean(1, 2); + decodeURI(1, 2); + decodeURIComponent(1, 2); + Date(1, 2); + encodeURI(1, 2); + encodeURIComponent(1, 2); + Error(1, 2); + escape(1, 2); + EvalError(1, 2); + isFinite(1, 2); + isNaN(1, 2); + Number(1, 2); + Object(1, 2); + parseFloat(1, 2); + parseInt(1, 2); + RangeError(1, 2); + ReferenceError(1, 2); + String(1, 2); + SyntaxError(1, 2); + TypeError(1, 2); + unescape(1, 2); + URIError(1, 2); + try { + Function(1, 2); + } catch (e) { + console.log(e.name); + } + try { + RegExp(1, 2); + } catch (e) { + console.log(e.name); + } + try { + Array(NaN); + } catch (e) { + console.log(e.name); + } + } + expect: { + try { + Function(1, 2); + } catch (e) { + console.log(e.name); + } + try { + RegExp(1, 2); + } catch (e) { + console.log(e.name); + } + try { + Array(NaN); + } catch (e) { + console.log(e.name); + } + } + expect_stdout: [ + "SyntaxError", + "SyntaxError", + "RangeError", + ] +} + +global_constructors: { + options = { + side_effects: true, + unsafe: true, + } + input: { + Map; + new Map(console.log("foo")); + Set; + new Set(console.log("bar")); + WeakMap; + new WeakMap(console.log("baz")); + WeakSet; + new WeakSet(console.log("moo")); + } + expect: { + console.log("foo"); + console.log("bar"); + console.log("baz"); + console.log("moo"); + } + expect_stdout: [ + "foo", + "bar", + "baz", + "moo", + ] + node_version: ">=0.12" +} + +unsafe_builtin_1: { + options = { + side_effects: true, + unsafe: true, + } + input: { + (!w).constructor(x); + Math.abs(y); + [ 1, 2, z ].valueOf(); + } + expect: { + w, x; + y; + z; + } +} + +unsafe_builtin_2: { + options = { + side_effects: true, + unsafe: true, + } + input: { + var o = {}; + constructor.call(o, 42); + __defineGetter__.call(o, "foo", function() { + return o.p; + }); + __defineSetter__.call(o, void 0, function(a) { + o.p = a; + }); + console.log(typeof o, o.undefined = "PASS", o.foo); + } + expect: { + var o = {}; + constructor.call(o, 42); + __defineGetter__.call(o, "foo", function() { + return o.p; + }); + __defineSetter__.call(o, void 0, function(a) { + o.p = a; + }); + console.log(typeof o, o.undefined = "PASS", o.foo); + } + expect_stdout: "object PASS PASS" +} + +unsafe_builtin_3: { + options = { + conditionals: true, + side_effects: true, + toplevel: true, + unsafe: true, + } + input: { + var o = {}; + if (42 < Math.random()) + o.p = "FAIL"; + else + o.p = "PASS"; + for (var k in o) + console.log(k, o[k]); + } + expect: { + var o = {}; + o.p = 42 < Math.random() ? "FAIL" : "PASS"; + for (var k in o) + console.log(k, o[k]); + } + expect_stdout: "p PASS" +} + +unsafe_string_replace: { + options = { + side_effects: true, + unsafe: true, + } + input: { + "foo".replace("f", function() { + console.log("PASS"); + }); + } + expect: { + "foo".replace("f", function() { + console.log("PASS"); + }); + } + expect_stdout: "PASS" +} + +unsafe_Object_call: { + options = { + side_effects: true, + unsafe: true, + } + input: { + var o = { + f: function(a) { + console.log(a ? this.p : "FAIL 1"); + }, + p: "FAIL 2", + }, p = "PASS"; + Object(o.f)(42); + } + expect: { + var o = { + f: function(a) { + console.log(a ? this.p : "FAIL 1"); + }, + p: "FAIL 2", + }, p = "PASS"; + (0, o.f)(42); + } + expect_stdout: "PASS" +} + +drop_value: { + options = { + side_effects: true, + } + input: { + (1, [2, foo()], 3, {a:1, b:bar()}); + } + expect: { + foo(), bar(); + } +} + +operator_in: { + options = { + side_effects: true, + } + input: { + try { + "foo" in true; + console.log("FAIL"); + } catch (e) { + console.log("PASS"); + } + } + expect: { + try { + 0 in true; + console.log("FAIL"); + } catch (e) { + console.log("PASS"); + } + } + expect_stdout: "PASS" +} + +issue_3983_1: { + options = { + collapse_vars: true, + conditionals: true, + evaluate: true, + inline: true, + reduce_vars: true, + side_effects: true, + toplevel: true, + unused: true, + } + input: { + var a = "PASS"; + function f() { + g && g(); + } + f(); + function g() { + 0 ? a : 0; + } + var b = a; + console.log(a); + } + expect: { + var a = "PASS"; + console.log(a); + } + expect_stdout: "PASS" +} + +issue_3983_2: { + options = { + collapse_vars: true, + conditionals: true, + evaluate: true, + inline: true, + passes: 2, + reduce_vars: true, + side_effects: true, + toplevel: true, + unused: true, + } + input: { + var a = "PASS"; + function f() { + g && g(); + } + f(); + function g() { + 0 ? a : 0; + } + var b = a; + console.log(a); + } + expect: { + console.log("PASS"); + } + expect_stdout: "PASS" +} + +issue_4008: { + options = { + collapse_vars: true, + evaluate: true, + inline: true, + pure_getters: "strict", + reduce_vars: true, + side_effects: true, + toplevel: true, + } + input: { + var a = "PASS"; + function f(b, b) { + console.log(b); + } + f && f(a && a[a]); + console.log(a); + } + expect: { + var a = "PASS"; + function f(b, b) { + console.log(b); + } + f(a[a]); + console.log(a); + } + expect_stdout: [ + "undefined", + "PASS", + ] +} + +trim_new: { + options = { + side_effects: true, + } + input: { + new function(a) { + console.log(a); + }("PASS"); + } + expect: { + (function(a) { + console.log(a); + })("PASS"); + } + expect_stdout: "PASS" +} + +issue_4325: { + options = { + keep_fargs: false, + passes: 2, + pure_getters: "strict", + reduce_vars: true, + side_effects: true, + unused: true, + } + input: { + (function f() { + (function(b, c) { + try { + c.p = 0; + } catch (e) { + console.log("PASS"); + return b; + } + c; + })(f++); + })(); + } + expect: { + (function() { + (function(c) { + try { + c.p = 0; + } catch (e) { + console.log("PASS"); + return; + } + })(void 0); + })(); + } + expect_stdout: "PASS" +} + +issue_4366_1: { + options = { + side_effects: true, + } + input: { + ({ + p: 42, + get p() {}, + q: console.log("PASS"), + }); + } + expect: { + console.log("PASS"); + } + expect_stdout: "PASS" + node_version: ">=4" +} + +issue_4366_2: { + options = { + side_effects: true, + } + input: { + ({ + set p(v) {}, + q: console.log("PASS"), + p: 42, + }); + } + expect: { + console.log("PASS"); + } + expect_stdout: "PASS" + node_version: ">=4" +} + +issue_4668: { + options = { + conditionals: true, + keep_fargs: false, + keep_fnames: true, + reduce_vars: true, + side_effects: true, + toplevel: true, + unused: true, + } + input: { + function f(a) { + var b, c; + function g() { + return a = 0 + a, !d || (a = 0); + } + c = g(); + } + console.log(f()); + var d = 0; + } + expect: { + console.log(function f() { + (function g() { + 0; + })(); + }()); + } + expect_stdout: "undefined" +} + +drop_side_effect_free_call: { + options = { + inline: true, + reduce_vars: true, + side_effects: true, + toplevel: true, + } + input: { + function f(a) { + return "PA" + a; + } + f(42); + console.log(f("SS")); + } + expect: { + function f(a) { + return "PA" + a; + } + console.log(f("SS")); + } + expect_stdout: "PASS" +} + +issue_4730_1: { + options = { + pure_getters: "strict", + side_effects: true, + } + input: { + var a; + console.log("PASS") + (a && a[a.p]); + } + expect: { + var a; + console.log("PASS"), + a && a[a.p]; + } + expect_stdout: "PASS" +} + +issue_4730_2: { + options = { + pure_getters: "strict", + side_effects: true, + } + input: { + var a; + !console.log("PASS") || a && a[a.p]; + } + expect: { + var a; + console.log("PASS") && a && a[a.p]; + } + expect_stdout: "PASS" +} + +issue_4751: { + options = { + pure_getters: "strict", + side_effects: true, + } + input: { + var o = { + get p() { + console.log("PASS"); + }, + }; + o && o.p; + } + expect: { + var o = { + get p() { + console.log("PASS"); + }, + }; + o && o.p; + } + expect_stdout: "PASS" +} + +drop_instanceof: { + options = { + side_effects: true, + } + input: { + 42 instanceof function() {}; + console.log("PASS"); + } + expect: { + console.log("PASS"); + } + expect_stdout: "PASS" +} + +drop_instanceof_reference: { + options = { + reduce_vars: true, + side_effects: true, + toplevel: true, + } + input: { + function f() {} + 42 instanceof f; + console.log("PASS"); + } + expect: { + function f() {} + console.log("PASS"); + } + expect_stdout: "PASS" +} + +retain_instanceof: { + options = { + side_effects: true, + } + input: { + try { + 42 instanceof "foo"; + } catch (e) { + console.log("PASS"); + } + } + expect: { + try { + 0 instanceof "foo"; + } catch (e) { + console.log("PASS"); + } + } + expect_stdout: "PASS" +} diff --git a/test/compress/spreads.js b/test/compress/spreads.js new file mode 100644 index 00000000000..82f69120e61 --- /dev/null +++ b/test/compress/spreads.js @@ -0,0 +1,1255 @@ +decimal: { + input: { + console.log({... 0.42}); + } + expect_exact: "console.log({....42});" + expect_stdout: "{}" + node_version: ">=8.3.0" +} + +collapse_vars_1: { + options = { + collapse_vars: true, + } + input: { + var a; + [ ...a = "PASS", "PASS" ].slice(); + console.log(a); + } + expect: { + var a; + [ ...a = "PASS", "PASS" ].slice(); + console.log(a); + } + expect_stdout: "PASS" + node_version: ">=6" +} + +collapse_vars_2: { + options = { + collapse_vars: true, + } + input: { + var a = "FAIL"; + try { + a = "PASS"; + [ ...42, "PASS" ].slice(); + } catch (e) { + console.log(a); + } + } + expect: { + var a = "FAIL"; + try { + a = "PASS"; + [ ...42, "PASS" ].slice(); + } catch (e) { + console.log(a); + } + } + expect_stdout: "PASS" + node_version: ">=6" +} + +collapse_vars_3: { + options = { + collapse_vars: true, + } + input: { + var a = "FAIL"; + try { + [ ...(a = "PASS", 42), "PASS" ].slice(); + } catch (e) { + console.log(a); + } + } + expect: { + var a = "FAIL"; + try { + [ ...(a = "PASS", 42), "PASS" ].slice(); + } catch (e) { + console.log(a); + } + } + expect_stdout: "PASS" + node_version: ">=6" +} + +collapse_vars_4: { + options = { + collapse_vars: true, + unused: true, + } + input: { + console.log(function(a) { + return a; + }(...[ "PASS", "FAIL" ])); + } + expect: { + console.log(function(a) { + return a; + }(...[ "PASS", "FAIL" ])); + } + expect_stdout: "PASS" + node_version: ">=6" +} + +conditionals_farg_1: { + options = { + conditionals: true, + } + input: { + function log(msg) { + console.log(msg); + } + var a = 42, b = [ "PASS" ], c = [ "FAIL" ]; + a ? log(...b) : log(...c); + } + expect: { + function log(msg) { + console.log(msg); + } + var a = 42, b = [ "PASS" ], c = [ "FAIL" ]; + log(...a ? b : c); + } + expect_stdout: "PASS" + node_version: ">=6" +} + +conditionals_farg_2: { + options = { + conditionals: true, + pure_getters: "strict", + reduce_vars: true, + } + input: { + var log = console.log; + (function(a) { + return a.length ? log(...a) : log("FAIL"); + })([ "PASS" ]); + } + expect: { + var log = console.log; + (function(a) { + return a.length ? log(...a) : log("FAIL"); + })([ "PASS" ]); + } + expect_stdout: "PASS" + node_version: ">=6" +} + +dont_inline: { + options = { + inline: true, + } + input: { + console.log(function(a) { + return a; + }(...[ "PASS", "FAIL" ])); + } + expect: { + console.log(function(a) { + return a; + }(...[ "PASS", "FAIL" ])); + } + expect_stdout: "PASS" + node_version: ">=6" +} + +do_inline_1: { + options = { + inline: true, + spreads: true, + } + input: { + console.log(function(a) { + return a; + }(...[ "PASS", "FAIL" ])); + } + expect: { + console.log(("FAIL", "PASS")); + } + expect_stdout: "PASS" + node_version: ">=6" +} + +do_inline_2: { + options = { + inline: true, + side_effects: true, + } + input: { + (function() { + (function() { + console.log("PASS"); + })(...""); + })(); + } + expect: { + [] = [ ..."" ], + console.log("PASS"); + } + expect_stdout: "PASS" + node_version: ">=6" +} + +do_inline_3: { + options = { + if_return: true, + inline: true, + } + input: { + (function() { + (function() { + while (console.log("PASS")); + })(...""); + })(); + } + expect: { + var [] = [ ..."" ]; + while (console.log("PASS")); + } + expect_stdout: "PASS" + node_version: ">=6" +} + +drop_empty_call_1: { + options = { + side_effects: true, + } + input: { + try { + (function() {})(...null); + } catch (e) { + console.log("PASS"); + } + } + expect: { + try { + [ ...null ]; + } catch (e) { + console.log("PASS"); + } + } + expect_stdout: "PASS" + node_version: ">=6" +} + +drop_empty_call_2: { + options = { + side_effects: true, + spreads: true, + } + input: { + (function() {})(...[ console.log("PASS") ]); + } + expect: { + console.log("PASS"); + } + expect_stdout: "PASS" + node_version: ">=6" +} + +convert_hole_array: { + options = { + spreads: true, + } + input: { + [ ...[ "PASS", , 42 ] ].forEach(function(a) { + console.log(a); + }); + } + expect: { + [ "PASS", void 0, 42 ].forEach(function(a) { + console.log(a); + }); + } + expect_stdout: [ + "PASS", + "undefined", + "42", + ] + node_version: ">=6" +} + +convert_hole_call: { + options = { + spreads: true, + } + input: { + console.log(...[ "PASS", , 42 ]); + } + expect: { + console.log("PASS", void 0, 42); + } + expect_stdout: "PASS undefined 42" + node_version: ">=6" +} + +keep_property_access: { + options = { + properties: true, + side_effects: true, + } + input: { + console.log(function() { + return [ ..."foo" ][0]; + }()); + } + expect: { + console.log(function() { + return [ ..."foo" ][0]; + }()); + } + expect_stdout: "f" + node_version: ">=6" +} + +keep_fargs: { + options = { + keep_fargs: false, + unused: true, + } + input: { + var a = [ "PASS" ]; + (function(b, c) { + console.log(c); + })(console, ...a); + } + expect: { + var a = [ "PASS" ]; + (function(b, c) { + console.log(c); + })(console, ...a); + } + expect_stdout: "PASS" + node_version: ">=6" +} + +reduce_vars_1: { + options = { + reduce_vars: true, + unused: true, + } + input: { + console.log(function(b, c) { + return c ? "PASS" : "FAIL"; + }(..."foo")); + } + expect: { + console.log(function(b, c) { + return c ? "PASS" : "FAIL"; + }(..."foo")); + } + expect_stdout: "PASS" + node_version: ">=6" +} + +reduce_vars_2: { + options = { + conditionals: true, + evaluate: true, + reduce_vars: true, + } + input: { + console.log(function(b, c) { + return c ? "PASS" : "FAIL"; + }(..."foo")); + } + expect: { + console.log(function(b, c) { + return c ? "PASS" : "FAIL"; + }(..."foo")); + } + expect_stdout: "PASS" + node_version: ">=6" +} + +reduce_vars_3: { + options = { + reduce_funcs: true, + reduce_vars: true, + toplevel: true, + unused: true, + } + input: { + function f() {} + function g() { + return (a => a)(...[ f ]); + } + console.log(g() === g() ? "PASS" : "FAIL"); + } + expect: { + function f() {} + function g() { + return (a => a)(...[ f ]); + } + console.log(g() === g() ? "PASS" : "FAIL"); + } + expect_stdout: "PASS" + node_version: ">=6" +} + +convert_setter: { + options = { + objects: true, + spreads: true, + } + input: { + var o = { + ...{ + set PASS(v) {}, + }, + }; + for (var k in o) + console.log(k, o[k]); + } + expect: { + var o = { + PASS: void 0, + }; + for (var k in o) + console.log(k, o[k]); + } + expect_stdout: "PASS undefined" + node_version: ">=8.3.0" +} + +keep_getter_1: { + options = { + side_effects: true, + } + input: { + ({ + ...{ + get p() { + console.log("PASS"); + }, + }, + get q() { + console.log("FAIL"); + }, + }); + } + expect: { + ({ + ...{ + get p() { + console.log("PASS"); + }, + }, + }); + } + expect_stdout: "PASS" + node_version: ">=8.3.0" +} + +keep_getter_2: { + options = { + side_effects: true, + } + input: { + ({ + ...(console.log("foo"), { + get p() { + console.log("bar"); + }, + }), + }); + } + expect: { + ({ + ...(console.log("foo"), { + get p() { + console.log("bar"); + }, + }), + }); + } + expect_stdout: [ + "foo", + "bar", + ] + node_version: ">=8.3.0" +} + +keep_getter_3: { + options = { + side_effects: true, + } + input: { + ({ + ...function() { + return { + get p() { + console.log("PASS"); + }, + }; + }(), + }); + } + expect: { + ({ + ...function() { + return { + get p() { + console.log("PASS"); + }, + }; + }(), + }); + } + expect_stdout: "PASS" + node_version: ">=8.3.0" +} + +keep_getter_4: { + options = { + reduce_vars: true, + side_effects: true, + toplevel: true, + } + input: { + var o = { + get p() { + console.log("PASS"); + }, + }; + ({ + q: o, + ...o, + }); + } + expect: { + var o = { + get p() { + console.log("PASS"); + }, + }; + ({ + ...o, + }); + } + expect_stdout: "PASS" + node_version: ">=8.3.0" +} + +keep_accessor: { + options = { + objects: true, + spreads: true, + } + input: { + var o = { + ...{ + get p() { + console.log("GET"); + return this.r; + }, + set q(v) { + console.log("SET", v); + }, + r: 42, + }, + r: null, + }; + for (var k in o) + console.log(k, o[k]); + } + expect: { + var o = { + ...{ + get p() { + console.log("GET"); + return this.r; + }, + set q(v) { + console.log("SET", v); + }, + r: 42, + }, + r: null, + }; + for (var k in o) + console.log(k, o[k]); + } + expect_stdout: [ + "GET", + "p 42", + "q undefined", + "r null", + ] + node_version: ">=8.3.0" +} + +object_key_order_1: { + options = { + objects: true, + spreads: true, + } + input: { + var o = { + ...{}, + a: 1, + b: 2, + a: 3, + }; + for (var k in o) + console.log(k, o[k]); + } + expect: { + var o = { + a: (1, 3), + b: 2, + }; + for (var k in o) + console.log(k, o[k]); + } + expect_stdout: [ + "a 3", + "b 2", + ] + node_version: ">=8.3.0 <=10" +} + +object_key_order_2: { + options = { + objects: true, + spreads: true, + } + input: { + var o = { + a: 1, + ...{}, + b: 2, + a: 3, + }; + for (var k in o) + console.log(k, o[k]); + } + expect: { + var o = { + a: (1, 3), + b: 2, + }; + for (var k in o) + console.log(k, o[k]); + } + expect_stdout: [ + "a 3", + "b 2", + ] + node_version: ">=8.3.0" +} + +object_key_order_3: { + options = { + objects: true, + spreads: true, + } + input: { + var o = { + a: 1, + b: 2, + ...{}, + a: 3, + }; + for (var k in o) + console.log(k, o[k]); + } + expect: { + var o = { + a: (1, 3), + b: 2, + }; + for (var k in o) + console.log(k, o[k]); + } + expect_stdout: [ + "a 3", + "b 2", + ] + node_version: ">=8.3.0" +} + +object_key_order_4: { + options = { + objects: true, + spreads: true, + } + input: { + var o = { + a: 1, + b: 2, + a: 3, + ...{}, + }; + for (var k in o) + console.log(k, o[k]); + } + expect: { + var o = { + a: (1, 3), + b: 2, + }; + for (var k in o) + console.log(k, o[k]); + } + expect_stdout: [ + "a 3", + "b 2", + ] + node_version: ">=8.3.0" +} + +object_spread_array: { + options = { + objects: true, + spreads: true, + } + input: { + var o = { + ...[ "foo", "bar" ], + }; + for (var k in o) + console.log(k, o[k]); + } + expect: { + var o = { + ...[ "foo", "bar" ], + }; + for (var k in o) + console.log(k, o[k]); + } + expect_stdout: [ + "0 foo", + "1 bar", + ] + node_version: ">=8.3.0" +} + +object_spread_string: { + options = { + objects: true, + spreads: true, + } + input: { + var o = { + ..."foo", + }; + for (var k in o) + console.log(k, o[k]); + } + expect: { + var o = { + ..."foo", + }; + for (var k in o) + console.log(k, o[k]); + } + expect_stdout: [ + "0 f", + "1 o", + "2 o", + ] + node_version: ">=8.3.0" +} + +unused_var_side_effects: { + options = { + unused: true, + } + input: { + (function f(a) { + var b = { + ...a, + }; + })({ + get p() { + console.log("PASS"); + }, + }); + } + expect: { + (function(a) { + ({ + ...a, + }); + })({ + get p() { + console.log("PASS"); + }, + }); + } + expect_stdout: "PASS" + node_version: ">=8.3.0" +} + +unsafe_join_1: { + options = { + unsafe: true, + } + input: { + console.log([ ..."foo" ].join()); + } + expect: { + console.log([ ..."foo" ].join()); + } + expect_stdout: "f,o,o" + node_version: ">=6" +} + +unsafe_join_2: { + options = { + evaluate: true, + unsafe: true, + } + input: { + console.log([ "foo", ..."bar" ].join("")); + } + expect: { + console.log([ "foo", ..."bar" ].join("")); + } + expect_stdout: "foobar" + node_version: ">=6" +} + +unsafe_join_3: { + options = { + unsafe: true, + } + input: { + try { + [].join(...console); + } catch (e) { + console.log("PASS"); + } + } + expect: { + try { + [].join(...console); + } catch (e) { + console.log("PASS"); + } + } + expect_stdout: "PASS" + node_version: ">=6" +} + +issue_4329: { + options = { + objects: true, + spreads: true, + } + input: { + console.log({ + ...{ + get 0() { + return "FAIL"; + }, + ...{ + 0: "PASS", + }, + }, + }[0]); + } + expect: { + console.log({ + ...{ + get 0() { + return "FAIL"; + }, + [0]: "PASS", + }, + }[0]); + } + expect_stdout: "PASS" + node_version: ">=8.3.0" +} + +issue_4331: { + options = { + collapse_vars: true, + toplevel: true, + } + input: { + var a = "PASS", b; + console, + b = a; + (function() { + a++; + })(...a); + console.log(b); + } + expect: { + var a = "PASS", b; + console; + (function() { + a++; + })(...b = a); + console.log(b); + } + expect_stdout: "PASS" + node_version: ">=6" +} + +issue_4342: { + options = { + side_effects: true, + } + input: { + try { + new function() {}(...42); + } catch (e) { + console.log("PASS"); + } + } + expect: { + try { + [ ...42 ]; + } catch (e) { + console.log("PASS"); + } + } + expect_stdout: "PASS" + node_version: ">=6" +} + +issue_4345: { + options = { + objects: true, + spreads: true, + } + input: { + console.log({ + ...{ + get 42() { + return "FAIL"; + }, + ...{}, + 42: "PASS", + }, + }[42]); + } + expect: { + console.log({ + ...{ + get 42() { + return "FAIL"; + }, + [42]: "PASS", + }, + }[42]); + } + expect_stdout: "PASS" + node_version: ">=8.3.0" +} + +issue_4361: { + options = { + reduce_vars: true, + unused: true, + } + input: { + console.log(function() { + var a = console.log("foo"); + console; + var b = { + ...a, + }; + }()); + } + expect: { + console.log(function() { + var a = console.log("foo"); + console; + ({ + ...a, + }); + }()); + } + expect_stdout: [ + "foo", + "undefined", + ] + node_version: ">=8.3.0" +} + +issue_4363: { + options = { + objects: true, + spreads: true, + } + input: { + ({ + ...{ + set [console.log("PASS")](v) {}, + }, + }); + } + expect: { + ({ + [console.log("PASS")]: void 0, + }); + } + expect_stdout: "PASS" + node_version: ">=8.3.0" +} + +issue_4556: { + options = { + reduce_vars: true, + unused: true, + } + input: { + console.log(function() { + var a = "" + [ a++ ]; + var b = [ ...a ]; + }()); + } + expect: { + console.log(function() { + var a; + }()); + } + expect_stdout: "undefined" + node_version: ">=6" +} + +issue_4614: { + options = { + pure_getters: "strict", + side_effects: true, + } + input: { + try { + (function(...[]) { + var arguments; + arguments[0]; + })(); + } catch (e) { + console.log("PASS"); + } + } + expect: { + try { + (function(...[]) { + var arguments; + arguments[0]; + })(); + } catch (e) { + console.log("PASS"); + } + } + expect_stdout: true + node_version: ">=6" +} + +issue_4849: { + options = { + reduce_vars: true, + unused: true, + } + input: { + while (function() { + while (!console); + }(new function(a) { + console.log(typeof { ...a }); + }(function() {}))); + } + expect: { + while (function() { + while (!console); + }(function(a) { + console.log(typeof { ...function() {} }); + }())); + } + expect_stdout: "object" + node_version: ">=8.3.0" +} + +issue_4882_1: { + options = { + objects: true, + spreads: true, + } + input: { + var o = { + p: "PASS", + ... { + __proto__: { + p: "FAIL 1", + q: "FAIL 2", + }, + }, + }; + console.log(o.p); + console.log(o.q); + } + expect: { + var o = { + p: "PASS", + }; + console.log(o.p); + console.log(o.q); + } + expect_stdout: [ + "PASS", + "undefined", + ] + node_version: ">=8.3.0" +} + +issue_4882_2: { + options = { + objects: true, + spreads: true, + } + input: { + console.log(null == Object.getPrototypeOf({ + ... { + __proto__: (console.log(42), null), + }, + }) ? "FAIL" : "PASS"); + } + expect: { + console.log(null == Object.getPrototypeOf({ + ... { + __proto__: (console.log(42), null), + }, + }) ? "FAIL" : "PASS"); + } + expect_stdout: [ + "42", + "PASS", + ] + node_version: ">=8.3.0" +} + +issue_4882_3: { + options = { + objects: true, + spreads: true, + } + input: { + var o = { + __proto__: { p: 42 }, + ... { + set __proto__(v) {}, + }, + }; + console.log(o.__proto__ === Object.getPrototypeOf(o) ? "FAIL" : "PASS"); + console.log(o.p); + } + expect: { + var o = { + __proto__: { p: 42 }, + ["__proto__"]: void 0, + }; + console.log(o.__proto__ === Object.getPrototypeOf(o) ? "FAIL" : "PASS"); + console.log(o.p); + } + expect_stdout: [ + "PASS", + "42", + ] + node_version: ">=8.3.0" +} + +issue_5006: { + options = { + arguments: true, + } + input: { + console.log(function(b, c) { + c = "FAIL 2"; + return arguments[1]; + }(...[], "FAIL 1") || "PASS"); + } + expect: { + console.log(function(b, c) { + c = "FAIL 2"; + return arguments[1]; + }(...[], "FAIL 1") || "PASS"); + } + expect_stdout: "PASS" + node_version: ">=6" +} + +issue_5382: { + options = { + side_effects: true, + } + input: { + ({ + f() { + ({ ...this }); + }, + get p() { + console.log("PASS"); + }, + }).f(); + } + expect: { + ({ + f() { + ({ ...this }); + }, + get p() { + console.log("PASS"); + }, + }).f(); + } + expect_stdout: "PASS" + node_version: ">=8.3.0" +} + +issue_5602: { + options = { + collapse_vars: true, + conditionals: true, + evaluate: true, + if_return: true, + inline: true, + passes: 2, + sequences: true, + spreads: true, + unused: true, + } + input: { + (function() { + try { + var b = function(c) { + if (c) + return FAIL; + var d = 42; + }(...[ null, A = 0 ]); + } catch (e) { + b(); + } + })(); + console.log(A); + } + expect: { + (function() { + try { + var b = void (A = 0); + } catch (e) { + b(); + } + })(), + console.log(A); + } + expect_stdout: "0" + expect_warnings: [ + "INFO: Dropping unused variable d [test/compress/spreads.js:6,24]", + "INFO: Collapsing c [test/compress/spreads.js:4,24]", + "INFO: Dropping unused variable c [test/compress/spreads.js:3,33]", + "INFO: pass 0: last_count: Infinity, count: 27", + "WARN: Condition always false [test/compress/spreads.js:4,20]", + "INFO: Collapsing null [test/compress/spreads.js:7,23]", + "INFO: Collapsing 0 [test/compress/spreads.js:3,24]", + "INFO: pass 1: last_count: 27, count: 22", + ] + node_version: ">=6" +} diff --git a/test/compress/string-literal.js b/test/compress/string-literal.js new file mode 100644 index 00000000000..5f49fb3e63a --- /dev/null +++ b/test/compress/string-literal.js @@ -0,0 +1,26 @@ +octal_escape_sequence: { + input: { + var boundaries = "\0\7\00\07\70\77\000\077\300\377"; + var border_check = "\400\700\0000\3000"; + } + expect: { + var boundaries = "\x00\x07\x00\x07\x38\x3f\x00\x3f\xc0\xff"; + var border_check = "\x20\x30\x38\x30\x00\x30\xc0\x30"; + } +} + +issue_1929: { + input: { + function f(s) { + return s.split(/[\\/]/); + } + console.log(JSON.stringify(f("A/B\\C\\D/E\\F"))); + } + expect: { + function f(s) { + return s.split(/[\\/]/); + } + console.log(JSON.stringify(f("A/B\\C\\D/E\\F"))); + } + expect_stdout: '["A","B","C","D","E","F"]' +} diff --git a/test/compress/switch.js b/test/compress/switch.js deleted file mode 100644 index 62e39cf7bce..00000000000 --- a/test/compress/switch.js +++ /dev/null @@ -1,260 +0,0 @@ -constant_switch_1: { - options = { dead_code: true, evaluate: true }; - input: { - switch (1+1) { - case 1: foo(); break; - case 1+1: bar(); break; - case 1+1+1: baz(); break; - } - } - expect: { - bar(); - } -} - -constant_switch_2: { - options = { dead_code: true, evaluate: true }; - input: { - switch (1) { - case 1: foo(); - case 1+1: bar(); break; - case 1+1+1: baz(); - } - } - expect: { - foo(); - bar(); - } -} - -constant_switch_3: { - options = { dead_code: true, evaluate: true }; - input: { - switch (10) { - case 1: foo(); - case 1+1: bar(); break; - case 1+1+1: baz(); - default: - def(); - } - } - expect: { - def(); - } -} - -constant_switch_4: { - options = { dead_code: true, evaluate: true }; - input: { - switch (2) { - case 1: - x(); - if (foo) break; - y(); - break; - case 1+1: - bar(); - default: - def(); - } - } - expect: { - bar(); - def(); - } -} - -constant_switch_5: { - options = { dead_code: true, evaluate: true }; - input: { - switch (1) { - case 1: - x(); - if (foo) break; - y(); - break; - case 1+1: - bar(); - default: - def(); - } - } - expect: { - // the break inside the if ruins our job - // we can still get rid of irrelevant cases. - switch (1) { - case 1: - x(); - if (foo) break; - y(); - } - // XXX: we could optimize this better by inventing an outer - // labeled block, but that's kinda tricky. - } -} - -constant_switch_6: { - options = { dead_code: true, evaluate: true }; - input: { - OUT: { - foo(); - switch (1) { - case 1: - x(); - if (foo) break OUT; - y(); - case 1+1: - bar(); - break; - default: - def(); - } - } - } - expect: { - OUT: { - foo(); - x(); - if (foo) break OUT; - y(); - bar(); - } - } -} - -constant_switch_7: { - options = { dead_code: true, evaluate: true }; - input: { - OUT: { - foo(); - switch (1) { - case 1: - x(); - if (foo) break OUT; - for (var x = 0; x < 10; x++) { - if (x > 5) break; // this break refers to the for, not to the switch; thus it - // shouldn't ruin our optimization - console.log(x); - } - y(); - case 1+1: - bar(); - break; - default: - def(); - } - } - } - expect: { - OUT: { - foo(); - x(); - if (foo) break OUT; - for (var x = 0; x < 10; x++) { - if (x > 5) break; - console.log(x); - } - y(); - bar(); - } - } -} - -constant_switch_8: { - options = { dead_code: true, evaluate: true }; - input: { - OUT: switch (1) { - case 1: - x(); - for (;;) break OUT; - y(); - break; - case 1+1: - bar(); - default: - def(); - } - } - expect: { - OUT: { - x(); - for (;;) break OUT; - y(); - } - } -} - -constant_switch_9: { - options = { dead_code: true, evaluate: true }; - input: { - OUT: switch (1) { - case 1: - x(); - for (;;) if (foo) break OUT; - y(); - case 1+1: - bar(); - default: - def(); - } - } - expect: { - OUT: { - x(); - for (;;) if (foo) break OUT; - y(); - bar(); - def(); - } - } -} - -drop_default_1: { - options = { dead_code: true }; - input: { - switch (foo) { - case 'bar': baz(); - default: - } - } - expect: { - switch (foo) { - case 'bar': baz(); - } - } -} - -drop_default_2: { - options = { dead_code: true }; - input: { - switch (foo) { - case 'bar': baz(); break; - default: - break; - } - } - expect: { - switch (foo) { - case 'bar': baz(); - } - } -} - -keep_default: { - options = { dead_code: true }; - input: { - switch (foo) { - case 'bar': baz(); - default: - something(); - break; - } - } - expect: { - switch (foo) { - case 'bar': baz(); - default: - something(); - } - } -} diff --git a/test/compress/switches.js b/test/compress/switches.js new file mode 100644 index 00000000000..0502db78d70 --- /dev/null +++ b/test/compress/switches.js @@ -0,0 +1,1690 @@ +constant_switch_1: { + options = { + conditionals: true, + dead_code: true, + evaluate: true, + side_effects: true, + switches: true, + } + input: { + switch (1+1) { + case 1: foo(); break; + case 1+1: bar(); break; + case 1+1+1: baz(); break; + } + } + expect: { + bar(); + } +} + +constant_switch_2: { + options = { + conditionals: true, + dead_code: true, + evaluate: true, + side_effects: true, + switches: true, + } + input: { + switch (1) { + case 1: foo(); + case 1+1: bar(); break; + case 1+1+1: baz(); + } + } + expect: { + foo(); + bar(); + } +} + +constant_switch_3: { + options = { + conditionals: true, + dead_code: true, + evaluate: true, + side_effects: true, + switches: true, + } + input: { + switch (10) { + case 1: foo(); + case 1+1: bar(); break; + case 1+1+1: baz(); + default: + def(); + } + } + expect: { + def(); + } +} + +constant_switch_4: { + options = { + conditionals: true, + dead_code: true, + evaluate: true, + side_effects: true, + switches: true, + } + input: { + switch (2) { + case 1: + x(); + if (foo) break; + y(); + break; + case 1+1: + bar(); + default: + def(); + } + } + expect: { + bar(); + def(); + } +} + +constant_switch_5: { + options = { + conditionals: true, + dead_code: true, + evaluate: true, + side_effects: true, + switches: true, + } + input: { + switch (1) { + case 1: + x(); + if (foo) break; + y(); + break; + case 1+1: + bar(); + default: + def(); + } + } + expect: { + // the break inside the if ruins our job + // we can still get rid of irrelevant cases. + switch (1) { + default: + x(); + if (foo) break; + y(); + } + // XXX: we could optimize this better by inventing an outer + // labeled block, but that's kinda tricky. + } +} + +constant_switch_6: { + options = { + conditionals: true, + dead_code: true, + evaluate: true, + side_effects: true, + switches: true, + } + input: { + OUT: { + foo(); + switch (1) { + case 1: + x(); + if (foo) break OUT; + y(); + case 1+1: + bar(); + break; + default: + def(); + } + } + } + expect: { + OUT: { + foo(); + x(); + if (foo) break OUT; + y(); + bar(); + } + } +} + +constant_switch_7: { + options = { + conditionals: true, + dead_code: true, + evaluate: true, + side_effects: true, + switches: true, + } + input: { + OUT: { + foo(); + switch (1) { + case 1: + x(); + if (foo) break OUT; + for (var x = 0; x < 10; x++) { + if (x > 5) break; // this break refers to the for, not to the switch; thus it + // shouldn't ruin our optimization + console.log(x); + } + y(); + case 1+1: + bar(); + break; + default: + def(); + } + } + } + expect: { + OUT: { + foo(); + x(); + if (foo) break OUT; + for (var x = 0; x < 10; x++) { + if (x > 5) break; + console.log(x); + } + y(); + bar(); + } + } +} + +constant_switch_8: { + options = { + conditionals: true, + dead_code: true, + evaluate: true, + side_effects: true, + switches: true, + } + input: { + OUT: switch (1) { + case 1: + x(); + for (;;) break OUT; + y(); + break; + case 1+1: + bar(); + default: + def(); + } + } + expect: { + OUT: { + x(); + for (;;) break OUT; + y(); + } + } +} + +constant_switch_9: { + options = { + conditionals: true, + dead_code: true, + evaluate: true, + side_effects: true, + switches: true, + } + input: { + OUT: switch (1) { + case 1: + x(); + for (;;) if (foo) break OUT; + y(); + case 1+1: + bar(); + default: + def(); + } + } + expect: { + OUT: { + x(); + for (;;) if (foo) break OUT; + y(); + bar(); + def(); + } + } +} + +drop_default_1: { + options = { + dead_code: true, + switches: true, + } + input: { + switch (foo) { + case "bar": baz(); + default: + } + } + expect: { + switch (foo) { + case "bar": baz(); + } + } +} + +drop_default_2: { + options = { + dead_code: true, + switches: true, + } + input: { + switch (foo) { + case "bar": baz(); break; + default: + break; + } + } + expect: { + switch (foo) { + case "bar": baz(); + } + } +} + +drop_default_3: { + options = { + dead_code: true, + evaluate: true, + switches: true, + } + input: { + function f() { + console.log("PASS"); + return 42; + } + switch (42) { + case f(): + break; + case void console.log("FAIL"): + default: + } + } + expect: { + function f() { + console.log("PASS"); + return 42; + } + switch (42) { + case f(): + case void console.log("FAIL"): + } + } + expect_stdout: "PASS" +} + +keep_default: { + options = { + dead_code: true, + switches: true, + } + input: { + switch (foo) { + case "bar": baz(); + default: + something(); + break; + } + } + expect: { + switch (foo) { + case "bar": baz(); + default: + something(); + } + } +} + +issue_1663: { + options = { + conditionals: true, + dead_code: true, + evaluate: true, + side_effects: true, + switches: true, + } + input: { + var a = 100, b = 10; + function f() { + switch (1) { + case 1: + b = a++; + return ++b; + default: + var b; + } + } + f(); + console.log(a, b); + } + expect: { + var a = 100, b = 10; + function f() { + var b; + b = a++; + return ++b; + } + f(); + console.log(a, b); + } + expect_stdout: true +} + +drop_case_1: { + options = { + dead_code: true, + switches: true, + } + input: { + switch (foo) { + case "bar": baz(); break; + case "moo": + break; + } + } + expect: { + switch (foo) { + case "bar": baz(); + } + } +} + +drop_case_2: { + options = { + dead_code: true, + switches: true, + } + input: { + switch (foo) { + case "bar": + bar(); + break; + default: + case "moo": + moo(); + break; + } + } + expect: { + switch (foo) { + case "bar": + bar(); + break; + default: + moo(); + } + } +} + +drop_case_3: { + options = { + dead_code: true, + switches: true, + } + input: { + var c = "PASS"; + switch ({}.p) { + default: + case void 0: + break; + case c = "FAIL": + } + console.log(c); + } + expect: { + var c = "PASS"; + switch ({}.p) { + default: + case void 0: + case c = "FAIL": + } + console.log(c); + } + expect_stdout: "PASS" +} + +drop_case_4: { + options = { + dead_code: true, + switches: true, + } + input: { + switch (0) { + case [ a, typeof b ]: + default: + var a; + } + console.log("PASS"); + } + expect: { + switch (0) { + case [ a, typeof b ]: + var a; + } + console.log("PASS"); + } + expect_stdout: "PASS" +} + +drop_case_5: { + options = { + dead_code: true, + evaluate: true, + switches: true, + } + input: { + switch (42) { + case void console.log("PASS 1"): + console.log("FAIL 1"); + case 42: + case console.log("FAIL 2"): + console.log("PASS 2"); + } + } + expect: { + switch (42) { + default: + void console.log("PASS 1"); + console.log("PASS 2"); + } + } + expect_stdout: [ + "PASS 1", + "PASS 2", + ] +} + +drop_case_6: { + options = { + dead_code: true, + evaluate: true, + switches: true, + } + input: { + switch (console.log("PASS 1"), 2) { + case 0: + console.log("FAIL 1"); + case (console.log("PASS 2"), 1): + console.log("FAIL 2"); + } + } + expect: { + switch (console.log("PASS 1"), 2) { + case (console.log("PASS 2"), 1): + } + } + expect_stdout: [ + "PASS 1", + "PASS 2", + ] +} + +drop_case_7: { + options = { + dead_code: true, + evaluate: true, + switches: true, + } + input: { + switch (2) { + case 0: + console.log("FAIL 1"); + case (console.log("PASS 1"), 1): + console.log("FAIL 2"); + case 2: + console.log("PASS 2"); + } + } + expect: { + switch (2) { + default: + console.log("PASS 1"), 1; + console.log("PASS 2"); + } + } + expect_stdout: [ + "PASS 1", + "PASS 2", + ] +} + +drop_case_8: { + options = { + dead_code: true, + switches: true, + } + input: { + function log(msg) { + console.log(msg); + return msg; + } + switch (log("foo")) { + case "bar": + log("moo"); + break; + case log("baz"): + log("moo"); + break; + default: + log("moo"); + } + } + expect: { + function log(msg) { + console.log(msg); + return msg; + } + switch (log("foo")) { + case "bar": + case log("baz"): + default: + log("moo"); + } + } + expect_stdout: [ + "foo", + "baz", + "moo", + ] +} + +drop_case_9: { + options = { + dead_code: true, + switches: true, + } + input: { + function log(msg) { + console.log(msg); + return msg; + } + switch (log("foo")) { + case log("bar"): + log("moo"); + break; + case "baz": + log("moo"); + break; + default: + log("moo"); + } + } + expect: { + function log(msg) { + console.log(msg); + return msg; + } + switch (log("foo")) { + default: + log("bar"); + log("moo"); + } + } + expect_stdout: [ + "foo", + "bar", + "moo", + ] +} + +keep_case_1: { + options = { + dead_code: true, + switches: true, + } + input: { + switch (foo) { + case "bar": baz(); break; + case moo: + break; + } + } + expect: { + switch (foo) { + case "bar": baz(); break; + case moo: + } + } +} + +keep_case_2: { + options = { + dead_code: true, + evaluate: true, + switches: true, + } + input: { + switch ("foo") { + case console.log("bar"): + case console.log("baz"), "moo": + } + } + expect: { + switch ("foo") { + case console.log("bar"): + case console.log("baz"), "moo": + } + } + expect_stdout: [ + "bar", + "baz", + ] +} + +keep_case_3: { + options = { + dead_code: true, + evaluate: true, + switches: true, + } + input: { + var a; + switch (void console.log("PASS")) { + case a: + case console.log("FAIL"), 42: + } + } + expect: { + var a; + switch (void console.log("PASS")) { + case a: + case console.log("FAIL"), 42: + } + } + expect_stdout: "PASS" +} + +keep_case_4: { + options = { + dead_code: true, + evaluate: true, + switches: true, + } + input: { + var a; + switch (void console.log("PASS")) { + case a: + case void console.log("FAIL"): + } + } + expect: { + var a; + switch (void console.log("PASS")) { + case a: + case void console.log("FAIL"): + } + } + expect_stdout: "PASS" +} + +issue_376: { + options = { + dead_code: true, + evaluate: true, + switches: true, + } + input: { + switch (true) { + case boolCondition: + console.log(1); + break; + case false: + console.log(2); + break; + } + } + expect: { + switch (true) { + case boolCondition: + console.log(1); + } + } +} + +issue_441_1: { + options = { + dead_code: true, + switches: true, + } + input: { + switch (foo) { + case bar: + qux(); + break; + case baz: + qux(); + break; + default: + qux(); + break; + } + } + expect: { + switch (foo) { + case bar: + case baz: + default: + qux(); + } + } +} + +issue_441_2: { + options = { + dead_code: true, + switches: true, + } + input: { + switch (foo) { + case bar: + // TODO: Fold into the case below + qux(); + break; + case fall: + case baz: + qux(); + break; + default: + qux(); + break; + } + } + expect: { + switch (foo) { + case bar: + qux(); + break; + case fall: + case baz: + default: + qux(); + } + } +} + +issue_1674: { + options = { + conditionals: true, + dead_code: true, + evaluate: true, + side_effects: true, + switches: true, + } + input: { + switch (0) { + default: + console.log("FAIL"); + break; + case 0: + console.log("PASS"); + break; + } + } + expect: { + console.log("PASS"); + } + expect_stdout: "PASS" +} + +issue_1679: { + options = { + dead_code: true, + evaluate: true, + switches: true, + } + input: { + var a = 100, b = 10; + function f() { + switch (--b) { + default: + case !function x() {}: + break; + case b--: + switch (0) { + default: + case a--: + } + break; + case (a++): + break; + } + } + f(); + console.log(a, b); + } + expect: { + var a = 100, b = 10; + function f() { + switch (--b) { + default: + case !function x() {}: + break; + case b--: + switch (0) { + default: + case a--: + } + break; + case (a++): + } + } + f(); + console.log(a, b); + } + expect_stdout: "99 8" +} + +issue_1680_1: { + options = { + dead_code: true, + evaluate: true, + switches: true, + } + input: { + function f(x) { + console.log(x); + return x + 1; + } + switch (2) { + case f(0): + case f(1): + f(2); + case 2: + case f(3): + case f(4): + f(5); + } + } + expect: { + function f(x) { + console.log(x); + return x + 1; + } + switch (2) { + case f(0): + case f(1): + f(2); + default: + f(5); + } + } + expect_stdout: [ + "0", + "1", + "2", + "5", + ] +} + +issue_1680_2: { + options = { + dead_code: true, + switches: true, + } + input: { + var a = 100, b = 10; + switch (b) { + case a--: + break; + case b: + var c; + break; + case a: + break; + case a--: + break; + } + console.log(a, b); + } + expect: { + var a = 100, b = 10; + switch (b) { + case a--: + break; + case b: + var c; + break; + case a: + case a--: + } + console.log(a, b); + } + expect_stdout: true +} + +issue_1690_1: { + options = { + dead_code: true, + switches: true, + } + input: { + switch (console.log("PASS")) {} + } + expect: { + console.log("PASS"); + } + expect_stdout: "PASS" +} + +issue_1690_2: { + options = { + dead_code: false, + switches: true, + } + input: { + switch (console.log("PASS")) {} + } + expect: { + switch (console.log("PASS")) {} + } + expect_stdout: "PASS" +} + +if_switch_typeof: { + options = { + conditionals: true, + dead_code: true, + side_effects: true, + switches: true, + } + input: { + if (a) switch(typeof b) {} + } + expect: { + a; + } +} + +issue_1698: { + options = { + side_effects: true, + switches: true, + } + input: { + var a = 1; + !function() { + switch (a++) {} + }(); + console.log(a); + } + expect: { + var a = 1; + !function() { + switch (a++) {} + }(); + console.log(a); + } + expect_stdout: "2" +} + +issue_1705_1: { + options = { + dead_code: true, + switches: true, + } + input: { + var a = 0; + switch (a) { + default: + console.log("FAIL"); + case 0: + break; + } + } + expect: { + var a = 0; + switch (a) { + default: + console.log("FAIL"); + case 0: + } + } + expect_stdout: true +} + +issue_1705_2: { + options = { + dead_code: true, + evaluate: true, + reduce_funcs: true, + reduce_vars: true, + sequences: true, + side_effects: true, + switches: true, + toplevel: true, + unused: true, + } + input: { + var a = 0; + switch (a) { + default: + console.log("FAIL"); + case 0: + break; + } + } + expect: { + } + expect_stdout: true +} + +issue_1705_3: { + options = { + dead_code: true, + switches: true, + } + input: { + switch (a) { + case 0: + break; + default: + break; + } + } + expect: { + a; + } + expect_stdout: true +} + +beautify: { + beautify = { + beautify: true, + } + input: { + switch (a) { + case 0: + case 1: + break; + case 2: + default: + } + switch (b) { + case 3: + foo(); + bar(); + default: + break; + } + } + expect_exact: [ + "switch (a) {", + " case 0:", + " case 1:", + " break;", + "", + " case 2:", + " default:", + "}", + "", + "switch (b) {", + " case 3:", + " foo();", + " bar();", + "", + " default:", + " break;", + "}", + ] +} + +issue_1758: { + options = { + conditionals: true, + dead_code: true, + switches: true, + } + input: { + var a = 1, b = 2; + switch (a--) { + default: + b++; + } + console.log(a, b); + } + expect: { + var a = 1, b = 2; + a--; + b++; + console.log(a, b); + } + expect_stdout: "0 3" +} + +issue_2535: { + options = { + conditionals: true, + dead_code: true, + evaluate: true, + switches: true, + } + input: { + switch(w(), 42) { + case 13: x(); + case 42: y(); + default: z(); + } + } + expect: { + w(), 42; + y(); + z(); + } +} + +issue_1750: { + options = { + conditionals: true, + dead_code: true, + evaluate: true, + switches: true, + } + input: { + var a = 0, b = 1; + switch (true) { + case a, true: + default: + b = 2; + case true: + } + console.log(a, b); + } + expect: { + var a = 0, b = 1; + true; + b = 2; + console.log(a, b); + } + expect_stdout: "0 2" +} + +drop_switch_1: { + options = { + dead_code: true, + switches: true, + } + input: { + switch (foo) { + default: + break; + case "bar": + break; + } + } + expect: { + foo; + } +} + +drop_switch_2: { + options = { + conditionals: true, + dead_code: true, + switches: true, + } + input: { + switch (foo) { + default: + case "bar": + baz(); + } + } + expect: { + foo; + baz(); + } +} + +drop_switch_3: { + options = { + dead_code: true, + switches: true, + } + input: { + console.log(function() { + switch (0) { + default: + return "PASS"; + case 1: + } + }()); + } + expect: { + console.log(function() { + switch (0) { + default: + return "PASS"; + case 1: + } + }()); + } + expect_stdout: "PASS" +} + +drop_switch_4: { + options = { + conditionals: true, + dead_code: true, + switches: true, + } + input: { + var a = "FAIL"; + switch (0) { + default: + case a: + var b = a = "PASS"; + break; + } + console.log(a); + } + expect: { + var a = "FAIL"; + 0; + var b = a = "PASS"; + console.log(a); + } + expect_stdout: "PASS" +} + +drop_switch_5: { + options = { + conditionals: true, + dead_code: true, + switches: true, + } + input: { + switch (A) { + case B: + x(); + default: + } + switch (C) { + default: + y(); + case D: + } + } + expect: { + A === B && x(); + C !== D && y(); + } +} + +drop_switch_6: { + options = { + conditionals: true, + dead_code: true, + switches: true, + } + input: { + switch (A) { + case B: + default: + x(); + } + switch (C) { + default: + case D: + y(); + } + } + expect: { + A; + B; + x(); + C !== D; + y(); + } +} + +drop_switch_7: { + options = { + conditionals: true, + dead_code: true, + switches: true, + } + input: { + switch (A) { + case B: + w(); + default: + x(); + } + switch (C) { + default: + y(); + case D: + z(); + } + } + expect: { + A === B && w(); + x(); + C !== D && y(); + z(); + } +} + +drop_switch_8: { + options = { + conditionals: true, + dead_code: true, + switches: true, + } + input: { + switch (A) { + case B: + w(); + break; + default: + x(); + } + switch (C) { + default: + y(); + break; + case D: + z(); + } + } + expect: { + (A === B ? w : x)(); + (C !== D ? y : z)(); + } +} + +issue_4059: { + options = { + conditionals: true, + dead_code: true, + evaluate: true, + switches: true, + } + input: { + switch (0) { + default: + case 1: + break; + case a: + break; + var a; + } + console.log("PASS"); + } + expect: { + switch (0) { + default: + break; + case a: + break; + var a; + } + console.log("PASS"); + } + expect_stdout: "PASS" +} + +issue_5008_1: { + options = { + dead_code: true, + evaluate: true, + reduce_vars: true, + switches: true, + unsafe: true, + } + input: { + console.log(function f() { + switch (f) { + case f: + return "PASS"; + default: + return "FAIL"; + } + }()); + } + expect: { + console.log(function f() { + switch (f) { + default: + return "PASS"; + } + }()); + } + expect_stdout: "PASS" +} + +issue_5008_2: { + options = { + dead_code: true, + evaluate: true, + reduce_vars: true, + switches: true, + unsafe: true, + } + input: { + console.log(function(a) { + switch (a) { + case a: + return "PASS"; + default: + return "FAIL"; + } + }([])); + } + expect: { + console.log(function(a) { + switch (a) { + default: + return "PASS"; + } + }([])); + } + expect_stdout: "PASS" +} + +issue_5008_3: { + options = { + dead_code: true, + evaluate: true, + reduce_vars: true, + switches: true, + unsafe: true, + } + input: { + console.log(function(a) { + switch (a) { + case a: + return "PASS"; + default: + return "FAIL"; + } + }({})); + } + expect: { + console.log(function(a) { + switch (a) { + default: + return "PASS"; + } + }({})); + } + expect_stdout: "PASS" +} + +issue_5008_4: { + options = { + dead_code: true, + evaluate: true, + reduce_vars: true, + switches: true, + } + input: { + console.log(function(a) { + switch (a) { + case a: + return "PASS"; + default: + return "FAIL"; + } + }(/foo/)); + } + expect: { + console.log(function(a) { + switch (a) { + default: + return "PASS"; + } + }(/foo/)); + } + expect_stdout: "PASS" +} + +issue_5010: { + options = { + dead_code: true, + evaluate: true, + switches: true, + } + input: { + var a; + switch (42) { + case console.log("PASS"): + case a: + console.log("FAIL"); + case 42: + } + } + expect: { + var a; + switch (42) { + case console.log("PASS"): + case a: + console.log("FAIL"); + } + } + expect_stdout: "PASS" +} + +issue_5012: { + options = { + dead_code: true, + evaluate: true, + switches: true, + } + input: { + switch (void 0) { + case console.log("PASS"): + break; + case void 0: + case 42: + console.log("FAIL"); + } + } + expect: { + switch (void 0) { + case console.log("PASS"): + break; + default: + console.log("FAIL"); + } + } + expect_stdout: "PASS" +} + +issue_5543_1: { + options = { + dead_code: true, + switches: true, + } + input: { + var a; + switch (a) { + default: + switch (42) { + case a: + case console.log("PASS"): + } + break; + case null: + switch (false) { + case a: + case console.log("FAIL"): + } + } + } + expect: { + var a; + switch (a) { + default: + switch (42) { + case a: + case console.log("PASS"): + } + break; + case null: + switch (false) { + case a: + case console.log("FAIL"): + } + } + } + expect_stdout: "PASS" +} + +issue_5543_2: { + options = { + dead_code: true, + switches: true, + } + input: { + var a; + switch (a) { + default: + switch (42) { + case a: + case console.log("PASS"): + } + break; + case null: + switch (42) { + case a: + case console.log("FAIL"): + } + } + } + expect: { + var a; + switch (a) { + default: + switch (42) { + case a: + case console.log("PASS"): + } + break; + case null: + switch (42) { + case a: + case console.log("FAIL"): + } + } + } + expect_stdout: "PASS" +} diff --git a/test/compress/templates.js b/test/compress/templates.js new file mode 100644 index 00000000000..db3ab203c96 --- /dev/null +++ b/test/compress/templates.js @@ -0,0 +1,771 @@ +simple: { + input: { + console.log(`foo + bar\nbaz`); + } + expect_exact: "console.log(`foo\n bar\\nbaz`);" + expect_stdout: [ + "foo", + " bar", + "baz", + ] + node_version: ">=4" +} + +placeholder: { + input: { + console.log(`foo ${ function(a, b) { + return a * b; + }(6, 7) }`); + } + expect_exact: "console.log(`foo ${function(a,b){return a*b}(6,7)}`);" + expect_stdout: "foo 42" + node_version: ">=4" +} + +nested: { + input: { + console.log(`P${`A${"S"}`}S`); + } + expect_exact: 'console.log(`P${`A${"S"}`}S`);' + expect_stdout: "PASS" + node_version: ">=4" +} + +tagged: { + input: { + console.log(String.raw`foo\nbar`); + } + expect_exact: "console.log(String.raw`foo\\nbar`);" + expect_stdout: "foo\\nbar" + node_version: ">=4" +} + +tagged_chain: { + input: { + function f(strings) { + return strings.join("") || f; + } + console.log(f```${42}``pass`.toUpperCase()); + } + expect_exact: 'function f(strings){return strings.join("")||f}console.log(f```${42}``pass`.toUpperCase());' + expect_stdout: "PASS" + node_version: ">=4" +} + +tag_parentheses_arrow: { + input: { + console.log((s => s.raw[0])`\tPASS`.slice(2)); + } + expect_exact: "console.log((s=>s.raw[0])`\\tPASS`.slice(2));" + expect_stdout: "PASS" + node_version: ">=4" +} + +tag_parentheses_binary: { + options = { + collapse_vars: true, + toplevel: true, + unused: true, + } + input: { + var f = function() { + console.log("PASS"); + } || console + f``; + } + expect_exact: '(function(){console.log("PASS")}||console)``;' + expect_stdout: "PASS" + node_version: ">=4" +} + +tag_parentheses_new: { + input: { + (new function() { + return console.log; + })`foo`; + } + expect_exact: "(new function(){return console.log})`foo`;" + expect_stdout: true + node_version: ">=4" +} + +tag_parentheses_sequence: { + input: { + var o = { + f() { + console.log(this === o ? "FAIL" : "PASS"); + }, + }; + (42, o.f)``; + } + expect_exact: 'var o={f(){console.log(this===o?"FAIL":"PASS")}};(42,o.f)``;' + expect_stdout: "PASS" + node_version: ">=4" +} + +tag_parentheses_unary: { + input: { + var a; + try { + (~a)``; + (a++)``; + } catch (e) { + console.log("PASS"); + } + } + expect_exact: 'var a;try{(~a)``;(a++)``}catch(e){console.log("PASS")}' + expect_stdout: "PASS" + node_version: ">=4" +} + +malformed_escape: { + input: { + (function(s) { + s.forEach((c, i) => console.log(i, c, s.raw[i])); + return () => console.log(arguments); + })`\uFo${42}`(); + } + expect_exact: "(function(s){s.forEach((c,i)=>console.log(i,c,s.raw[i]));return()=>console.log(arguments)})`\\uFo${42}`();" + expect_stdout: true + node_version: ">=4" +} + +booleans: { + options = { + booleans: true, + evaluate: true, + templates: true, + } + input: { + var a; + console.log(`$${a}${a}` ? "PASS" : "FAIL"); + } + expect: { + var a; + console.log("$" + a + a ? "PASS" : "FAIL"); + } + expect_stdout: "PASS" + node_version: ">=4" +} + +escape_placeholder_1: { + options = { + templates: true, + } + input: { + console.log(`\${\n`); + } + expect: { + console.log(`\${ +`); + } + expect_stdout: [ + "${", + "", + ] + node_version: ">=4" +} + +escape_placeholder_2: { + options = { + evaluate: true, + templates: true, + } + input: { + console.log(`\n${"${"}\n`); + } + expect: { + console.log(` +\${ +`); + } + expect_stdout: [ + "", + "${", + "", + ] + node_version: ">=4" +} + +escape_placeholder_3: { + options = { + evaluate: true, + templates: true, + } + input: { + console.log(`\n$${"{"}\n`); + } + expect: { + console.log(` +\${ +`); + } + expect_stdout: [ + "", + "${", + "", + ] + node_version: ">=4" +} + +escape_placeholder_4: { + options = { + evaluate: true, + templates: true, + } + input: { + console.log(`\n${"$"}${"{"}\n`); + } + expect: { + console.log(` +\${ +`); + } + expect_stdout: [ + "", + "${", + "", + ] + node_version: ">=4" +} + +evaluate: { + options = { + evaluate: true, + templates: false, + } + input: { + console.log(`foo ${ function(a, b) { + return a * b; + }(6, 7) }`); + } + expect: { + console.log(`foo ${42}`); + } + expect_stdout: "foo 42" + node_version: ">=4" +} + +evaluate_templates: { + options = { + evaluate: true, + templates: true, + } + input: { + console.log(`foo ${ function(a, b) { + return a * b; + }(6, 7) }`); + } + expect: { + console.log("foo 42"); + } + expect_stdout: "foo 42" + node_version: ">=4" +} + +partial_evaluate: { + options = { + evaluate: true, + templates: true, + } + input: { + console.log(`${6 * 7} foo ${console ? `PA` + "SS" : `FA` + `IL`}`); + } + expect: { + console.log("42 foo " + (console ? "PASS" : "FAIL")); + } + expect_stdout: "42 foo PASS" + node_version: ">=4" +} + +malformed_evaluate_1: { + options = { + evaluate: true, + templates: true, + } + input: { + console.log(`\67 ${6 * 7}`); + } + expect: { + console.log(`\67 42`); + } + expect_stdout: true + node_version: ">=4" +} + +malformed_evaluate_2: { + options = { + evaluate: true, + templates: true, + } + input: { + console.log(`\u0${0}b${5}`); + } + expect: { + console.log(`\u00b` + 5); + } + expect_stdout: true + node_version: ">=4" +} + +malformed_evaluate_3: { + options = { + evaluate: true, + templates: true, + } + input: { + console.log(`\u${0}b${5}`); + } + expect: { + console.log(`\u0b5`); + } + expect_stdout: true + node_version: ">=4" +} + +malformed_evaluate_4: { + options = { + evaluate: true, + templates: true, + unsafe: true, + } + input: { + console.log(String.raw`\u0${0}b${5}`); + } + expect: { + console.log("\\u00b5"); + } + expect_stdout: "\\u00b5" + node_version: ">=8.10.0" +} + +unsafe_evaluate: { + options = { + evaluate: true, + templates: true, + unsafe: true, + } + input: { + console.log(String.raw`\uFo`); + } + expect: { + console.log("\\uFo"); + } + expect_stdout: "\\uFo" + node_version: ">=8.10.0" +} + +side_effects_1: { + options = { + side_effects: true, + } + input: { + `42`; + `${console.log("foo")}`; + console.log`\nbar`; + } + expect: { + console.log("foo"); + console.log`\nbar`; + } + expect_stdout: true + node_version: ">=4" +} + +side_effects_2: { + options = { + side_effects: true, + } + input: { + var o = { + f() { + console.log(this === o ? "FAIL" : "PASS"); + }, + }; + (42, o.f)``; + } + expect: { + var o = { + f() { + console.log(this === o ? "FAIL" : "PASS"); + }, + }; + (0, o.f)``; + } + expect_stdout: "PASS" + node_version: ">=4" +} + +unsafe_side_effects: { + options = { + side_effects: true, + unsafe: true, + } + input: { + `42`; + `${console.log("foo")}`; + String.raw`\nbar`; + } + expect: { + console.log("foo"); + } + expect_stdout: "foo" + node_version: ">=4" +} + +pure_funcs: { + options = { + pure_funcs: "Math.random", + side_effects: true, + } + input: { + Math.random`${console.log("PASS")}`; + } + expect: { + console.log("PASS"); + } + expect_stdout: "PASS" + node_version: ">=4" +} + +issue_4604: { + options = { + collapse_vars: true, + } + input: { + var a = 0, log = console.log; + a = "FAIL"; + (function() { + a = "PASS"; + })``; + log(a); + } + expect: { + var a = 0, log = console.log; + a = "FAIL"; + (function() { + a = "PASS"; + })``; + log(a); + } + expect_stdout: "PASS" + node_version: ">=4" +} + +issue_4606: { + options = { + evaluate: true, + strings: true, + templates: true, + } + input: { + console.log(`${typeof A} ${"\r"} ${"\\"} ${"`"}`); + } + expect: { + console.log(typeof A + " \r \\ `"); + } + expect_stdout: "undefined \r \\ `" + node_version: ">=4" +} + +issue_4630: { + options = { + evaluate: true, + templates: true, + } + input: { + console.log(`${/PASS/}`); + } + expect: { + console.log("/PASS/"); + } + expect_stdout: "/PASS/" + node_version: ">=4" +} + +issue_4676: { + options = { + evaluate: true, + reduce_vars: true, + templates: true, + toplevel: true, + unsafe: true, + unused: true, + } + input: { + function f(a) { + var b = `foo${a = "PASS"}`; + for (var c in f && b) + b.p; + return a; + } + console.log(f("FAIL")); + } + expect: { + console.log(function f(a) { + var b = "fooPASS"; + for (var c in f, b) + b.p; + return "PASS"; + }()); + } + expect_stdout: "PASS" + node_version: ">=4" +} + +issue_4931: { + options = { + evaluate: true, + templates: true, + unsafe: true, + } + input: { + console.log(String.raw`${typeof A} ${"\r"}`); + console.log(String.raw`${"\\"} ${"`"}`); + } + expect: { + console.log(String.raw`${typeof A} ${"\r"}`); + console.log("\\ `"); + } + expect_stdout: [ + "undefined \r", + "\\ `", + ] + node_version: ">=4" +} + +issue_5125_1: { + options = { + evaluate: true, + strings: true, + templates: true, + } + input: { + console.log(`PASS ${typeof A}`); + } + expect: { + console.log("PASS " + typeof A); + } + expect_stdout: "PASS undefined" + node_version: ">=4" +} + +issue_5125_2: { + options = { + evaluate: true, + strings: true, + templates: true, + } + input: { + console.log(`PASS +${typeof A}`); + } + expect: { + console.log(`PASS +` + typeof A); + } + expect_stdout: [ + "PASS", + "undefined", + ] + node_version: ">=4" +} + +issue_5125_3: { + options = { + evaluate: true, + strings: true, + templates: true, + } + input: { + console.log(`PASS\n${typeof A}`); + } + expect: { + console.log(`PASS +` + typeof A); + } + expect_stdout: [ + "PASS", + "undefined", + ] + node_version: ">=4" +} + +issue_5125_4: { + options = { + evaluate: true, + strings: true, + templates: true, + } + input: { + console.log(`PASS + +${typeof A}`); + } + expect: { + console.log(`PASS + +` + typeof A); + } + expect_stdout: [ + "PASS", + "", + "undefined", + ] + node_version: ">=4" +} + +issue_5125_5: { + options = { + evaluate: true, + strings: true, + templates: true, + } + input: { + console.log(`PASS\n\n${typeof A}`); + } + expect: { + console.log(`PASS + +` + typeof A); + } + expect_stdout: [ + "PASS", + "", + "undefined", + ] + node_version: ">=4" +} + +issue_5125_6: { + options = { + evaluate: true, + strings: true, + templates: true, + } + input: { + console.log(`${typeof A} ${typeof B} PASS`); + } + expect: { + console.log(typeof A + ` ${typeof B} PASS`); + } + expect_stdout: "undefined undefined PASS" + node_version: ">=4" +} + +issue_5125_7: { + options = { + evaluate: true, + strings: true, + templates: true, + } + input: { + console.log(`${typeof A} ${typeof B} ${typeof C} PASS`); + } + expect: { + console.log(typeof A + ` ${typeof B} ${typeof C} PASS`); + } + expect_stdout: "undefined undefined undefined PASS" + node_version: ">=4" +} + +issue_5125_8: { + options = { + evaluate: true, + strings: true, + templates: true, + } + input: { + console.log(`${typeof A}${typeof B}${typeof C} PASS`); + } + expect: { + console.log(typeof A + typeof B + typeof C + " PASS"); + } + expect_stdout: "undefinedundefinedundefined PASS" + node_version: ">=4" +} + +issue_5136: { + options = { + templates: true, + } + input: { + console.log(`${A = []}${A[0] = 42}`); + } + expect: { + console.log(`` + (A = []) + (A[0] = 42)); + } + expect_stdout: "42" + node_version: ">=4" +} + +issue_5145_1: { + options = { + strings: true, + templates: true, + } + input: { + var a = []; + console.log(`${a}${a[0] = 42} +`); + } + expect: { + var a = []; + console.log(`${a}${a[0] = 42} +`); + } + expect_stdout: [ + "42", + "", + ] + node_version: ">=4" +} + +issue_5145_2: { + options = { + strings: true, + templates: true, + } + input: { + var a = []; + console.log(`${a}${a}${a[0] = 42} +`); + } + expect: { + var a = []; + console.log("" + a + a + (a[0] = 42) + ` +`); + } + expect_stdout: [ + "42", + "", + ] + node_version: ">=4" +} + +issue_5199: { + options = { + collapse_vars: true, + toplevel: true, + unused: true, + } + input: { + var a = function() { + console.log(typeof b); + }``; + { + const b = a; + } + } + expect: { + var a = function() { + console.log(typeof b); + }``; + { + const b = a; + } + } + expect_stdout: "undefined" + node_version: ">=4" +} diff --git a/test/compress/transform.js b/test/compress/transform.js new file mode 100644 index 00000000000..e3621eb2763 --- /dev/null +++ b/test/compress/transform.js @@ -0,0 +1,134 @@ +booleans_evaluate: { + options = { + booleans: true, + evaluate: true, + } + input: { + console.log(typeof void 0 != "undefined"); + console.log(1 == 1, 1 === 1) + console.log(1 != 1, 1 !== 1) + } + expect: { + console.log(!1); + console.log(!0, !0); + console.log(!1, !1); + } + expect_stdout: true +} + +booleans_global_defs: { + options = { + booleans: true, + evaluate: true, + global_defs: { + A: true, + }, + } + input: { + console.log(A == 1); + } + expect: { + console.log(!0); + } +} + +condition_evaluate: { + options = { + booleans: true, + dead_code: false, + evaluate: true, + loops: false, + } + input: { + while (1 === 2); + for (; 1 == true;); + if (void 0 == null); + } + expect: { + while (0); + for (; 1;); + if (1); + } +} + +if_else_empty: { + options = { + conditionals: true, + } + input: { + if ({} ? a : b); else {} + } + expect: { + ({}), a; + } +} + +label_if_break: { + options = { + conditionals: true, + dead_code: true, + evaluate: true, + side_effects: true, + unused: true, + } + input: { + L: if (true) { + a; + break L; + } + } + expect: { + a; + } +} + +while_if_break: { + options = { + conditionals: true, + loops: true, + sequences: true, + } + input: { + while (a) { + if (b) if(c) d; + if (e) break; + } + } + expect: { + for (; a && (b && c && d, !e);); + } +} + +if_return: { + options = { + booleans: true, + conditionals: true, + if_return: true, + passes: 2, + sequences: true, + side_effects: true, + } + input: { + function f(w, x, y, z) { + if (x) return; + if (w) { + if (y) return; + } else if (z) return; + if (x == y) return true; + + if (x) w(); + if (y) z(); + return true; + } + } + expect: { + function f(w, x, y, z) { + if (!x) { + if (w) { + if (y) return; + } else if (z) return; + return x != y && (x && w(), y) && z(), !0; + } + } + } +} diff --git a/test/compress/typeof.js b/test/compress/typeof.js index cefdd43c211..5faa72e93dc 100644 --- a/test/compress/typeof.js +++ b/test/compress/typeof.js @@ -1,7 +1,8 @@ typeof_evaluation: { options = { - evaluate: true - }; + evaluate: true, + typeofs: true, + } input: { a = typeof 1; b = typeof 'test'; @@ -23,3 +24,681 @@ typeof_evaluation: { h='undefined'; } } + +typeof_in_boolean_context: { + options = { + booleans: true, + conditionals: true, + evaluate: true, + side_effects: true, + } + input: { + function f1(x) { return typeof x ? "yes" : "no"; } + function f2() { return typeof g()? "Yes" : "No"; } + typeof 0 ? foo() : bar(); + !typeof console.log(1); + var a = !typeof console.log(2); + if (typeof (1 + foo())); + } + expect: { + function f1(x) { return "yes"; } + function f2() { return g(), "Yes"; } + foo(); + console.log(1); + var a = !(console.log(2), 1); + foo(); + } +} + +issue_1668: { + options = { + booleans: true, + } + input: { + if (typeof bar); + } + expect: { + if (1); + } +} + +typeof_defun_1: { + options = { + evaluate: true, + inline: true, + passes: 2, + reduce_vars: true, + side_effects: true, + toplevel: true, + typeofs: true, + unused: true, + } + input: { + function f() { + console.log("YES"); + } + function g() { + h = 42; + console.log("NOPE"); + } + function h() { + console.log("YUP"); + } + g = 42; + "function" == typeof f && f(); + "function" == typeof g && g(); + "function" == typeof h && h(); + } + expect: { + function h() { + console.log("YUP"); + } + console.log("YES"); + h(); + } + expect_stdout: [ + "YES", + "YUP", + ] +} + +typeof_defun_2: { + options = { + evaluate: true, + reduce_vars: true, + toplevel: true, + typeofs: true, + } + input: { + var f = function() { + console.log(x); + }; + var x = 0; + x++ < 2 && typeof f == "function" && f(); + x++ < 2 && typeof f == "function" && f(); + x++ < 2 && typeof f == "function" && f(); + } + expect: { + var f = function() { + console.log(x); + }; + var x = 0; + x++ < 2 && f(); + x++ < 2 && f(); + x++ < 2 && f(); + } + expect_stdout: [ + "1", + "2", + ] +} + +duplicate_defun_arg_name: { + options = { + evaluate: true, + reduce_vars: true, + typeofs: true, + } + input: { + function long_name(long_name) { + return typeof long_name; + } + console.log(typeof long_name, long_name()); + } + expect: { + function long_name(long_name) { + return typeof long_name; + } + console.log(typeof long_name, long_name()); + } + expect_stdout: "function undefined" +} + +duplicate_lambda_arg_name: { + options = { + evaluate: true, + reduce_vars: true, + typeofs: true, + } + input: { + console.log(function long_name(long_name) { + return typeof long_name; + }()); + } + expect: { + console.log("undefined"); + } + expect_stdout: "undefined" +} + +issue_2728_1: { + options = { + evaluate: true, + reduce_vars: true, + typeofs: true, + } + input: { + (function arguments() { + console.log(typeof arguments); + })(); + } + expect: { + (function arguments() { + console.log(typeof arguments); + })(); + } + expect_stdout: "object" +} + +issue_2728_2: { + options = { + evaluate: true, + reduce_vars: true, + typeofs: true, + } + input: { + function arguments() { + return typeof arguments; + } + console.log(typeof arguments, arguments()); + } + expect: { + function arguments() { + return typeof arguments; + } + console.log(typeof arguments, arguments()); + } + expect_stdout: "function object" +} + +issue_2728_3: { + options = { + evaluate: true, + reduce_vars: true, + typeofs: true, + } + input: { + (function() { + function arguments() { + } + console.log(typeof arguments); + })(); + } + expect: { + (function() { + function arguments() { + } + console.log("function"); + })(); + } + expect_stdout: "function" +} + +issue_2728_4: { + options = { + evaluate: true, + reduce_vars: true, + toplevel: true, + typeofs: true, + } + input: { + function arguments() { + } + console.log(typeof arguments); + } + expect: { + function arguments() { + } + console.log("function"); + } + expect_stdout: "function" +} + +issue_2728_5: { + options = { + evaluate: true, + reduce_vars: true, + typeofs: true, + } + input: { + (function arguments(arguments) { + console.log(typeof arguments); + })(); + } + expect: { + (function arguments(arguments) { + console.log(typeof arguments); + })(); + } + expect_stdout: "undefined" +} + +issue_2728_6: { + options = { + evaluate: true, + reduce_vars: true, + typeofs: true, + } + input: { + function arguments(arguments) { + return typeof arguments; + } + console.log(typeof arguments, arguments()); + } + expect: { + function arguments(arguments) { + return typeof arguments; + } + console.log(typeof arguments, arguments()); + } + expect_stdout: "function undefined" +} + +typeof_defined_1: { + options = { + side_effects: true, + typeofs: true, + } + input: { + "undefined" == typeof A && A; + "undefined" != typeof A && A; + "undefined" == typeof A || A; + "undefined" != typeof A || A; + } + expect: { + "undefined" == typeof A && A; + "undefined" == typeof A && A; + } +} + +typeof_defined_2: { + options = { + side_effects: true, + typeofs: true, + } + input: { + "function" == typeof A && A; + "function" != typeof A && A; + "function" == typeof A || A; + "function" != typeof A || A; + } + expect: { + "function" != typeof A && A; + "function" != typeof A && A; + } +} + +typeof_defined_3: { + options = { + side_effects: true, + typeofs: true, + } + input: { + "undefined" == typeof A && "undefined" == typeof B && (A, B); + "undefined" == typeof A && "undefined" != typeof B && (A, B); + "undefined" != typeof A && "undefined" == typeof B && (A, B); + "undefined" != typeof A && "undefined" != typeof B && (A, B); + "undefined" == typeof A && "undefined" == typeof B || (A, B); + "undefined" == typeof A && "undefined" != typeof B || (A, B); + "undefined" != typeof A && "undefined" == typeof B || (A, B); + "undefined" != typeof A && "undefined" != typeof B || (A, B); + "undefined" == typeof A || "undefined" == typeof B && (A, B); + "undefined" == typeof A || "undefined" != typeof B && (A, B); + "undefined" != typeof A || "undefined" == typeof B && (A, B); + "undefined" != typeof A || "undefined" != typeof B && (A, B); + "undefined" == typeof A || "undefined" == typeof B || (A, B); + "undefined" == typeof A || "undefined" != typeof B || (A, B); + "undefined" != typeof A || "undefined" == typeof B || (A, B); + "undefined" != typeof A || "undefined" != typeof B || (A, B); + } + expect: { + "undefined" == typeof A && "undefined" == typeof B && (A, B); + "undefined" == typeof A && "undefined" != typeof B && A; + "undefined" != typeof A && "undefined" == typeof B && B; + // dropped + "undefined" == typeof A && "undefined" == typeof B || (A, B); + "undefined" == typeof A && "undefined" != typeof B || (A, B); + "undefined" != typeof A && "undefined" == typeof B || (A, B); + "undefined" != typeof A && "undefined" != typeof B || (A, B); + "undefined" != typeof A && "undefined" == typeof B && B; + // dropped + "undefined" == typeof A && "undefined" == typeof B && (A, B); + "undefined" == typeof A && "undefined" != typeof B && A; + // dropped + "undefined" != typeof A && "undefined" == typeof B && B; + "undefined" == typeof A && "undefined" != typeof B && A; + "undefined" == typeof A && "undefined" == typeof B && (A, B); + } +} + +typeof_defined_4: { + options = { + side_effects: true, + typeofs: true, + } + input: { + "object" == typeof A && "object" == typeof B && (A, B); + "object" == typeof A && "object" != typeof B && (A, B); + "object" != typeof A && "object" == typeof B && (A, B); + "object" != typeof A && "object" != typeof B && (A, B); + "object" == typeof A && "object" == typeof B || (A, B); + "object" == typeof A && "object" != typeof B || (A, B); + "object" != typeof A && "object" == typeof B || (A, B); + "object" != typeof A && "object" != typeof B || (A, B); + "object" == typeof A || "object" == typeof B && (A, B); + "object" == typeof A || "object" != typeof B && (A, B); + "object" != typeof A || "object" == typeof B && (A, B); + "object" != typeof A || "object" != typeof B && (A, B); + "object" == typeof A || "object" == typeof B || (A, B); + "object" == typeof A || "object" != typeof B || (A, B); + "object" != typeof A || "object" == typeof B || (A, B); + "object" != typeof A || "object" != typeof B || (A, B); + } + expect: { + // dropped + "object" == typeof A && "object" != typeof B && B; + "object" != typeof A && "object" == typeof B && A; + "object" != typeof A && "object" != typeof B && (A, B); + "object" == typeof A && "object" == typeof B || (A, B); + "object" == typeof A && "object" != typeof B || (A, B); + "object" != typeof A && "object" == typeof B || (A, B); + "object" != typeof A && "object" != typeof B || (A, B); + "object" != typeof A && "object" == typeof B && A; + "object" != typeof A && "object" != typeof B && (A, B); + // dropped + "object" == typeof A && "object" != typeof B && B; + "object" != typeof A && "object" != typeof B && (A, B); + "object" != typeof A && "object" == typeof B && A; + "object" == typeof A && "object" != typeof B && B; + // dropped + } +} + +emberjs_global: { + options = { + comparisons: true, + conditionals: true, + if_return: true, + passes: 2, + side_effects: true, + toplevel: true, + typeofs: true, + unused: true, + } + input: { + var a; + if (typeof A === "object") { + a = A; + } else if (typeof B === "object") { + a = B; + } else { + throw new Error("PASS"); + } + } + expect: { + if ("object" != typeof A && "object" != typeof B) + throw new Error("PASS"); + } + expect_stdout: Error("PASS") +} + +reassign: { + options = { + comparisons: true, + conditionals: true, + passes: 2, + typeofs: true, + } + input: { + A = console; + if ("undefined" == typeof A) + console.log("FAIL 1"); + else { + A = void 0; + while (console.log(void 0 === A ? "PASS" : "FAIL 2")); + } + } + expect: { + A = console; + if ("undefined" == typeof A) + console.log("FAIL 1"); + else { + A = void 0; + while (console.log(void 0 === A ? "PASS" : "FAIL 2")); + } + } + expect_stdout: "PASS" +} + +reassign_call: { + options = { + comparisons: true, + conditionals: true, + passes: 2, + typeofs: true, + } + input: { + A = console; + function f() { + A = void 0; + } + if ("undefined" == typeof A) + console.log("FAIL 1"); + else { + f(); + while (console.log(void 0 === A ? "PASS" : "FAIL 2")); + } + } + expect: { + A = console; + function f() { + A = void 0; + } + if ("undefined" == typeof A) + console.log("FAIL 1"); + else { + f(); + while (console.log(void 0 === A ? "PASS" : "FAIL 2")); + } + } + expect_stdout: "PASS" +} + +reassign_conditional: { + options = { + comparisons: true, + conditionals: true, + passes: 2, + typeofs: true, + } + input: { + A = console; + if ("undefined" == typeof A) + console.log("FAIL 1"); + else { + A &&= void 0; + while (console.log(void 0 === A ? "PASS" : "FAIL 2")); + } + } + expect: { + A = console; + if ("undefined" == typeof A) + console.log("FAIL 1"); + else { + A &&= void 0; + while (console.log(void 0 === A ? "PASS" : "FAIL 2")); + } + } + expect_stdout: "PASS" + node_version: ">=15" +} + +reassign_do: { + options = { + comparisons: true, + conditionals: true, + if_return: true, + passes: 2, + reduce_vars: true, + typeofs: true, + } + input: { + A = console; + (function() { + if ("undefined" == typeof A) + return; + var a = A, i = 2; + do { + console.log(void 0 === A, void 0 === a); + A = void 0; + } while (--i); + })(); + } + expect: { + A = console; + (function() { + if ("undefined" != typeof A) { + var a = A, i = 2; + do { + console.log(void 0 === A, (a, false)); + A = void 0; + } while (--i); + } + })(); + } + expect_stdout: [ + "false false", + "true false", + ] +} + +reassign_for: { + options = { + comparisons: true, + conditionals: true, + passes: 2, + reduce_vars: true, + toplevel: true, + typeofs: true, + } + input: { + if (A = console, "undefined" != typeof A) + for (var a = A, i = 0; i < 2; i++) + console.log(void 0 === A, void 0 === a), + A = void 0; + } + expect: { + if (A = console, "undefined" != typeof A) + for (var a = A, i = 0; i < 2; i++) + console.log(void 0 === A, (a, false)), + A = void 0; + } + expect_stdout: [ + "false false", + "true false", + ] +} + +reassign_for_in: { + options = { + comparisons: true, + conditionals: true, + passes: 2, + reduce_vars: true, + typeofs: true, + } + input: { + (A = console) && "undefined" != typeof A && function(a) { + for (var k in [ a = A, 42 ]) { + console.log(void 0 === A, void 0 === a); + A = void 0; + } + }(); + } + expect: { + (A = console) && "undefined" != typeof A && function(a) { + for (var k in [ a = A, 42 ]) { + console.log(void 0 === A, (a, false)); + A = void 0; + } + }(); + } + expect_stdout: [ + "false false", + "true false", + ] +} + +reassign_iife: { + options = { + comparisons: true, + conditionals: true, + passes: 2, + typeofs: true, + } + input: { + A = console; + if ("undefined" == typeof A) + console.log("FAIL 1"); + else (function() { + A = void 0; + })(console.log(void 0 === A ? "FAIL 2" : "PASS")); + } + expect: { + A = console; + "undefined" == typeof A ? console.log("FAIL 1") : function() { + A = void 0; + }(console.log((A, false) ? "FAIL 2" : "PASS")); + } + expect_stdout: "PASS" +} + +reassign_property: { + options = { + comparisons: true, + conditionals: true, + passes: 2, + typeofs: true, + } + input: { + A = console; + if ("undefined" == typeof A) + console.log("FAIL 1"); + else { + A.p = void 0; + console.log(void 0 === A ? "FAIL 2" : "PASS"); + } + } + expect: { + A = console; + if ("undefined" == typeof A) + console.log("FAIL 1"); + else { + A.p = void 0; + console.log((A, false) ? "FAIL 2" : "PASS"); + } + } + expect_stdout: "PASS" +} + +issue_3817: { + options = { + comparisons: true, + conditionals: true, + passes: 2, + typeofs: true, + } + input: { + if ("A" == typeof A || !console.log("PASS")) switch (false) { + case "undefined" == typeof A: + console.log("FAIL"); + } + } + expect: { + if ("A" == typeof A || !console.log("PASS")) switch (false) { + case "undefined" == typeof A: + console.log("FAIL"); + } + } + expect_stdout: "PASS" +} diff --git a/test/compress/unicode.js b/test/compress/unicode.js index 9fb9ab8cf1a..abbd417df5b 100644 --- a/test/compress/unicode.js +++ b/test/compress/unicode.js @@ -1,5 +1,39 @@ +ascii_only_false: { + options = {} + beautify = { + ascii_only: false, + } + input: { + console.log( + "\x000\x001\x007\x008\x00", + "\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f", + "\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f", + "\x20\x21\x22\x23 ... \x7d\x7e\x7f\x80\x81 ... \xfe\xff\u0fff\uffff" + ); + } + expect_exact: 'console.log("\\x000\\x001\\x007\\x008\\0","\\0\x01\x02\x03\x04\x05\x06\x07\\b\\t\\n\\v\\f\\r\x0e\x0f","\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f",\' !"# ... }~\x7f\x80\x81 ... \xfe\xff\u0fff\uffff\');' + expect_stdout: true +} + +ascii_only_true: { + options = {} + beautify = { + ascii_only: true, + } + input: { + console.log( + "\x000\x001\x007\x008\x00", + "\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f", + "\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f", + "\x20\x21\x22\x23 ... \x7d\x7e\x7f\x80\x81 ... \xfe\xff\u0fff\uffff" + ); + } + expect_exact: 'console.log("\\x000\\x001\\x007\\x008\\0","\\0\\x01\\x02\\x03\\x04\\x05\\x06\\x07\\b\\t\\n\\v\\f\\r\\x0e\\x0f","\\x10\\x11\\x12\\x13\\x14\\x15\\x16\\x17\\x18\\x19\\x1a\\x1b\\x1c\\x1d\\x1e\\x1f",\' !"# ... }~\\x7f\\x80\\x81 ... \\xfe\\xff\\u0fff\\uffff\');' + expect_stdout: true +} + unicode_parse_variables: { - options = {}; + options = {} input: { var a = {}; a.你好 = 456; @@ -15,3 +49,175 @@ unicode_parse_variables: { var l০ = 3; } } + +unicode_escaped_identifier_1: { + input: { + var \u0061 = "\ud800\udc00"; + console.log(a); + } + expect_exact: 'var a="\ud800\udc00";console.log(a);' + expect_stdout: "\ud800\udc00" +} + +unicode_escaped_identifier_2: { + input: { + var \u{61} = "foo"; + var \u{10000} = "bar"; + console.log(a, \u{10000}); + } + expect_exact: 'var a="foo";var \u{10000}="bar";console.log(a,\u{10000});' + expect_stdout: "foo bar" + // non-BMP support is platform-dependent on Node.js v4 + node_version: ">=6" +} + +unicode_identifier_ascii_only: { + beautify = { + ascii_only: true, + } + input: { + var \u0061 = "testing \udbc4\udd11"; + var bar = "h\u0065llo"; + console.log(a, \u0062\u0061r); + } + expect_exact: 'var a="testing \\udbc4\\udd11";var bar="hello";console.log(a,bar);' + expect_stdout: "testing \udbc4\udd11 hello" +} + +unicode_string_literals: { + beautify = { + ascii_only: true, + } + input: { + var a = "6 length unicode character: \udbc4\udd11"; + console.log(\u0061); + } + expect_exact: 'var a="6 length unicode character: \\udbc4\\udd11";console.log(a);' + expect_stdout: "6 length unicode character: \udbc4\udd11" +} + +check_escape_style: { + beautify = { + ascii_only: true, + } + input: { + var a = "\x01"; + var \ua0081 = "\x10"; // \u0081 only in ID_Continue + var \u0100 = "\u0100"; + var \u1000 = "\u1000"; + var \u1000 = "\ud800\udc00"; + var \u3f80 = "\udbc0\udc00"; + console.log(\u0061, \ua0081, \u0100, \u1000, \u3f80); + } + expect_exact: 'var a="\\x01";var \\ua0081="\\x10";var \\u0100="\\u0100";var \\u1000="\\u1000";var \\u1000="\\ud800\\udc00";var \\u3f80="\\udbc0\\udc00";console.log(a,\\ua0081,\\u0100,\\u1000,\\u3f80);' + expect_stdout: "\u0001 \u0010 \u0100 \ud800\udc00 \udbc0\udc00" +} + +escape_non_escaped_identifier: { + beautify = { + ascii_only: true, + } + input: { + var µþ = "µþ"; + console.log(\u00b5þ); + } + expect_exact: 'var \\u00b5\\u00fe="\\xb5\\xfe";console.log(\\u00b5\\u00fe);' + expect_stdout: "µþ" +} + +non_escape_2_non_escape: { + beautify = { + ascii_only: false, + } + input: { + var µþ = "µþ"; + console.log(\u00b5þ); + } + expect_exact: 'var µþ="µþ";console.log(µþ);' + expect_stdout: "µþ" +} + +issue_2242_1: { + beautify = { + ascii_only: false, + } + input: { + console.log("\ud83d", "\ude00", "\ud83d\ude00", "\ud83d@\ude00"); + } + expect_exact: 'console.log("\\ud83d","\\ude00","\ud83d\ude00","\\ud83d@\\ude00");' + expect_stdout: "\ud83d \ude00 \ud83d\ude00 \ud83d@\ude00" +} + +issue_2242_2: { + beautify = { + ascii_only: true, + } + input: { + console.log("\ud83d", "\ude00", "\ud83d\ude00", "\ud83d@\ude00"); + } + expect_exact: 'console.log("\\ud83d","\\ude00","\\ud83d\\ude00","\\ud83d@\\ude00");' + expect_stdout: "\ud83d \ude00 \ud83d\ude00 \ud83d@\ude00" +} + +issue_2242_3: { + options = { + evaluate: false, + } + input: { + console.log("\ud83d" + "\ude00", "\ud83d" + "@" + "\ude00"); + } + expect_exact: 'console.log("\\ud83d"+"\\ude00","\\ud83d"+"@"+"\\ude00");' + expect_stdout: "\ud83d\ude00 \ud83d@\ude00" +} + +issue_2242_4: { + options = { + evaluate: true, + } + input: { + console.log("\ud83d" + "\ude00", "\ud83d" + "@" + "\ude00"); + } + expect_exact: 'console.log("\ud83d\ude00","\\ud83d@\\ude00");' + expect_stdout: "\ud83d\ude00 \ud83d@\ude00" +} + +issue_2569: { + input: { + new RegExp("[\udc42-\udcaa\udd74-\udd96\ude45-\ude4f\udea3-\udecc]"); + } + expect_exact: 'new RegExp("[\\udc42-\\udcaa\\udd74-\\udd96\\ude45-\\ude4f\\udea3-\\udecc]");' +} + +surrogate_pair: { + beautify = { + ascii_only: false, + } + input: { + var \u{2f800} = { + \u{2f801}: "\u{100000}", + }; + \u{2f800}.\u{2f802} = "\u{100001}"; + console.log(typeof \u{2f800}, \u{2f800}.\u{2f801}, \u{2f800}["\u{2f802}"]); + } + expect_exact: 'var \ud87e\udc00={"\ud87e\udc01":"\udbc0\udc00"};\ud87e\udc00.\ud87e\udc02="\udbc0\udc01";console.log(typeof \ud87e\udc00,\ud87e\udc00.\ud87e\udc01,\ud87e\udc00["\ud87e\udc02"]);' + expect_stdout: "object \udbc0\udc00 \udbc0\udc01" + // non-BMP support is platform-dependent on Node.js v4 + node_version: ">=6" +} + +surrogate_pair_ascii: { + beautify = { + ascii_only: true, + } + input: { + var \u{2f800} = { + \u{2f801}: "\u{100000}", + }; + \u{2f800}.\u{2f802} = "\u{100001}"; + console.log(typeof \u{2f800}, \u{2f800}.\u{2f801}, \u{2f800}["\u{2f802}"]); + } + expect_exact: 'var \\u{2f800}={"\\ud87e\\udc01":"\\udbc0\\udc00"};\\u{2f800}.\\u{2f802}="\\udbc0\\udc01";console.log(typeof \\u{2f800},\\u{2f800}.\\u{2f801},\\u{2f800}["\\ud87e\\udc02"]);' + expect_stdout: "object \udbc0\udc00 \udbc0\udc01" + // non-BMP support is platform-dependent on Node.js v4 + node_version: ">=6" +} diff --git a/test/compress/varify.js b/test/compress/varify.js new file mode 100644 index 00000000000..c9f67f42b71 --- /dev/null +++ b/test/compress/varify.js @@ -0,0 +1,882 @@ +reduce_merge_const: { + options = { + merge_vars: true, + reduce_vars: true, + toplevel: true, + unused: true, + varify: true, + } + input: { + const a = console; + console.log(typeof a); + var b = typeof a; + console.log(b); + } + expect: { + var a = console; + console.log(typeof a); + a = typeof a; + console.log(a); + } + expect_stdout: [ + "object", + "object", + ] +} + +reduce_merge_let: { + options = { + merge_vars: true, + reduce_vars: true, + toplevel: true, + unused: true, + varify: true, + } + input: { + "use strict"; + let a = console; + console.log(typeof a); + var b = typeof a; + console.log(b); + } + expect: { + "use strict"; + var a = console; + console.log(typeof a); + a = typeof a; + console.log(a); + } + expect_stdout: [ + "object", + "object", + ] + node_version: ">=4" +} + +reduce_block_const: { + options = { + reduce_vars: true, + toplevel: true, + varify: true, + } + input: { + { + const a = typeof console; + console.log(a); + } + } + expect: { + var a = typeof console; + console.log(a); + } + expect_stdout: "object" +} + +reduce_block_let: { + options = { + reduce_vars: true, + toplevel: true, + varify: true, + } + input: { + "use strict"; + { + let a = typeof console; + console.log(a); + } + } + expect: { + "use strict"; + var a = typeof console; + console.log(a); + } + expect_stdout: "object" + node_version: ">=4" +} + +hoist_props_const: { + options = { + hoist_props: true, + passes: 2, + reduce_vars: true, + toplevel: true, + varify: true, + } + input: { + { + const o = { + p: "PASS", + }; + console.log(o.p); + } + } + expect: { + var o, o_p = "PASS"; + console.log(o_p); + } + expect_stdout: "PASS" +} + +hoist_props_let: { + options = { + hoist_props: true, + passes: 2, + reduce_vars: true, + toplevel: true, + varify: true, + } + input: { + "use strict"; + { + let o = { + p: "PASS", + }; + console.log(o.p); + } + } + expect: { + "use strict"; + var o, o_p = "PASS"; + console.log(o_p); + } + expect_stdout: "PASS" + node_version: ">=4" +} + +scope_adjustment_const: { + options = { + conditionals: true, + inline: true, + reduce_vars: true, + toplevel: true, + unused: true, + varify: true, + } + input: { + for (var k in [ 42 ]) + console.log(function f() { + if (k) { + const a = 0; + } + }()); + } + expect: { + for (var k in [ 42 ]) + console.log(void (k && 0)); + } + expect_stdout: "undefined" +} + +scope_adjustment_let: { + options = { + conditionals: true, + inline: true, + reduce_vars: true, + toplevel: true, + unused: true, + varify: true, + } + input: { + "use strict"; + for (var k in [ 42 ]) + console.log(function f() { + if (k) { + let a = 0; + } + }()); + } + expect: { + "use strict"; + for (var k in [ 42 ]) + console.log(void (k && 0)); + } + expect_stdout: "undefined" + node_version: ">=4" +} + +escaped_const: { + options = { + reduce_vars: true, + toplevel: true, + varify: true, + } + input: { + const log = console.log; + log("PASS"); + } + expect: { + var log = console.log; + log("PASS"); + } + expect_stdout: "PASS" +} + +escaped_let: { + options = { + reduce_vars: true, + toplevel: true, + varify: true, + } + input: { + "use strict"; + let log = console.log; + log("PASS"); + } + expect: { + "use strict"; + var log = console.log; + log("PASS"); + } + expect_stdout: "PASS" + node_version: ">=4" +} + +issue_4191_const: { + options = { + functions: true, + reduce_vars: true, + toplevel: true, + unused: true, + varify: true, + } + input: { + const a = function() {}; + console.log(typeof a, a()); + } + expect: { + function a() {}; + console.log(typeof a, a()); + } + expect_stdout: "function undefined" +} + +issue_4191_let: { + options = { + functions: true, + reduce_vars: true, + toplevel: true, + unused: true, + varify: true, + } + input: { + "use strict"; + let a = function() {}; + console.log(typeof a, a()); + } + expect: { + "use strict"; + function a() {}; + console.log(typeof a, a()); + } + expect_stdout: "function undefined" + node_version: ">=4" +} + +forin_const_1: { + options = { + join_vars: true, + reduce_vars: true, + toplevel: true, + varify: true, + } + input: { + const o = { + foo: 42, + bar: "PASS", + }; + for (const k in o) + console.log(k, o[k]); + } + expect: { + var o = { + foo: 42, + bar: "PASS", + }; + for (const k in o) + console.log(k, o[k]); + } + expect_stdout: true + node_version: ">=4" +} + +forin_const_2: { + options = { + join_vars: true, + reduce_vars: true, + toplevel: true, + varify: true, + } + input: { + const o = { + p: 42, + q: "PASS", + }; + for (const [ k ] in o) + console.log(k, o[k]); + } + expect: { + var o = { + p: 42, + q: "PASS", + }, k; + for ([ k ] in o) + console.log(k, o[k]); + } + expect_stdout: [ + "p 42", + "q PASS", + ] + node_version: ">=6" +} + +forin_const_3: { + options = { + module: true, + reduce_vars: true, + toplevel: true, + varify: true, + } + input: { + "use strict"; + const o = { + p: 42, + q: "PASS", + }; + for (const k in o) + (function f() { + console.log(k, o[k]); + })(); + } + expect: { + "use strict"; + let o = { + p: 42, + q: "PASS", + }; + for (let k in o) + (function f() { + console.log(k, o[k]); + })(); + } + expect_stdout: [ + "p 42", + "q PASS", + ] + node_version: ">=4" +} + +forin_let_1: { + options = { + join_vars: true, + reduce_vars: true, + toplevel: true, + varify: true, + } + input: { + "use strict"; + let o = { + foo: 42, + bar: "PASS", + }; + for (let k in o) + console.log(k, o[k]); + } + expect: { + "use strict"; + var o = { + foo: 42, + bar: "PASS", + }, k; + for (k in o) + console.log(k, o[k]); + } + expect_stdout: [ + "foo 42", + "bar PASS", + ] + node_version: ">=4" +} + +forin_let_2: { + options = { + join_vars: true, + reduce_vars: true, + toplevel: true, + varify: true, + } + input: { + let o = { + p: 42, + q: "PASS", + }; + for (let [ k ] in o) + console.log(k, o[k]); + } + expect: { + var o = { + p: 42, + q: "PASS", + }, k; + for ([ k ] in o) + console.log(k, o[k]); + } + expect_stdout: [ + "p 42", + "q PASS", + ] + node_version: ">=6" +} + +loop_scope_1: { + options = { + toplevel: true, + varify: true, + } + input: { + "use strict"; + var o = { foo: 1, bar: 2 }; + for (let i in o) { + console.log(i); + } + for (const j in o) + setTimeout(() => console.log(j), 0); + for (let k in o) + setTimeout(function() { + console.log(k); + }, 0); + } + expect: { + "use strict"; + var o = { foo: 1, bar: 2 }; + for (var i in o) + console.log(i); + for (const j in o) + setTimeout(() => console.log(j), 0); + for (let k in o) + setTimeout(function() { + console.log(k); + }, 0); + } + expect_stdout: [ + "foo", + "bar", + "foo", + "bar", + "foo", + "bar", + ] + node_version: ">=4" +} + +loop_scope_2: { + options = { + reduce_vars: true, + toplevel: true, + varify: true, + } + input: { + "use strict"; + var a = [ "foo", "bar" ]; + for (var i = 0; i < a.length; i++) { + const x = a[i]; + console.log(x); + let y = a[i]; + setTimeout(() => console.log(y), 0); + const z = a[i]; + setTimeout(function() { + console.log(z); + }, 0); + } + } + expect: { + "use strict"; + var a = [ "foo", "bar" ]; + for (var i = 0; i < a.length; i++) { + var x = a[i]; + console.log(x); + let y = a[i]; + setTimeout(() => console.log(y), 0); + const z = a[i]; + setTimeout(function() { + console.log(z); + }, 0); + } + } + expect_stdout: [ + "foo", + "bar", + "foo", + "foo", + "bar", + "bar", + ] + node_version: ">=4" +} + +issue_4290_1_const: { + options = { + reduce_vars: true, + toplevel: true, + varify: true, + } + input: { + const a = 0; + var a; + } + expect: { + const a = 0; + var a; + } + expect_stdout: true +} + +issue_4290_1_let: { + options = { + reduce_vars: true, + toplevel: true, + varify: true, + } + input: { + "use strict"; + let a = 0; + var a; + } + expect: { + "use strict"; + let a = 0; + var a; + } + expect_stdout: true + node_version: ">=4" +} + +drop_forin_let: { + options = { + loops: true, + toplevel: true, + unused: true, + varify: true, + } + input: { + "use strict"; + for (let a in console.log("PASS")); + } + expect: { + "use strict"; + console.log("PASS"); + } + expect_stdout: "PASS" + node_version: ">=4" +} + +default_init: { + options = { + join_vars: true, + reduce_vars: true, + unused: true, + varify: true, + } + input: { + A = "PASS"; + (function() { + "use strict"; + let a; + a = A; + console.log(a); + })(); + } + expect: { + A = "PASS"; + (function() { + "use strict"; + var a = A; + console.log(a); + })(); + } + expect_stdout: "PASS" + node_version: ">=4" +} + +issue_4933_1: { + options = { + reduce_vars: true, + toplevel: true, + unused: true, + varify: true, + } + input: { + console.log(f()); + function f() { + var a; + for (console in a = [ f ]) { + const b = a; + } + } + } + expect: { + console.log(function f() { + var a; + for (console in a = [ f ]) { + const b = a; + } + }()); + } + expect_stdout: "undefined" +} + +issue_4933_2: { + options = { + passes: 2, + reduce_vars: true, + toplevel: true, + unused: true, + varify: true, + } + input: { + console.log(f()); + function f() { + var a; + for (console in a = [ f ]) { + const b = a; + } + } + } + expect: { + console.log(function f() { + for (console in [ f ]); + }()); + } + expect_stdout: "undefined" +} + +issue_4954: { + options = { + functions: true, + reduce_vars: true, + unused: true, + varify: true, + } + input: { + "use strict"; + (function() { + { + let a = console; + console.log(typeof a); + } + { + let a = function() {}; + a && console.log(typeof a); + } + })(); + } + expect: { + "use strict"; + (function() { + var a = console; + console.log(typeof a); + { + let a = function() {}; + a && console.log(typeof a); + } + })(); + } + expect_stdout: [ + "object", + "function", + ] + node_version: ">=4" +} + +issue_5516: { + options = { + reduce_funcs: true, + reduce_vars: true, + unused: true, + varify: true, + } + input: { + "use strict"; + console.log(typeof function() { + { + let a; + } + { + const a = function() {}; + return a; + } + }()); + } + expect: { + "use strict"; + console.log(typeof function() { + { + const a = function() {}; + return a; + } + }()); + } + expect_stdout: "function" + node_version: ">=4" +} + +issue_5697_1: { + options = { + if_return: true, + inline: true, + reduce_vars: true, + unused: true, + varify: true, + } + input: { + console.log(function() { + f(); + return typeof a; + function f() { + (function() { + for (var k in { foo: 42 }) { + const a = k; + console.log(a); + } + })(); + } + }()); + } + expect: { + console.log(function() { + (function() { + for (var k in { foo: 42 }) { + var a = k; + console.log(a); + } + })(); + return typeof a; + }()); + } + expect_stdout: [ + "foo", + "undefined", + ] +} + +issue_5697_2: { + options = { + if_return: true, + inline: true, + reduce_vars: true, + unused: true, + varify: true, + } + input: { + "use strict"; + console.log(function() { + f(); + return typeof a; + function f() { + (function() { + for (var k in { foo: 42 }) { + let a = k; + console.log(a); + } + })(); + } + }()); + } + expect: { + "use strict"; + console.log(function() { + (function() { + for (var k in { foo: 42 }) { + var a = k; + console.log(a); + } + })(); + return typeof a; + }()); + } + expect_stdout: [ + "foo", + "undefined", + ] + node_version: ">=4" +} + +issue_5697_3: { + options = { + inline: true, + reduce_vars: true, + side_effects: true, + unused: true, + varify: true, + } + input: { + console.log(function() { + f(); + return typeof a; + function f() { + (function() { + for (var k in { foo: 42 }) { + const a = k; + console.log(a); + } + })(); + } + }()); + } + expect: { + console.log(function() { + (function() { + for (var k in { foo: 42 }) { + var a = k; + console.log(a); + } + })(); + return typeof a; + }()); + } + expect_stdout: [ + "foo", + "undefined", + ] +} + +issue_5697_4: { + options = { + inline: true, + reduce_vars: true, + side_effects: true, + unused: true, + varify: true, + } + input: { + "use strict"; + console.log(function() { + f(); + return typeof a; + function f() { + (function() { + for (var k in { foo: 42 }) { + let a = k; + console.log(a); + } + })(); + } + }()); + } + expect: { + "use strict"; + console.log(function() { + (function() { + for (var k in { foo: 42 }) { + var a = k; + console.log(a); + } + })(); + return typeof a; + }()); + } + expect_stdout: [ + "foo", + "undefined", + ] + node_version: ">=4" +} diff --git a/test/compress/webkit.js b/test/compress/webkit.js new file mode 100644 index 00000000000..49ada510745 --- /dev/null +++ b/test/compress/webkit.js @@ -0,0 +1,315 @@ +lambda_call_dot_assign: { + beautify = { + webkit: false, + } + input: { + console.log(function() { + return {}; + }().a = 1); + } + expect_exact: "console.log(function(){return{}}().a=1);" + expect_stdout: "1" +} + +lambda_call_dot_assign_webkit: { + beautify = { + webkit: true, + } + input: { + console.log(function() { + return {}; + }().a = 1); + } + expect_exact: "console.log((function(){return{}}()).a=1);" + expect_stdout: "1" +} + +lambda_dot_assign: { + beautify = { + webkit: false, + } + input: { + console.log(function() { + 1 + 1; + }.a = 1); + } + expect_exact: "console.log(function(){1+1}.a=1);" + expect_stdout: "1" +} + +lambda_dot_assign_webkit: { + beautify = { + webkit: true, + } + input: { + console.log(function() { + 1 + 1; + }.a = 1); + } + expect_exact: "console.log((function(){1+1}).a=1);" + expect_stdout: "1" +} + +lambda_name_mangle: { + mangle = {} + input: { + console.log(typeof function foo(bar) {}); + } + expect_exact: "console.log(typeof function o(n){});" + expect_stdout: "function" +} + +lambda_name_mangle_ie8: { + mangle = { + ie: true, + toplevel: true, + } + input: { + console.log(typeof function foo(bar) {}); + } + expect_exact: "console.log(typeof function n(o){});" + expect_stdout: "function" +} + +function_name_mangle: { + options = { + keep_fnames: true, + reduce_vars: true, + unused: true, + } + mangle = {} + input: { + (function() { + function foo(bar) {} + console.log(typeof foo); + })(); + } + expect_exact: "(function(){console.log(typeof function o(n){})})();" + expect_stdout: "function" +} + +function_name_mangle_ie8: { + options = { + keep_fnames: true, + reduce_vars: true, + unused: true, + } + mangle = { + ie: true, + toplevel: true, + } + input: { + (function() { + function foo(bar) {} + console.log(typeof foo); + })(); + } + expect_exact: "(function(){console.log(typeof function n(o){})})();" + expect_stdout: "function" +} + +issue_1753: { + mangle = { + toplevel: false, + webkit: true, + } + input: { + "use strict"; + let l = null; + for (let i = 0; i < 1; i++) + console.log(i); + } + expect: { + "use strict"; + let l = null; + for (let i = 0; i < 1; i++) + console.log(i); + } + expect_stdout: "0" + node_version: ">=4" +} + +issue_1753_toplevel: { + mangle = { + toplevel: true, + webkit: true, + } + input: { + "use strict"; + let l = null; + for (let i = 0; i < 1; i++) + console.log(i); + } + expect: { + "use strict"; + let l = null; + for (let e = 0; e < 1; e++) + console.log(e); + } + expect_stdout: "0" + node_version: ">=4" +} + +issue_5032_await: { + options = { + merge_vars: true, + webkit: false, + } + input: { + function log(value) { + console.log(value); + return value; + } + async function f(a) { + var b = log(a), c = b; + log(b); + log(c); + } + f("PASS"); + } + expect: { + function log(value) { + console.log(value); + return value; + } + async function f(a) { + var a = log(a), c = a; + log(a); + log(c); + } + f("PASS"); + } + expect_stdout: [ + "PASS", + "PASS", + "PASS", + ] + node_version: ">=8" +} + +issue_5032_await_webkit: { + options = { + merge_vars: true, + webkit: true, + } + input: { + function log(value) { + console.log(value); + return value; + } + async function f(a) { + var b = log(a), c = b; + log(b); + log(c); + } + f("PASS"); + } + expect: { + function log(value) { + console.log(value); + return value; + } + async function f(a) { + var b = log(a), c = b; + log(b); + log(c); + } + f("PASS"); + } + expect_stdout: [ + "PASS", + "PASS", + "PASS", + ] + node_version: ">=8" +} + +issue_5032_yield: { + options = { + merge_vars: true, + webkit: false, + } + input: { + function log(value) { + console.log(value); + return value; + } + function *f(a) { + var b = log(a), c = b; + log(b); + log(c); + } + f("PASS").next(); + } + expect: { + function log(value) { + console.log(value); + return value; + } + function *f(a) { + var a = log(a), c = a; + log(a); + log(c); + } + f("PASS").next(); + } + expect_stdout: [ + "PASS", + "PASS", + "PASS", + ] + node_version: ">=4" +} + +issue_5032_yield_webkit: { + options = { + merge_vars: true, + webkit: true, + } + input: { + function log(value) { + console.log(value); + return value; + } + function *f(a) { + var b = log(a), c = b; + log(b); + log(c); + } + f("PASS").next(); + } + expect: { + function log(value) { + console.log(value); + return value; + } + function *f(a) { + var b = log(a), c = b; + log(b); + log(c); + } + f("PASS").next(); + } + expect_stdout: [ + "PASS", + "PASS", + "PASS", + ] + node_version: ">=4" +} + +issue_5480: { + mangle = { + webkit: true, + } + input: { + "use strict"; + L: for (let a in console.log("PASS")); + } + expect: { + "use strict"; + o: for (let o in console.log("PASS")); + } + expect_stdout: "PASS" + node_version: ">=4" +} diff --git a/test/compress/wrap_iife.js b/test/compress/wrap_iife.js new file mode 100644 index 00000000000..45417a42a90 --- /dev/null +++ b/test/compress/wrap_iife.js @@ -0,0 +1,48 @@ +wrap_iife: { + options = { + negate_iife: false, + } + beautify = { + wrap_iife: true, + } + input: { + (function() { + return function() { + console.log('test') + }; + })()(); + } + expect_exact: '(function(){return function(){console.log("test")}})()();' +} + +wrap_iife_in_expression: { + options = { + negate_iife: false, + } + beautify = { + wrap_iife: true, + } + input: { + foo = (function() { + return bar(); + })(); + } + expect_exact: 'foo=(function(){return bar()})();' +} + +wrap_iife_in_return_call: { + options = { + negate_iife: false, + } + beautify = { + wrap_iife: true, + } + input: { + (function() { + return (function() { + console.log('test') + })(); + })()(); + } + expect_exact: '(function(){return(function(){console.log("test")})()})()();' +} diff --git a/test/compress/yields.js b/test/compress/yields.js new file mode 100644 index 00000000000..0736adaa0d2 --- /dev/null +++ b/test/compress/yields.js @@ -0,0 +1,2209 @@ +binary: { + input: { + var a = function*() { + console.log(6 * (yield "PA" + "SS")); + }(); + console.log(a.next("FAIL").value); + console.log(a.next(7).done); + } + expect_exact: 'var a=function*(){console.log(6*(yield"PA"+"SS"))}();console.log(a.next("FAIL").value);console.log(a.next(7).done);' + expect_stdout: [ + "PASS", + "42", + "true", + ] + node_version: ">=4" +} + +empty_yield: { + input: { + var a = function*() { + yield; + console.log(yield); + yield + "FAIL 1"; + }(); + console.log(a.next("FAIL 2").value); + console.log(a.next("FAIL 3").value); + console.log(a.next("PASS").value); + console.log(a.next("FAIL 4").done); + } + expect_exact: 'var a=function*(){yield;console.log(yield);yield;"FAIL 1"}();console.log(a.next("FAIL 2").value);console.log(a.next("FAIL 3").value);console.log(a.next("PASS").value);console.log(a.next("FAIL 4").done);' + expect_stdout: [ + "undefined", + "undefined", + "PASS", + "undefined", + "true", + ] + node_version: ">=4" +} + +empty_yield_conditional: { + input: { + var a = function*() { + console.log((yield) ? yield : yield); + }(); + console.log(a.next("FAIL 1").value); + console.log(a.next("FAIL 2").value); + console.log(a.next("PASS").value); + console.log(a.next("FAIL 3").done); + } + expect_exact: 'var a=function*(){console.log((yield)?yield:yield)}();console.log(a.next("FAIL 1").value);console.log(a.next("FAIL 2").value);console.log(a.next("PASS").value);console.log(a.next("FAIL 3").done);' + expect_stdout: [ + "undefined", + "undefined", + "PASS", + "undefined", + "true", + ] + node_version: ">=4" +} + +nested_yield: { + input: { + console.log(function*() { + (yield* + f()) + function* f() { + return "FAIL"; + } + yield* + f(); + yield *f(); + }().next().value || "PASS"); + } + expect_exact: 'console.log(function*(){yield*f();function*f(){return"FAIL"}yield*f();yield*f()}().next().value||"PASS");' + expect_stdout: "PASS" + node_version: ">=4" +} + +pause_resume: { + input: { + function* f() { + console.log(yield "PASS"); + } + var a = f(); + console.log(a.next("FAIL").value); + console.log(a.next(42).done); + } + expect_exact: 'function*f(){console.log(yield"PASS")}var a=f();console.log(a.next("FAIL").value);console.log(a.next(42).done);' + expect_stdout: [ + "PASS", + "42", + "true", + ] + node_version: ">=4" +} + +arrow_yield_1: { + input: { + yield = "PASS"; + console.log(function*() { + return () => yield || "FAIL"; + }().next().value()); + } + expect_exact: 'yield="PASS";console.log(function*(){return()=>yield||"FAIL"}().next().value());' + expect_stdout: "PASS" + node_version: ">=4" +} + +arrow_yield_2: { + input: { + console.log(typeof function *() { + // Syntax error on Node.js v6+ + return (yield) => {}; + }().next().value); + } + expect_exact: "console.log(typeof function*(){return(yield)=>{}}().next().value);" + expect_stdout: "function" + node_version: "4" +} + +for_of: { + input: { + function* f() { + if (yield "PASS") yield "FAIL 1"; + yield 42; + return "FAIL 2"; + } + for (var a of f()) + console.log(a); + } + expect_exact: 'function*f(){if(yield"PASS")yield"FAIL 1";yield 42;return"FAIL 2"}for(var a of f())console.log(a);' + expect_stdout: [ + "PASS", + "42", + ] + node_version: ">=4" +} + +for_await_of: { + input: { + async function* f() { + if (yield "PASS") yield "FAIL 1"; + yield { + then: function(r) { + r(42); + }, + }; + return "FAIL 2"; + } + (async function(a) { + for await (a of f()) + console.log(a); + })(); + } + expect_exact: 'async function*f(){if(yield"PASS")yield"FAIL 1";yield{then:function(r){r(42)}};return"FAIL 2"}(async function(a){for await(a of f())console.log(a)})();' + expect_stdout: [ + "PASS", + "42", + ] + node_version: ">=10" +} + +comment_newline: { + beautify = { + comments: "all", + } + input: { + console.log(function*() { + yield ( + /* */ + "PASS" + ); + }().next().value); + } + expect_exact: [ + "console.log(function*(){", + "/* */", + 'yield"PASS"}().next().value);', + ] + expect_stdout: "PASS" + node_version: ">=4" +} + +collapse_vars_1: { + options = { + collapse_vars: true, + } + input: { + var a = "FAIL"; + (function*() { + a = "PASS"; + yield 42; + return "PASS"; + })().next(); + console.log(a); + } + expect: { + var a = "FAIL"; + (function*() { + a = "PASS"; + yield 42; + return "PASS"; + })().next(); + console.log(a); + } + expect_stdout: "PASS" + node_version: ">=4" +} + +collapse_vars_2: { + options = { + collapse_vars: true, + } + input: { + var a = "FAIL"; + (function*() { + yield (a = "PASS"); + return "PASS"; + })().next(); + console.log(a); + } + expect: { + var a = "FAIL"; + (function*() { + yield (a = "PASS"); + return "PASS"; + })().next(); + console.log(a); + } + expect_stdout: "PASS" + node_version: ">=4" +} + +collapse_vars_3: { + options = { + collapse_vars: true, + } + input: { + var a = "FAIL"; + (function*() { + yield (a = "PASS", 42); + return "PASS"; + })().next(); + console.log(a); + } + expect: { + var a = "FAIL"; + (function*() { + yield (a = "PASS", 42); + return "PASS"; + })().next(); + console.log(a); + } + expect_stdout: "PASS" + node_version: ">=4" +} + +collapse_vars_4: { + options = { + collapse_vars: true, + unused: true, + } + input: { + var a = "FAIL"; + var b = function*(c) { + return c; + }(a = "PASS"); + console.log(a, b.next().done); + } + expect: { + var a = "FAIL"; + var b = function*(c) { + return c; + }(a = "PASS"); + console.log(a, b.next().done); + } + expect_stdout: "PASS true" + node_version: ">=4" +} + +collapse_vars_5: { + options = { + collapse_vars: true, + } + input: { + var a = function* f(b, c) { + b = yield c = b; + console.log(c); + }("PASS"); + a.next(); + a.next("FAIL"); + } + expect: { + var a = function* f(b, c) { + b = yield c = b; + console.log(c); + }("PASS"); + a.next(); + a.next("FAIL"); + } + expect_stdout: "PASS" + node_version: ">=4" +} + +collapse_property_lambda: { + options = { + collapse_vars: true, + pure_getters: "strict", + } + input: { + console.log(function* f() { + f.g = () => 42; + return f.g(); + }().next().value); + } + expect: { + console.log(function* f() { + return (f.g = () => 42)(); + }().next().value); + } + expect_stdout: "42" + node_version: ">=4" +} + +defun_name: { + input: { + function* yield() { + console.log("PASS"); + } + yield().next(); + } + expect: { + function* yield() { + console.log("PASS"); + } + yield().next(); + } + expect_stdout: "PASS" + node_version: ">=4" +} + +drop_fname: { + rename = true + options = { + reduce_vars: true, + toplevel: true, + unused: true, + } + mangle = { + toplevel: true, + } + input: { + function* yield() { + console.log("PASS"); + } + yield().next(); + } + expect: { + (function*() { + console.log("PASS"); + })().next(); + } + expect_stdout: "PASS" + node_version: ">=4" +} + +keep_fname: { + options = { + keep_fnames: true, + reduce_vars: true, + toplevel: true, + unused: true, + } + input: { + function* yield() { + console.log("PASS"); + } + yield().next(); + } + expect: { + function* yield() { + console.log("PASS"); + } + yield().next(); + } + expect_stdout: "PASS" + node_version: ">=4" +} + +evaluate: { + options = { + evaluate: true, + reduce_vars: true, + toplevel: true, + unused: true, + } + input: { + var a = function*() {}(); + console.log(typeof a); + } + expect: { + var a = function*() {}(); + console.log(typeof a); + } + expect_stdout: "object" + node_version: ">=4" +} + +functions: { + options = { + functions: true, + reduce_vars: true, + unused: true, + } + input: { + !function*() { + var a = function* a() { + return a && "a"; + }; + var b = function* x() { + return !!x; + }; + var c = function*(c) { + return c; + }; + if (yield* c(yield* b(yield* a()))) { + var d = function*() {}; + var e = function* y() { + return typeof y; + }; + var f = function*(f) { + return f; + }; + console.log(yield* a(yield* d()), yield* b(yield* e()), yield* c(yield* f(42)), typeof d, yield* e(), typeof f); + } + }().next(); + } + expect: { + !function*() { + function* a() { + return a && "a"; + } + function* b() { + return !!b; + } + function* c(c) { + return c; + } + if (yield* c(yield* b(yield* a()))) { + var d = function*() {}; + var e = function* y() { + return typeof y; + }; + var f = function*(f) { + return f; + }; + console.log(yield* a(yield* d()), yield* b(yield* e()), yield* c(yield* f(42)), typeof d, yield* e(), typeof f); + } + }().next(); + } + expect_stdout: "a true 42 function function function" + node_version: ">=4" +} + +functions_use_strict: { + options = { + functions: true, + reduce_vars: true, + unused: true, + } + input: { + "use strict"; + !function*() { + var a = function* a() { + return a && "a"; + }; + var b = function* x() { + return !!x; + }; + var c = function*(c) { + return c; + }; + if (yield* c(yield* b(yield* a()))) { + var d = function*() {}; + var e = function* y() { + return typeof y; + }; + var f = function*(f) { + return f; + }; + console.log(yield* a(yield* d()), yield* b(yield* e()), yield* c(yield* f(42)), typeof d, yield* e(), typeof f); + } + }().next(); + } + expect: { + "use strict"; + !function*() { + function* a() { + return a && "a"; + } + function* b() { + return !!b; + } + function* c(c) { + return c; + } + if (yield* c(yield* b(yield* a()))) { + var d = function*() {}; + var e = function* y() { + return typeof y; + }; + var f = function*(f) { + return f; + }; + console.log(yield* a(yield* d()), yield* b(yield* e()), yield* c(yield* f(42)), typeof d, yield* e(), typeof f); + } + }().next(); + } + expect_stdout: "a true 42 function function function" + node_version: ">=4" +} + +functions_anonymous: { + options = { + functions: true, + reduce_vars: true, + toplevel: true, + unused: true, + } + input: { + var yield = function*() { + return "PASS"; + }; + console.log(yield().next(yield).value); + } + expect: { + function* yield() { + return "PASS"; + } + console.log(yield().next(yield).value); + } + expect_stdout: "PASS" + node_version: ">=4" +} + +functions_inner_var: { + options = { + functions: true, + reduce_vars: true, + toplevel: true, + unused: true, + } + input: { + var yield = function* a() { + var a; + console.log(a, a); + }; + yield().next(yield); + } + expect: { + function* yield() { + var a; + console.log(a, a); + } + yield().next(yield); + } + expect_stdout: "undefined undefined" + node_version: ">=4" +} + +negate_iife: { + options = { + negate_iife: true, + side_effects: true, + } + input: { + (function*(a) { + console.log(a); + })("PASS").next(); + } + expect: { + !function*(a) { + console.log(a); + }("PASS").next(); + } + expect_stdout: "PASS" + node_version: ">=4" +} + +reduce_iife_1: { + options = { + reduce_vars: true, + unused: true, + } + input: { + console.log(function*(a) { + yield a; + }(42).next().value); + } + expect: { + console.log(function*(a) { + yield 42; + }().next().value); + } + expect_stdout: "42" + node_version: ">=4" +} + +reduce_iife_2: { + options = { + evaluate: true, + reduce_vars: true, + toplevel: true, + unused: true, + } + input: { + var a = "PASS"; + (function*() { + a = "FAIL"; + })(); + console.log(a); + } + expect: { + var a = "PASS"; + (function*() { + a = "FAIL"; + })(); + console.log(a); + } + expect_stdout: "PASS" + node_version: ">=4" +} + +reduce_single_use_defun: { + options = { + reduce_vars: true, + toplevel: true, + unused: true, + } + input: { + function* f(a) { + console.log(a); + } + f("PASS").next(); + } + expect: { + (function*(a) { + console.log(a); + })("PASS").next(); + } + expect_stdout: "PASS" + node_version: ">=4" +} + +reduce_tagged: { + options = { + reduce_funcs: true, + reduce_vars: true, + unused: true, + } + input: { + function* f() { + function g() { + h`foo`; + } + g(); + function h(s) { + console.log(s[0]); + } + h([ "bar" ]); + } + f().next(); + } + expect: { + function* f() { + (function() { + h`foo`; + })(); + function h(s) { + console.log(s[0]); + } + h([ "bar" ]); + } + f().next(); + } + expect_stdout: [ + "foo", + "bar", + ] + node_version: ">=4" +} + +reduce_tagged_async: { + options = { + reduce_funcs: true, + reduce_vars: true, + unused: true, + } + input: { + async function* f() { + function g() { + h`foo`; + } + g(); + function h(s) { + console.log(s[0]); + } + h([ "bar" ]); + } + f().next(); + } + expect: { + async function* f() { + (function() { + h`foo`; + })(); + function h(s) { + console.log(s[0]); + } + h([ "bar" ]); + } + f().next(); + } + expect_stdout: [ + "foo", + "bar", + ] + node_version: ">=10" +} + +lift_sequence: { + options = { + sequences: true, + yields: true, + } + input: { + console.log(function*() { + yield (console, "PASS"); + }().next().value); + } + expect: { + console.log(function*() { + console, yield "PASS"; + }().next().value); + } + expect_stdout: "PASS" + node_version: ">=4" +} + +inline_nested: { + options = { + inline: true, + sequences: true, + yields: true, + } + input: { + var a = function*() { + yield* function*() { + yield "foo"; + return "FAIL"; + }(); + }(), b; + do { + b = a.next(); + console.log(b.value); + } while (!b.done); + } + expect: { + var a = function*() { + yield "foo", + "FAIL"; + }(), b; + do { + b = a.next(), + console.log(b.value); + } while (!b.done); + } + expect_stdout: [ + "foo", + "undefined", + ] + node_version: ">=4" +} + +inline_nested_async: { + options = { + awaits: true, + inline: true, + sequences: true, + side_effects: true, + yields: true, + } + input: { + console.log("foo"); + var a = async function*() { + console.log(await(yield* async function*() { + yield { + then: r => r("bar"), + }; + return "baz"; + }())); + }(); + console.log("moo"); + a.next().then(function f(b) { + console.log(b.value); + b.done || a.next().then(f); + }); + console.log("moz"); + } + expect: { + console.log("foo"); + var a = async function*() { + console.log((yield { + then: r => r("bar"), + }, await "baz")); + }(); + console.log("moo"), + a.next().then(function f(b) { + console.log(b.value), + b.done || a.next().then(f); + }), + console.log("moz"); + } + expect_stdout: [ + "foo", + "moo", + "moz", + "bar", + "baz", + "undefined", + ] + node_version: ">=10" +} + +inline_nested_block: { + options = { + if_return: true, + inline: true, + yields: true, + } + input: { + var a = function*() { + yield* function*() { + for (var a of [ "foo", "bar" ]) + yield a; + return "FAIL"; + }(); + }(), b; + do { + b = a.next(); + console.log(b.value); + } while (!b.done); + } + expect: { + var a = function*() { + for (var a of [ "foo", "bar" ]) + yield a; + "FAIL"; + }(), b; + do { + b = a.next(); + console.log(b.value); + } while (!b.done); + } + expect_stdout: [ + "foo", + "bar", + "undefined", + ] + node_version: ">=4" +} + +dont_inline_nested: { + options = { + inline: true, + } + input: { + var yield = "PASS"; + (function*() { + (function() { + console.log(yield); + })(); + })().next(); + } + expect: { + var yield = "PASS"; + (function*() { + (function() { + console.log(yield); + })(); + })().next(); + } + expect_stdout: "PASS" + node_version: ">=4" +} + +drop_body_1: { + options = { + side_effects: true, + yields: true, + } + input: { + (function*([ , a = console.log("foo") ]) { + console.log("bar"); + })([ console.log("baz") ]); + } + expect: { + void ([ [ , [][0] = console.log("foo") ] ] = [ [ console.log("baz") ] ]); + } + expect_stdout: [ + "baz", + "foo", + ] + node_version: ">=6" +} + +drop_body_2: { + options = { + passes: 2, + side_effects: true, + yields: true, + } + input: { + (function*([ , a = console.log("foo") ]) { + console.log("bar"); + })([ console.log("baz") ]); + } + expect: { + [ [ , [][0] = console.log("foo") ] ] = [ [ console.log("baz") ] ]; + } + expect_stdout: [ + "baz", + "foo", + ] + node_version: ">=6" +} + +drop_unused_call: { + options = { + inline: true, + side_effects: true, + toplevel: true, + unused: true, + yields: true, + } + input: { + var a = function*(){}(console.log("PASS")); + } + expect: { + console.log("PASS"); + } + expect_stdout: "PASS" + node_version: ">=4" +} + +instanceof_lambda: { + options = { + evaluate: true, + side_effects: true, + } + input: { + console.log(42 instanceof function*() {}); + } + expect: { + console.log(false); + } + expect_stdout: "false" + node_version: ">=4" +} + +issue_4454_1: { + rename = false + options = { + merge_vars: true, + } + input: { + function f(a) { + (function*(b = console.log(a)) {})(); + var yield = 42..toString(); + console.log(yield); + } + f("PASS"); + } + expect: { + function f(a) { + (function*(b = console.log(a)) {})(); + var yield = 42..toString(); + console.log(yield); + } + f("PASS"); + } + expect_stdout: [ + "PASS", + "42", + ] + node_version: ">=6" +} + +issue_4454_2: { + rename = true + options = { + merge_vars: true, + } + input: { + function f(a) { + (function*(b = console.log(a)) {})(); + var yield = 42..toString(); + console.log(yield); + } + f("PASS"); + } + expect: { + function f(a) { + (function*(c = console.log(a)) {})(); + var b = 42..toString(); + console.log(b); + } + f("PASS"); + } + expect_stdout: [ + "PASS", + "42", + ] + node_version: ">=6" +} + +issue_4618: { + options = { + functions: true, + reduce_vars: true, + unused: true, + } + input: { + console.log(typeof function() { + var yield = function* f() { + console || f(); + }; + console.log; + return yield; + }()); + } + expect: { + console.log(typeof function() { + var yield = function* f() { + console || f(); + }; + console.log; + return yield; + }()); + } + expect_stdout: "function" + node_version: ">=4" +} + +issue_4623: { + options = { + conditionals: true, + } + input: { + if (console ? function*() {} : 0) + console.log("PASS"); + } + expect: { + (console ? function*() {} : 0) && console.log("PASS"); + } + expect_stdout: "PASS" + node_version: ">=4" +} + +issue_4633: { + options = { + collapse_vars: true, + unused: true, + } + input: { + var a = function*() { + (function(log) { + log(typeof this); + })(yield "PASS"); + }(); + console.log(a.next().value); + a.next(console.log); + } + expect: { + var a = function*() { + (function(log) { + log(typeof this); + })(yield "PASS"); + }(); + console.log(a.next().value); + a.next(console.log); + } + expect_stdout: [ + "PASS", + "object", + ] + node_version: ">=4" +} + +issue_4639_1: { + options = { + inline: true, + } + input: { + console.log(function*() { + return function() { + return yield => "PASS"; + }(); + }().next().value()); + } + expect: { + console.log(function*() { + return function() { + return yield => "PASS"; + }(); + }().next().value()); + } + expect_stdout: "PASS" + node_version: ">=4 <7 || >=8.7.0" +} + +issue_4639_2: { + options = { + inline: true, + } + input: { + (function*() { + console.log(function() { + return typeof yield; + }()); + })().next(); + } + expect: { + (function*() { + console.log(function() { + return typeof yield; + }()); + })().next(); + } + expect_stdout: "undefined" + node_version: ">=4" +} + +issue_4641_1: { + options = { + sequences: true, + } + input: { + console.log(typeof async function*() { + try { + console.log("foo"); + return; + } finally { + console.log("bar"); + } + }().next().then); + } + expect: { + console.log(typeof async function*() { + try { + console.log("foo"); + return; + } finally { + console.log("bar"); + } + }().next().then); + } + expect_stdout: [ + "foo", + "bar", + "function", + ] + node_version: ">=10" +} + +issue_4641_2: { + options = { + side_effects: true, + } + input: { + console.log(typeof async function*() { + try { + return void "FAIL"; + } finally { + console.log("PASS"); + } + }().next().then); + } + expect: { + console.log(typeof async function*() { + try { + return void 0; + } finally { + console.log("PASS"); + } + }().next().then); + } + expect_stdout: [ + "function", + "PASS", + ] + node_version: ">=10" +} + +issue_4641_3: { + options = { + if_return: true, + } + input: { + console.log(typeof async function*() { + try { + return void "FAIL"; + } finally { + console.log("PASS"); + } + }().next().then); + } + expect: { + console.log(typeof async function*() { + try { + return void "FAIL"; + } finally { + console.log("PASS"); + } + }().next().then); + } + expect_stdout: [ + "function", + "PASS", + ] + node_version: ">=10" +} + +issue_4769_1: { + options = { + side_effects: true, + } + input: { + console.log(function*() { + (function({} = yield => {}) {})(); + }().next().done); + } + expect: { + console.log(function*() { + (function({} = yield => {}) {})(); + }().next().done); + } + expect_stdout: "true" + node_version: ">=6" +} + +issue_4769_2: { + options = { + inline: true, + } + input: { + console.log(function*() { + return function({} = yield => {}) { + return "PASS"; + }(); + }().next().value); + } + expect: { + console.log(function*() { + return function({} = yield => {}) { + return "PASS"; + }(); + }().next().value); + } + expect_stdout: "PASS" + node_version: ">=6" +} + +issue_5019_1: { + options = { + dead_code: true, + } + input: { + (function(a) { + return a = function*() { + console.log(typeof a); + }(); + })().next(); + } + expect: { + (function(a) { + return a = function*() { + console.log(typeof a); + }(); + })().next(); + } + expect_stdout: "object" + node_version: ">=4" +} + +issue_5019_2: { + options = { + inline: true, + toplevel: true, + } + input: { + var a = []; + for (var b in "foo") + a.push(function(c) { + return function*() { + console.log(c); + }(); + }(b)); + a.map(function(d) { + return d.next(); + }); + } + expect: { + var a = []; + for (var b in "foo") + a.push(function(c) { + return function*() { + console.log(c); + }(); + }(b)); + a.map(function(d) { + return d.next(); + }); + } + expect_stdout: [ + "0", + "1", + "2", + ] + node_version: ">=4" +} + +issue_5034: { + options = { + functions: true, + reduce_vars: true, + unused: true, + } + input: { + console.log(function() { + var yield = function f() { + return function*() { + return f; + }; + }; + return yield()().next().value === yield; + }() ? "PASS" : "FAIL"); + } + expect: { + console.log(function() { + var yield = function f() { + return function*() { + return f; + }; + }; + return yield()().next().value === yield; + }() ? "PASS" : "FAIL"); + } + expect_stdout: "PASS" + node_version: ">=4" +} + +issue_5076_1: { + options = { + evaluate: true, + hoist_vars: true, + keep_fargs: false, + pure_getters: "strict", + sequences: true, + side_effects: true, + toplevel: true, + unused: true, + yields: true, + } + input: { + var a; + console.log("PASS"); + var b = function*({ + p: {}, + }) {}({ + p: { a } = 42, + }); + } + expect: { + var a; + console.log("PASS"), + a = 42["a"]; + } + expect_stdout: "PASS" + node_version: ">=6" +} + +issue_5076_2: { + options = { + evaluate: true, + hoist_vars: true, + keep_fargs: false, + passes: 2, + pure_getters: "strict", + sequences: true, + side_effects: true, + toplevel: true, + unused: true, + yields: true, + } + input: { + var a; + console.log("PASS"); + var b = function*({ + p: {}, + }) {}({ + p: { a } = 42, + }); + } + expect: { + console.log("PASS"); + } + expect_stdout: "PASS" + node_version: ">=6" +} + +issue_5177: { + options = { + properties: true, + } + input: { + console.log(typeof function*() { + return { + p(yield) {}, + }.p; + }().next().value); + } + expect: { + console.log(typeof function*() { + return { + p(yield) {}, + }.p; + }().next().value); + } + expect_stdout: "function" + node_version: ">=4" +} + +issue_5385_1: { + options = { + inline: true, + } + input: { + (async function*() { + (function() { + try { + return console.log("foo"); + } finally { + return console.log("bar"); + } + console.log("baz"); + })(); + })().next(); + console.log("moo"); + } + expect: { + (async function*() { + (function() { + try { + return console.log("foo"); + } finally { + return console.log("bar"); + } + console.log("baz"); + })(); + })().next(); + console.log("moo"); + } + expect_stdout: [ + "foo", + "bar", + "moo", + ] + node_version: ">=10" +} + +issue_5385_2: { + options = { + inline: true, + } + input: { + (async function*() { + return function() { + try { + return console.log("foo"); + } finally { + return console.log("bar"); + } + }(); + })().next(); + console.log("moo"); + } + expect: { + (async function*() { + return function() { + try { + return console.log("foo"); + } finally { + return console.log("bar"); + } + }(); + })().next(); + console.log("moo"); + } + expect_stdout: [ + "foo", + "bar", + "moo", + ] + node_version: ">=10" +} + +issue_5385_3: { + options = { + inline: true, + } + input: { + (async function*() { + return function() { + try { + throw console.log("foo"); + } catch (e) { + return console.log("bar"); + } + }(); + })().next(); + console.log("moo"); + } + expect: { + (async function*() { + try { + throw console.log("foo"); + } catch (e) { + return console.log("bar"); + } + return void 0; + })().next(); + console.log("moo"); + } + expect_stdout: [ + "foo", + "bar", + "moo", + ] + node_version: ">=10" +} + +issue_5385_4: { + options = { + awaits: true, + inline: true, + } + input: { + (async function*() { + return async function() { + try { + return { + then(resolve) { + resolve(console.log("FAIL")); + }, + }; + } finally { + return "PASS"; + } + }(); + })().next().then(o => console.log(o.value, o.done)); + } + expect: { + (async function*() { + return async function() { + try { + return { + then(resolve) { + resolve(console.log("FAIL")); + }, + }; + } finally { + return "PASS"; + } + }(); + })().next().then(o => console.log(o.value, o.done)); + } + expect_stdout: "PASS true" + node_version: ">=10" +} + +issue_5425: { + options = { + assignments: true, + ie: true, + toplevel: true, + unused: true, + yields: true, + } + input: { + var a = "FAIL"; + var b = function* f() {}(a ? a = "PASS" : 42); + console.log(a, typeof f); + } + expect: { + var a = "FAIL"; + (function* f() {})(a && (a = "PASS")); + console.log(a, typeof f); + } + expect_stdout: "PASS undefined" + node_version: ">=4" +} + +issue_5456: { + options = { + inline: true, + merge_vars: true, + } + input: { + var a = true; + (function() { + (function(b, c) { + var d = function*() { + c = null; + }(); + var e = function() { + if (c) + console.log(typeof d); + while (b); + }(); + })(function(i) { + return console.log("foo") && i; + }(a)); + })(); + } + expect: { + var a = true; + (function() { + b = (i = a, console.log("foo") && i), + d = function*() { + c = null; + }(), + e = function() { + if (c) console.log(typeof d); + while (b); + }(), + void 0; + var b, c, d, e; + var i; + })(); + } + expect_stdout: "foo" + node_version: ">=4" +} + +issue_5506: { + options = { + dead_code: true, + } + input: { + console.log(function(a) { + var b = function*() { + a = null in (a = "PASS"); + }(); + try { + b.next(); + } catch (e) { + return a; + } + }("FAIL")); + } + expect: { + console.log(function(a) { + var b = function*() { + a = null in (a = "PASS"); + }(); + try { + b.next(); + } catch (e) { + return a; + } + }("FAIL")); + } + expect_stdout: "PASS" + node_version: ">=4" +} + +issue_5526: { + options = { + inline: true, + side_effects: true, + } + input: { + (async function*() { + try { + return function() { + while (console.log("foo")); + }(); + } finally { + console.log("bar"); + } + })().next(); + console.log("baz"); + } + expect: { + (async function*() { + try { + while (console.log("foo")); + return void 0; + } finally { + console.log("bar"); + } + })().next(); + console.log("baz"); + } + expect_stdout: [ + "foo", + "baz", + "bar", + ] + node_version: ">=10" +} + +issue_5576: { + options = { + inline: true, + } + input: { + (async function*() { + try { + (function() { + while (console.log("foo")); + })(); + } finally { + console.log("bar"); + } + })().next(); + console.log("baz"); + } + expect: { + (async function*() { + try { + while (console.log("foo")); + } finally { + console.log("bar"); + } + })().next(); + console.log("baz"); + } + expect_stdout: [ + "foo", + "bar", + "baz", + ] + node_version: ">=10" +} + +issue_5663: { + options = { + toplevel: true, + unused: true, + } + input: { + var [ , a ] = function*() { + console.log("foo"); + yield console.log("bar"); + console.log("baz"); + yield console.log("moo"); + console.log("moz"); + yield FAIL; + }(); + } + expect: { + var [ , , ] = function*() { + console.log("foo"); + yield console.log("bar"); + console.log("baz"); + yield console.log("moo"); + console.log("moz"); + yield FAIL; + }(); + } + expect_stdout: [ + "foo", + "bar", + "baz", + "moo", + ] + node_version: ">=6" +} + +issue_5679_1: { + options = { + conditionals: true, + } + input: { + var a = "FAIL"; + async function* f(b) { + try { + if (b) + return; + else + return; + } finally { + a = "PASS"; + } + } + f().next(); + console.log(a); + } + expect: { + var a = "FAIL"; + async function* f(b) { + try { + b; + return; + } finally { + a = "PASS"; + } + } + f().next(); + console.log(a); + } + expect_stdout: "PASS" + node_version: ">=10" +} + +issue_5679_2: { + options = { + conditionals: true, + } + input: { + var a = "FAIL"; + async function* f(b) { + try { + if (b) + return undefined; + else + return; + } finally { + a = "PASS"; + } + } + f().next(); + console.log(a); + } + expect: { + var a = "FAIL"; + async function* f(b) { + try { + if (b) + return void 0; + return; + } finally { + a = "PASS"; + } + } + f().next(); + console.log(a); + } + expect_stdout: "PASS" + node_version: ">=10" +} + +issue_5679_3: { + options = { + conditionals: true, + } + input: { + var a = "FAIL"; + async function* f(b) { + try { + if (b) + return; + else + return undefined; + } finally { + a = "PASS"; + } + } + f(42).next(); + console.log(a); + } + expect: { + var a = "FAIL"; + async function* f(b) { + try { + if (b) + return; + return void 0; + } finally { + a = "PASS"; + } + } + f(42).next(); + console.log(a); + } + expect_stdout: "PASS" + node_version: ">=10" +} + +issue_5679_4: { + options = { + conditionals: true, + } + input: { + var a = "PASS"; + async function* f(b) { + try { + if (b) + return undefined; + else + return undefined; + } finally { + a = "FAIL"; + } + } + f(null).next(); + console.log(a); + } + expect: { + var a = "PASS"; + async function* f(b) { + try { + return b, void 0; + } finally { + a = "FAIL"; + } + } + f(null).next(); + console.log(a); + } + expect_stdout: "PASS" + node_version: ">=10" +} + +issue_5679_5: { + options = { + conditionals: true, + if_return: true, + } + input: { + var a = "FAIL"; + async function* f(b) { + try { + if (b) + return console; + else + return; + } finally { + a = "PASS"; + } + } + f().next(); + console.log(a); + } + expect: { + var a = "FAIL"; + async function* f(b) { + try { + if (b) + return console; + return; + } finally { + a = "PASS"; + } + } + f().next(); + console.log(a); + } + expect_stdout: "PASS" + node_version: ">=10" +} + +issue_5679_6: { + options = { + conditionals: true, + if_return: true, + } + input: { + var a = "PASS"; + async function* f(b) { + try { + if (b) + return; + else + return console; + } finally { + a = "FAIL"; + } + } + f().next(); + console.log(a); + } + expect: { + var a = "PASS"; + async function* f(b) { + try { + if (!b) + return console; + } finally { + a = "FAIL"; + } + } + f().next(); + console.log(a); + } + expect_stdout: "PASS" + node_version: ">=10" +} + +issue_5684: { + options = { + conditionals: true, + if_return: true, + } + input: { + (async function*() { + switch (42) { + default: + if (console.log("PASS")) + return; + return null; + case false: + } + })().next(); + } + expect: { + (async function*() { + switch (42) { + default: + return console.log("PASS") ? void 0 : null; + case false: + } + })().next(); + } + expect_stdout: "PASS" + node_version: ">=10" +} + +issue_5707_1: { + options = { + hoist_props: true, + reduce_vars: true, + side_effects: true, + toplevel: true, + unused: true, + yields: true, + } + input: { + var a, b; + function* f(c = (b = 42, console.log("PASS"))) {} + b = f(); + } + expect: { + (function(c = console.log("PASS")) {})(); + } + expect_stdout: "PASS" + node_version: ">=6" +} + +issue_5707_2: { + options = { + hoist_props: true, + passes: 2, + reduce_vars: true, + side_effects: true, + toplevel: true, + unused: true, + yields: true, + } + input: { + var a, b; + function* f(c = (b = 42, console.log("PASS"))) {} + b = f(); + } + expect: { + console.log("PASS"); + } + expect_stdout: "PASS" + node_version: ">=6" +} + +issue_5710: { + options = { + conditionals: true, + if_return: true, + } + input: { + (async function*() { + try { + switch (42) { + case 42: + { + if (console.log("PASS")) + return; + return null; + } + break; + } + } finally {} + })().next(); + } + expect: { + (async function*() { + try { + switch (42) { + case 42: + if (console.log("PASS")) + return; + return null; + break; + } + } finally {} + })().next(); + } + expect_stdout: "PASS" + node_version: ">=10" +} + +issue_5749_1: { + options = { + collapse_vars: true, + reduce_vars: true, + side_effects: true, + toplevel: true, + unused: true, + yields: true, + } + input: { + var a; + function* f() {} + a = f(new function() { + var b = a |= 0, c = a += console.log("PASS"); + }()); + } + expect: { + (function() {})(function() { + console.log("PASS"); + }()); + } + expect_stdout: "PASS" + node_version: ">=4" +} + +issue_5749_2: { + options = { + collapse_vars: true, + inline: true, + reduce_vars: true, + sequences: true, + side_effects: true, + toplevel: true, + unused: true, + yields: true, + } + input: { + var a; + function* f() {} + a = f(new function() { + var b = a |= 0, c = a += console.log("PASS"); + }()); + } + expect: { + console.log("PASS"); + } + expect_stdout: "PASS" + node_version: ">=4" +} + +issue_5754: { + options = { + if_return: true, + } + input: { + async function* f(a, b) { + try { + if (a) + return void 0; + } finally { + console.log(b); + } + } + f(42, "foo").next(); + f(null, "bar").next(); + console.log("baz"); + } + expect: { + async function* f(a, b) { + try { + if (a) + return void 0; + } finally { + console.log(b); + } + } + f(42, "foo").next(); + f(null, "bar").next(); + console.log("baz"); + } + expect_stdout: [ + "bar", + "baz", + "foo", + ] + node_version: ">=10" +} diff --git a/test/exports.js b/test/exports.js new file mode 100644 index 00000000000..c5bcb1111c4 --- /dev/null +++ b/test/exports.js @@ -0,0 +1,18 @@ +exports["Compressor"] = Compressor; +exports["defaults"] = defaults; +exports["is_statement"] = is_statement; +exports["JS_Parse_Error"] = JS_Parse_Error; +exports["List"] = List; +exports["mangle_properties"] = mangle_properties; +exports["minify"] = minify; +exports["OutputStream"] = OutputStream; +exports["parse"] = parse; +exports["push_uniq"] = push_uniq; +exports["reserve_quoted_keys"] = reserve_quoted_keys; +exports["string_template"] = string_template; +exports["to_ascii"] = to_ascii; +exports["tokenizer"] = tokenizer; +exports["TreeTransformer"] = TreeTransformer; +exports["TreeWalker"] = TreeWalker; +exports["vlq_decode"] = vlq_decode; +exports["extractFeatures"] = extractFeatures; diff --git a/test/feature_extraction.js b/test/feature_extraction.js new file mode 100644 index 00000000000..11e17039c4e --- /dev/null +++ b/test/feature_extraction.js @@ -0,0 +1,168 @@ +#! /usr/bin/env node + +var assert = require("assert"); +var fs = require("fs"); +var path = require("path"); +var U = require("./node"); + +var failures = 0; +var failed_files = Object.create(null); +var dir = path.resolve(path.dirname(module.filename), "feature_extraction"); +fs.readdirSync(dir).filter(function(name) { + return /\.js$/i.test(name); +}).forEach(function(file) { + log("--- {file}", { file: file }); + var tests = parse_test(path.resolve(dir, file)); + for (var i in tests) if (!test_case(tests[i])) { + failures++; + failed_files[file] = 1; + } +}); +if (failures) { + console.error(); + console.error("!!! Failed " + failures + " test case(s)."); + console.error("!!! " + Object.keys(failed_files).join(", ")); + process.exit(1); +} + +/* -----[ utils ]----- */ + +function evaluate(code) { + if (code instanceof U.AST_Node) code = make_code(code, { beautify: true }); + return new Function("return(" + code + ")")(); +} + +function log() { + console.log("%s", tmpl.apply(null, arguments)); +} + +function make_code(ast, options) { + var stream = U.OutputStream(options); + ast.print(stream); + return stream.get(); +} + +function parse_test(file) { + var script = fs.readFileSync(file, "utf8"); + // TODO try/catch can be removed after fixing https://github.com/mishoo/UglifyJS2/issues/348 + try { + var ast = U.parse(script, { + filename: file + }); + } catch (e) { + console.error("Caught error while parsing tests in " + file); + console.error(e); + process.exit(1); + } + var tests = Object.create(null); + var tw = new U.TreeWalker(function(node, descend) { + if (node instanceof U.AST_LabeledStatement + && tw.parent() instanceof U.AST_Toplevel) { + var name = node.label.name; + if (name in tests) { + throw new Error('Duplicated test name "' + name + '" in ' + file); + } + tests[name] = get_one_test(name, node.body); + return true; + } + if (!(node instanceof U.AST_Toplevel)) croak(node); + }); + ast.walk(tw); + return tests; + + function croak(node) { + throw new Error(tmpl("Can't understand test file {file} [{line},{col}]\n{code}", { + file: file, + line: node.start.line, + col: node.start.col, + code: make_code(node, { beautify: false }) + })); + } + + function read_string(stat) { + if (stat.TYPE == "SimpleStatement") { + var body = stat.body; + switch(body.TYPE) { + case "String": + return body.value; + case "Template": + if (body.expressions.length > 0) + throw new Error("Should be empty template string"); + return body.strings.join("\n"); + case "Array": + return body.elements.map(function(element) { + if (element.TYPE !== "String") + throw new Error("Should be array of strings"); + return element.value; + }).join("\n"); + } + } + throw new Error("Should be string or array of strings"); + } + + function get_one_test(name, block) { + var test = { name: name, options: {} }; + var tw = new U.TreeWalker(function(node, descend) { + if (node instanceof U.AST_Assign) { + if (!(node.left instanceof U.AST_SymbolRef)) { + croak(node); + } + var name = node.left.name; + test[name] = evaluate(node.right); + return true; + } + if (node instanceof U.AST_LabeledStatement) { + var label = node.label; + assert.ok([ + "input", + "expect", + ].indexOf(label.name) >= 0, tmpl("Unsupported label {name} [{line},{col}]", { + name: label.name, + line: label.start.line, + col: label.start.col + })); + var stat = node.body; + if (label.name == "expect") { + test[label.name] = read_string(stat); + } else { + test[label.name] = stat; + } + + return true; + } + }); + block.walk(tw); + return test; + } +} + +function removeWhitespace(input){ + return input.replace(/\s/g,""); +} + +function test_case(test) { + log(" Running test [{name}]", { name: test.name }); + var features = "FNAMES,ASTREL"; + if (test.options.features) { + features = test.options.features; + }; + + var expect = test.expect; + + var input_code = make_code(test.input, { beautify: true }); + var output = U.extractFeatures(input_code, test.name, false, features); + + if (removeWhitespace(expect) != removeWhitespace(output)) { + log("!!! failed\n---INPUT---\n{input}\n---OUTPUT---\n{output}\n---EXPECTED---\n{expected}\n\n", { + input: input_code, + output: output, + expected: expect + }); + return false;; + } + return true; +} + +function tmpl() { + return U.string_template.apply(null, arguments); +} diff --git a/test/feature_extraction/fnames.js b/test/feature_extraction/fnames.js index 2d5c79514cc..25120b9439f 100644 --- a/test/feature_extraction/fnames.js +++ b/test/feature_extraction/fnames.js @@ -5,8 +5,7 @@ func_decl_params: { input: { function chunkData(e, t) { } } - expect: { - '{ + expect: `{ "query":[ {"a": 0, "b": 1, "f2": "FNPAR"}, {"a": 0, "b": 2, "f2": "FNPAR"} @@ -16,8 +15,7 @@ func_decl_params: { {"v": 1, "inf": "e"}, {"v": 2, "inf": "t"} ] - }' - } + }` } func_simple_call: { @@ -29,15 +27,13 @@ func_simple_call: { foo(); } } - expect: { - '{ + expect: `{ "query":[ ], "assign":[ ] } - ' - } + ` } func_args: { @@ -50,8 +46,7 @@ func_args: { x.foo(b, n1, 42, n1, 42); } } - expect: { - '{ + expect: `{ "query":[ {"a": 0, "b": 1, "f2": "FNPAR"}, {"a": 0, "b": 2, "f2": "FNDECL"} @@ -62,8 +57,7 @@ func_args: { {"v": 2, "inf": "n1"} ] } - ' - } + ` } inner_lambda_assign_local: { @@ -77,8 +71,7 @@ inner_lambda_assign_local: { }; } } - expect: { - '{ + expect: `{ "query":[ {"a": 0, "b": 1, "f2": "FNPAR"}, {"a": 0, "b": 2, "f2": "FNDECL"}, @@ -92,8 +85,7 @@ inner_lambda_assign_local: { {"v": 3, "giv": "log"}, {"v": 4, "giv": "hello"} ] - }' - } + }` } inner_lambda_assign_global: { @@ -107,8 +99,7 @@ inner_lambda_assign_global: { }; } } - expect: { - '{ + expect: `{ "query":[ {"a": 0, "b": 1, "f2": "FNPAR"} ], @@ -116,8 +107,7 @@ inner_lambda_assign_global: { {"v": 0, "giv": "global"}, {"v": 1, "inf": "a"} ] - }' - } + }` } inner_lambda_assign_sub: { @@ -131,8 +121,7 @@ inner_lambda_assign_sub: { }; } } - expect: { - '{ + expect: `{ "query":[ {"a": 0, "b": 1, "f2": "[]-FNPAR"} ], @@ -140,8 +129,7 @@ inner_lambda_assign_sub: { {"v": 0, "giv": "global"}, {"v": 1, "inf": "a"} ] - }' - } + }` } inner_lambda_prop: { @@ -157,8 +145,7 @@ inner_lambda_prop: { }); } } - expect: { - '{ + expect: `{ "query":[ {"a": 0, "b": 1, "f2": "FNPAR"}, {"a": 2, "b": 3, "f2": "FNPAR"} @@ -169,8 +156,7 @@ inner_lambda_prop: { {"v": 2, "giv": "awesome_key"}, {"v": 3, "inf": "a"} ] - }' - } + }` } inner_lambda_arg: { @@ -184,8 +170,7 @@ inner_lambda_arg: { }); } } - expect: { - '{ + expect: `{ "query":[ {"a": 0, "b": 1, "f2": "(2)-FNPAR"} ], @@ -193,8 +178,7 @@ inner_lambda_arg: { {"v": 0, "giv": "substring"}, {"v": 1, "inf": "x"} ] - }' - } + }` } inner_lambda_arg2: { @@ -208,8 +192,7 @@ inner_lambda_arg2: { }); } } - expect: { - '{ + expect: `{ "query":[ {"a": 0, "b": 1, "f2": "(2)-FNPAR"} ], @@ -217,8 +200,7 @@ inner_lambda_arg2: { {"v": 0, "giv": "substring"}, {"v": 1, "inf": "b"} ] - }' - } + }` } func_return: { @@ -231,8 +213,7 @@ func_return: { return a; } } - expect: { - '{ + expect: `{ "query":[ {"a": 0, "b": 1, "f2": "FNDECL"}, {"a": 0, "b": 1, "f2": "FNRETURN"} @@ -241,8 +222,7 @@ func_return: { {"v": 0, "giv": "chunkData"}, {"v": 1, "inf": "a"} ] - }' - } + }` } func_scopes: { @@ -257,8 +237,7 @@ func_scopes: { })(2); } } - expect: { - '{ + expect: `{ "query":[ {"a": 0, "b": 1, "f2": "FNPAR"}, {"a": 0, "b": 2, "f2": "FNPAR"}, @@ -275,7 +254,6 @@ func_scopes: { {"v": 4, "giv": "y"}, {"v": 5, "inf": "r"} ] - }' - } + }` } diff --git a/test/feature_extraction/general.js b/test/feature_extraction/general.js index 470a3536712..4ed1ac8970d 100644 --- a/test/feature_extraction/general.js +++ b/test/feature_extraction/general.js @@ -7,17 +7,15 @@ truncate_constants: { var x = "very long string, very long string, very long string, very long string, very long string, very long string, very long string, very long string, very long string, very long string, very long string, very long string"; } } - expect: { - '{ + expect: `{ "query":[ - {"a": 0, "b": 1, "f2": ":VarDef[1]-String"} + {"a": 0, "b": 1, "f2": "[0]:VarDef[1]-String"} ], "assign":[ {"v": 0, "inf": "x"}, {"v": 1, "giv": "very%20long%20string%2C%20very%20long%20string%2C%20very%20long%20string%2C%20very%20long%20"} ] - }' - } + }` } escape_constants: { @@ -29,17 +27,15 @@ escape_constants: { var x = '"quoted text"'; } } - expect: { - '{ + expect: `{ "query":[ - {"a": 0, "b": 1, "f2": ":VarDef[1]-String"} + {"a": 0, "b": 1, "f2": "[0]:VarDef[1]-String"} ], "assign":[ {"v": 0, "inf": "x"}, {"v": 1, "giv": "%22quoted%20text%22"} ] - }' - } + }` } @@ -55,16 +51,15 @@ var_scope: { x = a + b; } } - expect: { - '{ + expect: `{ "query":[ - {"a": 0, "b": 1, "f2": ":VarDef[1]-Number"}, - {"a": 2, "b": 3, "f2": ":VarDef[1]-Number"}, - {"a": 4, "b": 0, "f2": ":VarDef[1]Binary+[0]"}, - {"a": 4, "b": 2, "f2": ":VarDef[1]Binary+[1]"}, - {"a": 0, "b": 2, "f2": ":Binary+[1]"}, - {"a": 4, "b": 0, "f2": ":Assign=[1]Binary+[0]"}, - {"a": 4, "b": 2, "f2": ":Assign=[1]Binary+[1]"} + {"a": 0, "b": 1, "f2": "[0]:VarDef[1]-Number"}, + {"a": 2, "b": 3, "f2": "[0]:VarDef[1]-Number"}, + {"a": 4, "b": 0, "f2": "[0]:VarDef[1]Binary+[0]"}, + {"a": 4, "b": 2, "f2": "[0]:VarDef[1]Binary+[1]"}, + {"a": 0, "b": 2, "f2": "[0]:Binary+[1]"}, + {"a": 4, "b": 0, "f2": "[0]:Assign=[1]Binary+[0]"}, + {"a": 4, "b": 2, "f2": "[0]:Assign=[1]Binary+[1]"} ], "assign":[ {"v": 0, "inf": "a"}, @@ -73,8 +68,7 @@ var_scope: { {"v": 3, "giv": "2"}, {"v": 4, "inf": "x"} ] - }' - } + }` } this_scope: { @@ -90,13 +84,12 @@ this_scope: { this.x = a; } } - expect: { - '{ + expect: `{ "query":[ - {"a": 0, "b": 1, "f2": ":Assign=[1]"}, - {"a": 2, "b": 1, "f2": "Dot[0]:Assign=[1]"}, - {"a": 0, "b": 3, "f2": ":Assign=[1]"}, - {"a": 2, "b": 3, "f2": "Dot[0]:Assign=[1]"} + {"a": 0, "b": 1, "f2": "[0]:Assign=[1]"}, + {"a": 2, "b": 1, "f2": "[0]Dot[0]:Assign=[1]"}, + {"a": 0, "b": 3, "f2": "[0]:Assign=[1]"}, + {"a": 2, "b": 3, "f2": "[0]Dot[0]:Assign=[1]"} ], "assign":[ {"v": 0, "giv": "x"}, @@ -104,8 +97,7 @@ this_scope: { {"v": 2, "giv": "this"}, {"v": 3, "inf": "a"} ] - }' - } + }` } this_is_given: { @@ -117,19 +109,17 @@ this_is_given: { this.x = a; } } - expect: { - '{ + expect: `{ "query":[ - {"a": 0, "b": 1, "f2": ":Assign=[1]"}, - {"a": 2, "b": 1, "f2": "Dot[0]:Assign=[1]"} + {"a": 0, "b": 1, "f2": "[0]:Assign=[1]"}, + {"a": 2, "b": 1, "f2": "[0]Dot[0]:Assign=[1]"} ], "assign":[ {"v": 0, "giv": "x"}, {"v": 1, "inf": "a"}, {"v": 2, "giv": "this"} ] - }' - } + }` } @@ -143,13 +133,12 @@ this_attr_scope: { this.x = b; } } - expect: { - '{ + expect: `{ "query":[ - {"a": 0, "b": 1, "f2": ":Assign=[1]"}, - {"a": 2, "b": 1, "f2": "Dot[0]:Assign=[1]"}, - {"a": 0, "b": 3, "f2": ":Assign=[1]"}, - {"a": 2, "b": 3, "f2": "Dot[0]:Assign=[1]"} + {"a": 0, "b": 1, "f2": "[0]:Assign=[1]"}, + {"a": 2, "b": 1, "f2": "[0]Dot[0]:Assign=[1]"}, + {"a": 0, "b": 3, "f2": "[0]:Assign=[1]"}, + {"a": 2, "b": 3, "f2": "[0]Dot[0]:Assign=[1]"} ], "assign":[ {"v": 0, "giv": "x"}, @@ -157,8 +146,7 @@ this_attr_scope: { {"v": 2, "giv": "this"}, {"v": 3, "inf": "b"} ] - }' - } + }` } bool_const_type: { @@ -170,8 +158,7 @@ bool_const_type: { var x = true; } } - expect: { - '{ + expect: `{ "query":[ {"a": 0, "b": 1, "f2": "FNDECL"} ], @@ -179,8 +166,7 @@ bool_const_type: { {"v": 0, "giv": "chunkData"}, {"v": 1, "inf": "x"} ] - }' - } + }` } handles_toString_call: { @@ -193,8 +179,7 @@ handles_toString_call: { x.toString(); } } - expect: { - '{ + expect: `{ "query":[ {"a": 0, "b": 1, "f2": "FNDECL"} ], @@ -202,8 +187,7 @@ handles_toString_call: { {"v": 0, "giv": "chunkData"}, {"v": 1, "inf": "x"} ] - }' - } + }` } escape_backslash: { @@ -215,8 +199,7 @@ escape_backslash: { x.replace(/\s/g, "a"); } } - expect: { - '{ + expect: `{ "query":[ {"a": 0, "b": 1, "f2": "FNPAR"} ], @@ -224,8 +207,7 @@ escape_backslash: { {"v": 0, "giv": "chunkData"}, {"v": 1, "inf": "x"} ] - }' - } + }` } func_no_duplicates: { @@ -238,18 +220,16 @@ func_no_duplicates: { a = new chunkData(); } } - expect: { - '{ + expect: `{ "query":[ - {"a": 0, "b": 1, "f2": ":VarDef[1]New[0]"}, - {"a": 0, "b": 1, "f2": ":Assign=[1]New[0]"} + {"a": 0, "b": 1, "f2": "[0]:VarDef[1]New[0]"}, + {"a": 0, "b": 1, "f2": "[0]:Assign=[1]New[0]"} ], "assign":[ {"v": 0, "inf": "a"}, {"v": 1, "giv": "chunkData"} ] - }' - } + }` } func_allow_different_features_duplicates: { @@ -262,22 +242,20 @@ func_allow_different_features_duplicates: { return 42; } } - expect: { - '{ + expect: `{ "query":[ - {"a": 0, "b": 1, "f2": ":Dot[0]"}, - {"a": 0, "b": 2, "f2": "Dot[0]:Call[1]-Number"}, - {"a": 0, "b": 2, "f2": "Dot[0]:Call[2]-Number"}, - {"a": 0, "b": 2, "f2": "Dot[0]:Call[3]-Number"}, - {"a": 0, "b": 2, "f2": "Dot[0]:Call[4]-Number"} + {"a": 0, "b": 1, "f2": "[0]:Dot[0]"}, + {"a": 0, "b": 2, "f2": "[0]Dot[0]:Call[1]-Number"}, + {"a": 0, "b": 2, "f2": "[0]Dot[0]:Call[2]-Number"}, + {"a": 0, "b": 2, "f2": "[0]Dot[0]:Call[3]-Number"}, + {"a": 0, "b": 2, "f2": "[0]Dot[0]:Call[4]-Number"} ], "assign":[ {"v": 0, "inf": "x"}, {"v": 1, "giv": "foo"}, {"v": 2, "giv": "42"} ] - }' - } + }` } method_name_fixed: { @@ -287,10 +265,9 @@ method_name_fixed: { bar(); } } - expect: { - '{ + expect: `{ "query":[ - {"a": 0, "b": 1, "f2": ":Dot[0]"}, + {"a": 0, "b": 1, "f2": "[0]:Dot[0]"}, {"a": 2, "b": 0, "f2": "FNPAR"} ], "assign":[ @@ -298,6 +275,5 @@ method_name_fixed: { {"v": 1, "giv": "foo"}, {"v": 2, "giv": "chunkData"} ] - }' - } + }` } \ No newline at end of file diff --git a/test/fetch.js b/test/fetch.js new file mode 100644 index 00000000000..cd78d2d0660 --- /dev/null +++ b/test/fetch.js @@ -0,0 +1,33 @@ +var fs = require("fs"); +var parse = require("url").parse; +var path = require("path"); + +try { + fs.mkdirSync("./tmp"); +} catch (e) { + if (e.code != "EEXIST") throw e; +} + +function local(url) { + return path.join("./tmp", encodeURIComponent(url)); +} + +function read(url) { + return fs.createReadStream(local(url)); +} + +module.exports = function(url, callback) { + var result = read(url); + result.on("error", function(e) { + if (e.code != "ENOENT") return callback(e); + var options = parse(url); + options.rejectUnauthorized = false; + require(options.protocol.slice(0, -1)).get(options, function(res) { + if (res.statusCode !== 200) return callback(res.statusCode); + res.pipe(fs.createWriteStream(local(url))); + callback(null, res); + }); + }).on("open", function() { + callback(null, result); + }); +}; diff --git a/test/input/comments/filter.js b/test/input/comments/filter.js new file mode 100644 index 00000000000..c752080f44d --- /dev/null +++ b/test/input/comments/filter.js @@ -0,0 +1,3 @@ +// foo +/*@preserve*/ +// bar diff --git a/test/input/enclose/input.js b/test/input/enclose/input.js new file mode 100644 index 00000000000..5f53997b428 --- /dev/null +++ b/test/input/enclose/input.js @@ -0,0 +1,4 @@ +function enclose() { + console.log("test enclose"); +} +enclose(); diff --git a/test/input/global_defs/nested.js b/test/input/global_defs/nested.js new file mode 100644 index 00000000000..dbf57909c73 --- /dev/null +++ b/test/input/global_defs/nested.js @@ -0,0 +1 @@ +console.log(C.V, C.D); diff --git a/test/input/global_defs/simple.js b/test/input/global_defs/simple.js new file mode 100644 index 00000000000..44d515e3d33 --- /dev/null +++ b/test/input/global_defs/simple.js @@ -0,0 +1 @@ +console.log(D); diff --git a/test/input/invalid/assign_1.js b/test/input/invalid/assign_1.js new file mode 100644 index 00000000000..6d09d132d2c --- /dev/null +++ b/test/input/invalid/assign_1.js @@ -0,0 +1 @@ +console.log(1 || 5--); diff --git a/test/input/invalid/assign_2.js b/test/input/invalid/assign_2.js new file mode 100644 index 00000000000..197bdc90e56 --- /dev/null +++ b/test/input/invalid/assign_2.js @@ -0,0 +1 @@ +console.log(2 || (Math.random() /= 2)); diff --git a/test/input/invalid/assign_3.js b/test/input/invalid/assign_3.js new file mode 100644 index 00000000000..7c560e4b86c --- /dev/null +++ b/test/input/invalid/assign_3.js @@ -0,0 +1 @@ +console.log(3 || ++this); diff --git a/test/input/invalid/assign_4.js b/test/input/invalid/assign_4.js new file mode 100644 index 00000000000..787a0f3ed9f --- /dev/null +++ b/test/input/invalid/assign_4.js @@ -0,0 +1 @@ +console.log(4 || (null = 4)); diff --git a/test/input/invalid/assign_5.js b/test/input/invalid/assign_5.js new file mode 100644 index 00000000000..1a850dd08ef --- /dev/null +++ b/test/input/invalid/assign_5.js @@ -0,0 +1 @@ +console.log(5 || ([]?.length ^= 5)); diff --git a/test/input/invalid/delete.js b/test/input/invalid/delete.js new file mode 100644 index 00000000000..9753d3afadd --- /dev/null +++ b/test/input/invalid/delete.js @@ -0,0 +1,14 @@ +function f(x) { + delete 42; + delete (0, x); + delete null; + delete x; +} + +function g(x) { + "use strict"; + delete 42; + delete (0, x); + delete null; + delete x; +} diff --git a/test/input/invalid/destructured_var.js b/test/input/invalid/destructured_var.js new file mode 100644 index 00000000000..8e882f7e7c8 --- /dev/null +++ b/test/input/invalid/destructured_var.js @@ -0,0 +1,8 @@ +function f() { + var { eval } = null; +} + +function g() { + "use strict"; + var { eval } = 42; +} diff --git a/test/input/invalid/dot_1.js b/test/input/invalid/dot_1.js new file mode 100644 index 00000000000..7c4f3a66a1d --- /dev/null +++ b/test/input/invalid/dot_1.js @@ -0,0 +1 @@ +a.= diff --git a/test/input/invalid/dot_2.js b/test/input/invalid/dot_2.js new file mode 100644 index 00000000000..32c027f887d --- /dev/null +++ b/test/input/invalid/dot_2.js @@ -0,0 +1 @@ +%.a; diff --git a/test/input/invalid/dot_3.js b/test/input/invalid/dot_3.js new file mode 100644 index 00000000000..6557382812c --- /dev/null +++ b/test/input/invalid/dot_3.js @@ -0,0 +1 @@ +a./(); diff --git a/test/input/invalid/else.js b/test/input/invalid/else.js new file mode 100644 index 00000000000..89e8e5019a1 --- /dev/null +++ b/test/input/invalid/else.js @@ -0,0 +1 @@ +if (0) else 1; diff --git a/test/input/invalid/eof.js b/test/input/invalid/eof.js new file mode 100644 index 00000000000..330d5023efa --- /dev/null +++ b/test/input/invalid/eof.js @@ -0,0 +1 @@ +foo, bar( diff --git a/test/input/invalid/for-await.js b/test/input/invalid/for-await.js new file mode 100644 index 00000000000..13e07607522 --- /dev/null +++ b/test/input/invalid/for-await.js @@ -0,0 +1 @@ +for await (; console.log(42);); diff --git a/test/input/invalid/for-in_1.js b/test/input/invalid/for-in_1.js new file mode 100644 index 00000000000..4d872d194fd --- /dev/null +++ b/test/input/invalid/for-in_1.js @@ -0,0 +1,4 @@ +var a, b = [1, 2]; +for (1, 2, a in b) { + console.log(a, b[a]); +} diff --git a/test/input/invalid/for-in_2.js b/test/input/invalid/for-in_2.js new file mode 100644 index 00000000000..57d861e92ce --- /dev/null +++ b/test/input/invalid/for-in_2.js @@ -0,0 +1,4 @@ +var c = [1, 2]; +for (var a, b in c) { + console.log(a, c[a]); +} diff --git a/test/input/invalid/for-of_1.js b/test/input/invalid/for-of_1.js new file mode 100644 index 00000000000..7d4ffe25fcc --- /dev/null +++ b/test/input/invalid/for-of_1.js @@ -0,0 +1,3 @@ +var a = [ 1 ], b; +for (b = 2 of a) + console.log(b); diff --git a/test/input/invalid/for-of_2.js b/test/input/invalid/for-of_2.js new file mode 100644 index 00000000000..52972b8036d --- /dev/null +++ b/test/input/invalid/for-of_2.js @@ -0,0 +1,3 @@ +var a = [ 1 ]; +for (var b = 2 of a) + console.log(b); diff --git a/test/input/invalid/function_1.js b/test/input/invalid/function_1.js new file mode 100644 index 00000000000..bff9c75a742 --- /dev/null +++ b/test/input/invalid/function_1.js @@ -0,0 +1,6 @@ +function f(arguments) { +} + +function g(arguments) { + "use strict"; +} diff --git a/test/input/invalid/function_2.js b/test/input/invalid/function_2.js new file mode 100644 index 00000000000..cc496a4ea0d --- /dev/null +++ b/test/input/invalid/function_2.js @@ -0,0 +1,6 @@ +function arguments() { +} + +function eval() { + "use strict"; +} diff --git a/test/input/invalid/function_3.js b/test/input/invalid/function_3.js new file mode 100644 index 00000000000..4a20d2a6fdd --- /dev/null +++ b/test/input/invalid/function_3.js @@ -0,0 +1,6 @@ +!function eval() { +}(); + +!function arguments() { + "use strict"; +}(); diff --git a/test/input/invalid/loop-no-body.js b/test/input/invalid/loop-no-body.js new file mode 100644 index 00000000000..07b27428da7 --- /dev/null +++ b/test/input/invalid/loop-no-body.js @@ -0,0 +1 @@ +for (var i = 0; i < 1; i++) diff --git a/test/input/invalid/object.js b/test/input/invalid/object.js new file mode 100644 index 00000000000..46216d813cb --- /dev/null +++ b/test/input/invalid/object.js @@ -0,0 +1 @@ +console.log({%: 1}); diff --git a/test/input/invalid/optional-template.js b/test/input/invalid/optional-template.js new file mode 100644 index 00000000000..608f4831d84 --- /dev/null +++ b/test/input/invalid/optional-template.js @@ -0,0 +1 @@ +console?.log``; diff --git a/test/input/invalid/return.js b/test/input/invalid/return.js new file mode 100644 index 00000000000..d232c623620 --- /dev/null +++ b/test/input/invalid/return.js @@ -0,0 +1 @@ +return 42; diff --git a/test/input/invalid/simple.js b/test/input/invalid/simple.js new file mode 100644 index 00000000000..98a07d20245 --- /dev/null +++ b/test/input/invalid/simple.js @@ -0,0 +1 @@ +function f(a{} diff --git a/test/input/invalid/switch.js b/test/input/invalid/switch.js new file mode 100644 index 00000000000..6ea32158694 --- /dev/null +++ b/test/input/invalid/switch.js @@ -0,0 +1,4 @@ +switch (0) { + default: + default: +} diff --git a/test/input/invalid/tab.js b/test/input/invalid/tab.js new file mode 100644 index 00000000000..f209b8ee6c9 --- /dev/null +++ b/test/input/invalid/tab.js @@ -0,0 +1 @@ + foo( xyz, 0abc); diff --git a/test/input/invalid/try.js b/test/input/invalid/try.js new file mode 100644 index 00000000000..e65a55ccb30 --- /dev/null +++ b/test/input/invalid/try.js @@ -0,0 +1,8 @@ +function f() { + try {} catch (eval) {} +} + +function g() { + "use strict"; + try {} catch (eval) {} +} diff --git a/test/input/invalid/var.js b/test/input/invalid/var.js new file mode 100644 index 00000000000..e3ccbe87b41 --- /dev/null +++ b/test/input/invalid/var.js @@ -0,0 +1,8 @@ +function f() { + var eval; +} + +function g() { + "use strict"; + var eval; +} diff --git a/test/input/issue-1236/simple.js b/test/input/issue-1236/simple.js new file mode 100644 index 00000000000..8bdd7f26ec8 --- /dev/null +++ b/test/input/issue-1236/simple.js @@ -0,0 +1,8 @@ +"use strict"; + +var foo = function foo(x) { + return "foo " + x; +}; +console.log(foo("bar")); + +//# sourceMappingURL=simple.js.map diff --git a/test/input/issue-1236/simple.js.map b/test/input/issue-1236/simple.js.map new file mode 100644 index 00000000000..28989e0f19e --- /dev/null +++ b/test/input/issue-1236/simple.js.map @@ -0,0 +1,8 @@ +{ + "version": 3, + "sources": ["index.js"], + "names": [], + "mappings": ";;AAAA,IAAI,MAAM,SAAN,GAAM;AAAA,SAAK,SAAS,CAAd;AAAA,CAAV;AACA,QAAQ,GAAR,CAAY,IAAI,KAAJ,CAAZ", + "file": "simple.js", + "sourcesContent": ["let foo = x => \"foo \" + x;\nconsole.log(foo(\"bar\"));"] +} diff --git a/test/input/issue-1242/bar.es5 b/test/input/issue-1242/bar.es5 new file mode 100644 index 00000000000..6e308a301b5 --- /dev/null +++ b/test/input/issue-1242/bar.es5 @@ -0,0 +1,4 @@ +function bar(x) { + var triple = x * (2 + 1); + return triple; +} diff --git a/test/input/issue-1242/baz.es5 b/test/input/issue-1242/baz.es5 new file mode 100644 index 00000000000..83c98ef6d66 --- /dev/null +++ b/test/input/issue-1242/baz.es5 @@ -0,0 +1,4 @@ +function baz(x) { + var half = x / 2; + return half; +} diff --git a/test/input/issue-1242/foo.es5 b/test/input/issue-1242/foo.es5 new file mode 100644 index 00000000000..4b439075689 --- /dev/null +++ b/test/input/issue-1242/foo.es5 @@ -0,0 +1,5 @@ +var print = console.log.bind(console); +function foo(x) { + var twice = x * 2; + print('Foo:', twice); +} diff --git a/test/input/issue-1242/qux.js b/test/input/issue-1242/qux.js new file mode 100644 index 00000000000..4a72ffc279a --- /dev/null +++ b/test/input/issue-1242/qux.js @@ -0,0 +1,4 @@ +var x = bar(1+2); +var y = baz(3+9); +print('q' + 'u' + 'x', x, y); +foo(5+6); diff --git a/test/input/issue-1323/sample.js b/test/input/issue-1323/sample.js new file mode 100644 index 00000000000..a1798cf589b --- /dev/null +++ b/test/input/issue-1323/sample.js @@ -0,0 +1,7 @@ +var bar = (function() { + function foo (bar) { + return bar; + } + + return foo; +})(); \ No newline at end of file diff --git a/test/input/issue-1431/sample.js b/test/input/issue-1431/sample.js new file mode 100644 index 00000000000..32068cb2ee0 --- /dev/null +++ b/test/input/issue-1431/sample.js @@ -0,0 +1,14 @@ +function f(x) { + return function() { + function n(a) { + return a * a; + } + return x(n); + }; +} + +function g(op) { + return op(1) + op(2); +} + +console.log(f(g)() == 5); \ No newline at end of file diff --git a/test/input/issue-1482/beautify.js b/test/input/issue-1482/beautify.js new file mode 100644 index 00000000000..14054e9820e --- /dev/null +++ b/test/input/issue-1482/beautify.js @@ -0,0 +1,17 @@ +if (x) foo(); + +if (x) foo(); else baz(); + +if (x) foo(); else if (y) bar(); else baz(); + +if (x) if (y) foo(); else bar(); else baz(); + +if (x) foo(); else if (y) bar(); else if (z) baz(); else moo(); + +function f() { + if (x) foo(); + if (x) foo(); else baz(); + if (x) foo(); else if (y) bar(); else baz(); + if (x) if (y) foo(); else bar(); else baz(); + if (x) foo(); else if (y) bar(); else if (z) baz(); else moo(); +} diff --git a/test/input/issue-1482/braces.js b/test/input/issue-1482/braces.js new file mode 100644 index 00000000000..2c2b103c5f5 --- /dev/null +++ b/test/input/issue-1482/braces.js @@ -0,0 +1,73 @@ +if (x) { + foo(); +} + +if (x) { + foo(); +} else { + baz(); +} + +if (x) { + foo(); +} else if (y) { + bar(); +} else { + baz(); +} + +if (x) { + if (y) { + foo(); + } else { + bar(); + } +} else { + baz(); +} + +if (x) { + foo(); +} else if (y) { + bar(); +} else if (z) { + baz(); +} else { + moo(); +} + +function f() { + if (x) { + foo(); + } + if (x) { + foo(); + } else { + baz(); + } + if (x) { + foo(); + } else if (y) { + bar(); + } else { + baz(); + } + if (x) { + if (y) { + foo(); + } else { + bar(); + } + } else { + baz(); + } + if (x) { + foo(); + } else if (y) { + bar(); + } else if (z) { + baz(); + } else { + moo(); + } +} diff --git a/test/input/issue-1482/default.js b/test/input/issue-1482/default.js new file mode 100644 index 00000000000..505b30cc40c --- /dev/null +++ b/test/input/issue-1482/default.js @@ -0,0 +1 @@ +if(x)foo();if(x)foo();else baz();if(x)foo();else if(y)bar();else baz();if(x)if(y)foo();else bar();else baz();if(x)foo();else if(y)bar();else if(z)baz();else moo();function f(){if(x)foo();if(x)foo();else baz();if(x)foo();else if(y)bar();else baz();if(x)if(y)foo();else bar();else baz();if(x)foo();else if(y)bar();else if(z)baz();else moo()} diff --git a/test/input/issue-1482/input.js b/test/input/issue-1482/input.js new file mode 100644 index 00000000000..0186e82c8c9 --- /dev/null +++ b/test/input/issue-1482/input.js @@ -0,0 +1,12 @@ +if (x) foo(); +if (x) foo(); else baz(); +if (x) foo(); else if (y) bar(); else baz(); +if (x) if (y) foo(); else bar(); else baz(); +if (x) foo(); else if (y) bar(); else if (z) baz(); else moo(); +function f() { +if (x) foo(); +if (x) foo(); else baz(); +if (x) foo(); else if (y) bar(); else baz(); +if (x) if (y) foo(); else bar(); else baz(); +if (x) foo(); else if (y) bar(); else if (z) baz(); else moo(); +} diff --git a/test/input/issue-1632/^{foo}[bar](baz)+$.js b/test/input/issue-1632/^{foo}[bar](baz)+$.js new file mode 100644 index 00000000000..f92e3a10b4b --- /dev/null +++ b/test/input/issue-1632/^{foo}[bar](baz)+$.js @@ -0,0 +1 @@ +console.log(x); \ No newline at end of file diff --git a/test/input/issue-2082/sample.js b/test/input/issue-2082/sample.js new file mode 100644 index 00000000000..f92e3a10b4b --- /dev/null +++ b/test/input/issue-2082/sample.js @@ -0,0 +1 @@ +console.log(x); \ No newline at end of file diff --git a/test/input/issue-2082/sample.js.map b/test/input/issue-2082/sample.js.map new file mode 100644 index 00000000000..88b42f5ab1f --- /dev/null +++ b/test/input/issue-2082/sample.js.map @@ -0,0 +1 @@ +{"version": 3,"sources": ["index.js"],"mappings": ";"} diff --git a/test/input/issue-2310/input.js b/test/input/issue-2310/input.js new file mode 100644 index 00000000000..ea7c1f33678 --- /dev/null +++ b/test/input/issue-2310/input.js @@ -0,0 +1,10 @@ +function foo() { + return function() { + console.log("PASS"); + }; +} + +(function() { + var f = foo(); + f(); +})(); diff --git a/test/input/issue-3040/expect.js b/test/input/issue-3040/expect.js new file mode 100644 index 00000000000..19b76b3ede9 --- /dev/null +++ b/test/input/issue-3040/expect.js @@ -0,0 +1,41 @@ +"use strict"; + +function _toConsumableArray(arr) { + return _arrayWithoutHoles(arr) || _iterableToArray(arr) || _unsupportedIterableToArray(arr) || _nonIterableSpread(); +} + +function _nonIterableSpread() { + throw new TypeError("Invalid attempt to spread non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); +} + +function _unsupportedIterableToArray(o, minLen) { + if (!o) return; + if (typeof o === "string") return _arrayLikeToArray(o, minLen); + var n = Object.prototype.toString.call(o).slice(8, -1); + if (n === "Object" && o.constructor) n = o.constructor.name; + if (n === "Map" || n === "Set") return Array.from(n); + if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen); +} + +function _iterableToArray(iter) { + if (typeof Symbol !== "undefined" && Symbol.iterator in Object(iter)) return Array.from(iter); +} + +function _arrayWithoutHoles(arr) { + if (Array.isArray(arr)) return _arrayLikeToArray(arr); +} + +function _arrayLikeToArray(arr, len) { + if (len == null || len > arr.length) len = arr.length; + for (var i = 0, arr2 = new Array(len); i < len; i++) { + arr2[i] = arr[i]; + } + return arr2; +} + +var _require = require("bar"), foo = _require.foo; + +var _require2 = require("world"), hello = _require2.hello; + +foo.x.apply(foo, _toConsumableArray(foo.y(hello.z))); +//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbImlucHV0LmpzIl0sInNvdXJjZXNDb250ZW50IjpbImNvbnN0IHtmb299ID0gcmVxdWlyZShcImJhclwiKTtcbmNvbnN0IHtoZWxsb30gPSByZXF1aXJlKFwid29ybGRcIik7XG5cbmZvby54KC4uLmZvby55KGhlbGxvLnopKTtcbiJdLCJuYW1lcyI6WyJyZXF1aXJlIiwiZm9vIiwiaGVsbG8iLCJ4IiwiYXBwbHkiLCJfdG9Db25zdW1hYmxlQXJyYXkiLCJ5IiwieiJdLCJtYXBwaW5ncyI6Ijs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7ZUFBY0EsUUFBUSxLQUFELEdBQWRDLE0sU0FBQUE7O0EsZ0JBQ1NELFFBQVEsT0FBRCxHQUFoQkUsUSxVQUFBQTs7QUFFUEQsSUFBSUUsRUFBSkMsTUFBQUgsS0FBR0ksbUJBQU1KLElBQUlLLEVBQUVKLE1BQU1LLENBQVosQ0FBTixDQUFBIn0= diff --git a/test/input/issue-3040/input.js b/test/input/issue-3040/input.js new file mode 100644 index 00000000000..6e4c2ccee34 --- /dev/null +++ b/test/input/issue-3040/input.js @@ -0,0 +1,23 @@ +"use strict"; + +function _toConsumableArray(arr) { return _arrayWithoutHoles(arr) || _iterableToArray(arr) || _unsupportedIterableToArray(arr) || _nonIterableSpread(); } + +function _nonIterableSpread() { throw new TypeError("Invalid attempt to spread non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } + +function _unsupportedIterableToArray(o, minLen) { if (!o) return; if (typeof o === "string") return _arrayLikeToArray(o, minLen); var n = Object.prototype.toString.call(o).slice(8, -1); if (n === "Object" && o.constructor) n = o.constructor.name; if (n === "Map" || n === "Set") return Array.from(n); if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen); } + +function _iterableToArray(iter) { if (typeof Symbol !== "undefined" && Symbol.iterator in Object(iter)) return Array.from(iter); } + +function _arrayWithoutHoles(arr) { if (Array.isArray(arr)) return _arrayLikeToArray(arr); } + +function _arrayLikeToArray(arr, len) { if (len == null || len > arr.length) len = arr.length; for (var i = 0, arr2 = new Array(len); i < len; i++) { arr2[i] = arr[i]; } return arr2; } + +var _require = require("bar"), + foo = _require.foo; + +var _require2 = require("world"), + hello = _require2.hello; + +foo.x.apply(foo, _toConsumableArray(foo.y(hello.z))); + +//# sourceMappingURL=input.js.map \ No newline at end of file diff --git a/test/input/issue-3040/input.js.map b/test/input/issue-3040/input.js.map new file mode 100644 index 00000000000..285e768827f --- /dev/null +++ b/test/input/issue-3040/input.js.map @@ -0,0 +1,11 @@ +{ + "version": 3, + "sources": [ + "input.js" + ], + "names": [], + "mappings": ";;;;;;;;;;;;;;eAAc,OAAO,CAAC,KAAD,C;IAAd,G,YAAA,G;;gBACS,OAAO,CAAC,OAAD,C;IAAhB,K,aAAA,K;;AAEP,GAAG,CAAC,CAAJ,OAAA,GAAG,qBAAM,GAAG,CAAC,CAAJ,CAAM,KAAK,CAAC,CAAZ,CAAN,EAAH", + "sourcesContent": [ + "const {foo} = require(\"bar\");\nconst {hello} = require(\"world\");\n\nfoo.x(...foo.y(hello.z));\n" + ] +} diff --git a/test/input/issue-3294/input.js b/test/input/issue-3294/input.js new file mode 100644 index 00000000000..09ee40f6fc8 --- /dev/null +++ b/test/input/issue-3294/input.js @@ -0,0 +1,6 @@ +var Foo = function Foo(){console.log(1+2);}; new Foo(); + +//# sourceMappingURL=data:application/json;charset=utf-8;base64,DeadBeef +//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjpudWxsLCJzb3VyY2VzIjpbInN0ZGluIl0sInNvdXJjZXNDb250ZW50IjpbImNsYXNzIEZvbyB7IGNvbnN0cnVjdG9yKCl7Y29uc29sZS5sb2coMSsyKTt9IH0gbmV3IEZvbygpO1xuIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLElBQU0sR0FBRyxHQUFDLEFBQUUsWUFBVyxFQUFFLENBQUMsT0FBTyxDQUFDLEdBQUcsQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFBLEFBQUUsQ0FBQyxJQUFJLEdBQUcsRUFBRSxDQUFDOyJ9 + + diff --git a/test/input/issue-3294/output.js b/test/input/issue-3294/output.js new file mode 100644 index 00000000000..cac93512c9a --- /dev/null +++ b/test/input/issue-3294/output.js @@ -0,0 +1,2 @@ +console.log(3); +//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbInN0ZGluIl0sInNvdXJjZXNDb250ZW50IjpbImNsYXNzIEZvbyB7IGNvbnN0cnVjdG9yKCl7Y29uc29sZS5sb2coMSsyKTt9IH0gbmV3IEZvbygpO1xuIl0sIm5hbWVzIjpbImNvbnNvbGUiLCJsb2ciXSwibWFwcGluZ3MiOiJBQUEwQkEsUUFBUUMsSUFBSSxDQUFHIn0= diff --git a/test/input/issue-3315/config.json b/test/input/issue-3315/config.json new file mode 100644 index 00000000000..097149c73c0 --- /dev/null +++ b/test/input/issue-3315/config.json @@ -0,0 +1,8 @@ +{ + "compress": false, + "mangle": { + "properties": { + "regex": "/^_/" + } + } +} diff --git a/test/input/issue-3315/input.js b/test/input/issue-3315/input.js new file mode 100644 index 00000000000..5013fc2f910 --- /dev/null +++ b/test/input/issue-3315/input.js @@ -0,0 +1,8 @@ +function f() { + "aaaaaaaaaa"; + var o = { + prop: 1, + _int: 2, + }; + return o.prop + o._int; +} diff --git a/test/input/issue-3441/input.js b/test/input/issue-3441/input.js new file mode 100644 index 00000000000..c9ca8e7745d --- /dev/null +++ b/test/input/issue-3441/input.js @@ -0,0 +1,8 @@ +// Generated by CoffeeScript 2.4.1 +(function() { + console.log('hello'); + +}).call(this); + +//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoibWFpbi5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIm1haW4uY29mZmVlIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7QUFBQTtFQUFBLE9BQU8sQ0FBQyxHQUFSLENBQVksT0FBWjtBQUFBIiwic291cmNlc0NvbnRlbnQiOlsiY29uc29sZS5sb2cgJ2hlbGxvJ1xuIl19 +//# sourceURL=/Users/mohamed/Downloads/main.coffee diff --git a/test/input/issue-505/input.js b/test/input/issue-505/input.js new file mode 100644 index 00000000000..600b12cf6d3 --- /dev/null +++ b/test/input/issue-505/input.js @@ -0,0 +1,5 @@ +function test(callback) { + 'aaaaaaaaaaaaaaaa'; + callback(err, data); + callback(err, data); +} diff --git a/test/input/issue-505/output.js b/test/input/issue-505/output.js new file mode 100644 index 00000000000..139c568db8f --- /dev/null +++ b/test/input/issue-505/output.js @@ -0,0 +1,5 @@ +function test(a){ +"aaaaaaaaaaaaaaaa"; +a(err,data),a(err, +data)} +//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIjAiXSwibmFtZXMiOlsidGVzdCIsImNhbGxiYWNrIiwiZXJyIiwiZGF0YSJdLCJtYXBwaW5ncyI6IkFBQUEsU0FBU0E7SEFBS0MsR0FDVjtBQUNBQTtoQkFBU0MsSUFBS0MsSUFBSSxFQUNsQkYsRUFBU0MsSUFBS0MsSUFBSSxDQUN0QiJ9 \ No newline at end of file diff --git a/test/input/issue-520/input.js b/test/input/issue-520/input.js new file mode 100644 index 00000000000..62428a523a8 --- /dev/null +++ b/test/input/issue-520/input.js @@ -0,0 +1,3 @@ +var Foo = function Foo(){console.log(1+2);}; new Foo(); + +//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjpudWxsLCJzb3VyY2VzIjpbInN0ZGluIl0sInNvdXJjZXNDb250ZW50IjpbImNsYXNzIEZvbyB7IGNvbnN0cnVjdG9yKCl7Y29uc29sZS5sb2coMSsyKTt9IH0gbmV3IEZvbygpO1xuIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLElBQU0sR0FBRyxHQUFDLEFBQUUsWUFBVyxFQUFFLENBQUMsT0FBTyxDQUFDLEdBQUcsQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFBLEFBQUUsQ0FBQyxJQUFJLEdBQUcsRUFBRSxDQUFDOyJ9 diff --git a/test/input/issue-520/output.js b/test/input/issue-520/output.js new file mode 100644 index 00000000000..cac93512c9a --- /dev/null +++ b/test/input/issue-520/output.js @@ -0,0 +1,2 @@ +console.log(3); +//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbInN0ZGluIl0sInNvdXJjZXNDb250ZW50IjpbImNsYXNzIEZvbyB7IGNvbnN0cnVjdG9yKCl7Y29uc29sZS5sb2coMSsyKTt9IH0gbmV3IEZvbygpO1xuIl0sIm5hbWVzIjpbImNvbnNvbGUiLCJsb2ciXSwibWFwcGluZ3MiOiJBQUEwQkEsUUFBUUMsSUFBSSxDQUFHIn0= diff --git a/test/input/module/expect.js b/test/input/module/expect.js new file mode 100644 index 00000000000..01f3aeed671 --- /dev/null +++ b/test/input/module/expect.js @@ -0,0 +1 @@ +function n(){return this||arguments[0]+arguments[1]}function o(){return this||arguments[0]+arguments[1]}console.log(n(n(1,3),5)),console.log(o(o(2,4),6)); diff --git a/test/input/module/input.js b/test/input/module/input.js new file mode 100644 index 00000000000..9ee80225073 --- /dev/null +++ b/test/input/module/input.js @@ -0,0 +1,13 @@ +console.log(function() { + function sum(...params) { + return this || arguments[0] + arguments[1]; + } + return sum(sum(1, 3), 5); +}()); +console.log(function() { + "use strict"; + function sum(...params) { + return this || arguments[0] + arguments[1]; + } + return sum(sum(2, 4), 6); +}()); \ No newline at end of file diff --git a/test/input/reduce/destructured_assign.js b/test/input/reduce/destructured_assign.js new file mode 100644 index 00000000000..26e1580bc59 --- /dev/null +++ b/test/input/reduce/destructured_assign.js @@ -0,0 +1,3 @@ +var o = {}; +[ o[1 + .1 + .1] ] = [ 42 ]; +console.log(o[1.2]); diff --git a/test/input/reduce/destructured_assign.reduced.js b/test/input/reduce/destructured_assign.reduced.js new file mode 100644 index 00000000000..887810d4e06 --- /dev/null +++ b/test/input/reduce/destructured_assign.reduced.js @@ -0,0 +1,17 @@ +// (beautified) +var o = {}; + +[ o[1 + .1 + .1] ] = []; + +console.log(o); +// output: { '1.2000000000000002': undefined } +// +// minify: { '1.2': undefined } +// +// options: { +// "compress": { +// "unsafe_math": true +// }, +// "mangle": false, +// "validate": true +// } \ No newline at end of file diff --git a/test/input/reduce/destructured_catch.js b/test/input/reduce/destructured_catch.js new file mode 100644 index 00000000000..3b2484552be --- /dev/null +++ b/test/input/reduce/destructured_catch.js @@ -0,0 +1,7 @@ +try { + "foo" in 42; +} catch ({ + message, +}) { + console.log(message); +} diff --git a/test/input/reduce/destructured_catch.reduced.js b/test/input/reduce/destructured_catch.reduced.js new file mode 100644 index 00000000000..1979b99db7c --- /dev/null +++ b/test/input/reduce/destructured_catch.reduced.js @@ -0,0 +1,13 @@ +// (beautified) +try { + 1 in 0; +} catch (message) { + console.log(message); +} +// output: TypeError: Cannot use 'in' operator to search for '1' in 0 +// +// minify: TypeError: Cannot use 'in' operator to search for '0' in 0 +// +// options: { +// "mangle": false +// } \ No newline at end of file diff --git a/test/input/reduce/diff_error.js b/test/input/reduce/diff_error.js new file mode 100644 index 00000000000..467ee599d57 --- /dev/null +++ b/test/input/reduce/diff_error.js @@ -0,0 +1,5 @@ +console.log(function(undefined) { + return undefined[function() { + {} + }] || 1 + .1 + .1; +}(42)); diff --git a/test/input/reduce/diff_error.reduced.js b/test/input/reduce/diff_error.reduced.js new file mode 100644 index 00000000000..4fed66efd24 --- /dev/null +++ b/test/input/reduce/diff_error.reduced.js @@ -0,0 +1,12 @@ +// (beautified) +console.log(1 + .1 + .1); +// output: 1.2000000000000002 +// +// minify: 1.2 +// +// options: { +// "compress": { +// "unsafe_math": true +// }, +// "mangle": false +// } \ No newline at end of file diff --git a/test/input/reduce/export_default.js b/test/input/reduce/export_default.js new file mode 100644 index 00000000000..d7c063c6205 --- /dev/null +++ b/test/input/reduce/export_default.js @@ -0,0 +1,17 @@ +var unused; +export default class { + ____11111() { + a, b, c, d, e; + f, g, h, i, j; + k, l, m, n, o; + p, q, r, s, t; + u, v, w, x, y, z; + A, B, C, D, E; + F, G, H, I, J; + K, L, M, N, O; + P, Q, R, S, T; + U, V, W, X, Y, Z; + $, _; + unused; + } +} diff --git a/test/input/reduce/label.js b/test/input/reduce/label.js new file mode 100644 index 00000000000..5b0f059322d --- /dev/null +++ b/test/input/reduce/label.js @@ -0,0 +1,3 @@ +UNUSED: { + console.log(0 - .1 - .1 - .1); +} diff --git a/test/input/reduce/label.reduced.js b/test/input/reduce/label.reduced.js new file mode 100644 index 00000000000..ba4b56c9b40 --- /dev/null +++ b/test/input/reduce/label.reduced.js @@ -0,0 +1,12 @@ +// (beautified) +console.log(0 - 1 - .1 - .1); +// output: -1.2000000000000002 +// +// minify: -1.2 +// +// options: { +// "compress": { +// "unsafe_math": true +// }, +// "mangle": false +// } \ No newline at end of file diff --git a/test/input/reduce/setter.js b/test/input/reduce/setter.js new file mode 100644 index 00000000000..61ba5952a2c --- /dev/null +++ b/test/input/reduce/setter.js @@ -0,0 +1,5 @@ +({ + set p(v) { + console.log(+v + .1 + .1); + } +}).p = 1; diff --git a/test/input/reduce/setter.reduced.js b/test/input/reduce/setter.reduced.js new file mode 100644 index 00000000000..68d5f366aaa --- /dev/null +++ b/test/input/reduce/setter.reduced.js @@ -0,0 +1,16 @@ +// (beautified) +({ + set p(v) { + console.log(1 + .1 + .1); + } +}).p = 0; +// output: 1.2000000000000002 +// +// minify: 1.2 +// +// options: { +// "compress": { +// "unsafe_math": true +// }, +// "mangle": false +// } \ No newline at end of file diff --git a/test/input/reduce/unsafe_math.js b/test/input/reduce/unsafe_math.js new file mode 100644 index 00000000000..b99ffd0e3be --- /dev/null +++ b/test/input/reduce/unsafe_math.js @@ -0,0 +1,18 @@ +var _calls_ = 10, a = 100, b = 10, c = 0; + +function f0(b_1, a, undefined_2) { + a++ + ++b; + { + var expr2 = (b + 1 - .1 - .1 - .1 || a || 3).toString(); + L20778: for (var key2 in expr2) { + (c = c + 1) + [ --b + b_1, typeof f0 == "function" && --_calls_ >= 0 && f0(--b + typeof (undefined_2 = 1 === 1 ? a : b), --b + { + c: (c = c + 1) + null + }, a++ + (typeof f0 == "function" && --_calls_ >= 0 && f0(typeof (c = 1 + c, 3 / "a" * ("c" >>> 23..toString()) >= (b_1 && (b_1[(c = c + 1) + a--] = (- -0, + true + {})))), 3, 25))), 1 === 1 ? a : b ]; + } + } +} + +var a_1 = f0([ , 0 ].length === 2); + +console.log(null, a, b, c, Infinity, NaN, undefined); diff --git a/test/input/reduce/unsafe_math.reduced.js b/test/input/reduce/unsafe_math.reduced.js new file mode 100644 index 00000000000..d0252d59d56 --- /dev/null +++ b/test/input/reduce/unsafe_math.reduced.js @@ -0,0 +1,14 @@ +// (beautified) +var expr2 = (0 - 1 - .1 - .1).toString(); + +console.log(expr2); +// output: -1.2000000000000002 +// +// minify: -1.2 +// +// options: { +// "compress": { +// "unsafe_math": true +// }, +// "mangle": false +// } \ No newline at end of file diff --git a/test/input/rename/input.js b/test/input/rename/input.js new file mode 100644 index 00000000000..7aff03a56d4 --- /dev/null +++ b/test/input/rename/input.js @@ -0,0 +1,6 @@ +function f(x) { + return g(x); + function g(x) { + return x + x; + } +} diff --git a/test/jetstream.js b/test/jetstream.js new file mode 100644 index 00000000000..f5d91a13758 --- /dev/null +++ b/test/jetstream.js @@ -0,0 +1,132 @@ +#! /usr/bin/env node +// -*- js -*- + +"use strict"; + +var site = "https://browserbench.org/JetStream1.1"; +if (typeof phantom == "undefined") { + require("../tools/tty"); + var args = process.argv.slice(2); + var debug = args.indexOf("--debug"); + if (debug < 0) { + debug = false; + } else { + args.splice(debug, 1); + debug = true; + } + if (!args.length) args.push("-mcO", "webkit"); + args.unshift("bin/uglifyjs"); + args.push("--validate", "--timings"); + var child_process = require("child_process"); + var fetch = require("./fetch"); + var http = require("http"); + var server = http.createServer(function(request, response) { + request.resume(); + var url = site + request.url; + fetch(url, function(err, res) { + if (err) { + if (typeof err != "number") throw err; + response.writeHead(err); + response.end(); + } else { + response.writeHead(200, { + "Content-Type": { + css: "text/css", + js: "application/javascript", + png: "image/png" + }[url.slice(url.lastIndexOf(".") + 1)] || "text/html; charset=utf-8" + }); + if (/\.js$/.test(url)) { + var stderr = ""; + var uglifyjs = child_process.spawn(process.argv[0], args, { + silent: true + }).on("exit", function(code) { + console.log("uglifyjs", url.slice(site.length + 1), args.slice(1).join(" ")); + console.log(stderr); + if (code) throw new Error("uglifyjs failed with code " + code); + }); + uglifyjs.stderr.on("data", function(data) { + stderr += data; + }).setEncoding("utf8"); + uglifyjs.stdout.pipe(response); + res.pipe(uglifyjs.stdin); + } else { + res.pipe(response); + } + } + }); + }).listen(); + server.on("listening", function() { + var port = server.address().port; + if (debug) return console.log("http://localhost:" + port + "/"); + var cmd = process.platform == "win32" ? "npm.cmd" : "npm"; + + function npm(args, done) { + args.push("--loglevel=error"); + child_process.spawn(cmd, args, { stdio: [ "ignore", 1, 2 ] }).on("exit", done); + } + + (function install() { + npm([ + "install", + "graceful-fs@4.2.6", + "is-my-json-valid@2.20.5", + "phantomjs-prebuilt@2.1.14", + "--no-audit", + "--no-fund", + "--no-optional", + "--no-save", + "--no-strict-ssl", + "--no-update-notifier", + "--production", + ], function(code) { + if (code) { + console.log("npm install failed with code", code); + return npm([ + "cache", + "clean", + "--force", + ], install); + } + var program = require("phantomjs-prebuilt").exec(process.argv[1], port); + program.stdout.pipe(process.stdout); + program.stderr.pipe(process.stderr); + program.on("exit", function(code) { + server.close(); + if (code) throw new Error("JetStream failed!"); + console.log("JetStream completed successfully."); + process.exit(0); + }); + }); + })(); + }); + server.timeout = 0; +} else { + var page = require("webpage").create(); + page.onError = function(msg, trace) { + var body = [ msg ]; + if (trace) trace.forEach(function(t) { + body.push(" " + (t.function || "Anonymous function") + " (" + t.file + ":" + t.line + ")"); + }); + console.error(body.join("\n")); + phantom.exit(1); + }; + var url = "http://localhost:" + require("system").args[1] + "/"; + page.onConsoleMessage = function(msg) { + if (/Error:/i.test(msg)) { + console.error(msg); + phantom.exit(1); + } + console.log(msg); + if (~msg.indexOf("Raw results:")) { + phantom.exit(); + } + }; + page.open(url, function(status) { + if (status != "success") phantom.exit(1); + page.evaluate(function() { + JetStream.switchToQuick(); + JetStream.start(); + }); + }); +} diff --git a/test/mocha.js b/test/mocha.js new file mode 100644 index 00000000000..96edde981a5 --- /dev/null +++ b/test/mocha.js @@ -0,0 +1,117 @@ +var fs = require("fs"); + +var config = { + timeout: function(limit) { + this.limit = limit + lag; + }, +}; +var lag = +process.env["UGLIFY_GITHUB_LAG"] || 0; +var tasks = []; +var titles = []; +config.timeout(10000); +describe = function(title, fn) { + config = Object.create(config); + titles.push(title); + fn.call(config); + titles.pop(); + config = Object.getPrototypeOf(config); +}; +it = function(title, fn) { + fn.limit = config.limit; + fn.titles = titles.slice(); + fn.titles.push(title); + tasks.push(fn); +}; +(function(arg) { + return arg ? [ arg ] : fs.readdirSync("test/mocha").filter(function(file) { + return /\.js$/.test(file); + }); +})(process.argv[2]).forEach(function(file) { + require("./mocha/" + file); +}); + +function log_titles(log, current, marker) { + var indent = ""; + var writing = false; + for (var i = 0; i < current.length; i++, indent += " ") { + if (titles[i] != current[i]) writing = true; + if (writing) log(indent + (i == current.length - 1 && marker || "") + current[i]); + } + titles = current; +} + +function red(text) { + return "\u001B[31m" + text + "\u001B[39m"; +} + +function green(text) { + return "\u001B[32m" + text + "\u001B[39m"; +} + +var errors = []; +var total = tasks.length; +titles = []; +process.nextTick(function run() { + var task = tasks.shift(); + if (task) try { + var elapsed = Date.now(); + var timer; + var done = function() { + elapsed = Date.now() - elapsed; + if (elapsed > task.limit) { + throw new Error("Timed out: " + elapsed + "ms > " + task.limit + "ms"); + } + reset(); + log_titles(console.log, task.titles, green('\u221A ')); + process.nextTick(run); + }; + if (task.length) { + task.timeout = function(limit) { + clearTimeout(timer); + limit += lag; + task.limit = limit; + timer = setTimeout(function() { + raise(new Error("Timed out: exceeds " + limit + "ms")); + }, limit); + }; + task.timeout(task.limit - lag); + process.on("uncaughtException", raise); + task.call(task, done); + } else { + task.timeout = config.timeout; + task.call(task); + done(); + } + } catch (err) { + raise(err); + } else if (errors.length) { + console.error(); + console.log(red(errors.length + " test(s) failed!")); + titles = []; + errors.forEach(function(titles, index) { + console.error(); + log_titles(console.error, titles, (index + 1) + ") "); + var lines = titles.error.stack.split('\n'); + console.error(red(lines[0])); + console.error(lines.slice(1).join("\n")); + }); + process.exit(1); + } else { + console.log(); + console.log(green(total + " test(s) passed.")); + } + + function raise(err) { + reset(); + task.titles.error = err; + errors.push(task.titles); + log_titles(console.log, task.titles, red('\u00D7 ')); + process.nextTick(run); + } + + function reset() { + clearTimeout(timer); + done = function() {}; + process.removeListener("uncaughtException", raise); + } +}); diff --git a/test/mocha/arguments.js b/test/mocha/arguments.js new file mode 100644 index 00000000000..6970853c69e --- /dev/null +++ b/test/mocha/arguments.js @@ -0,0 +1,27 @@ +var assert = require("assert"); +var UglifyJS = require("../.."); + +describe("arguments", function() { + it("Should known that arguments in functions are local scoped", function() { + var ast = UglifyJS.parse("var arguments; var f = function() {arguments.length}"); + ast.figure_out_scope(); + // Test scope of `var arguments` + assert.strictEqual(ast.find_variable("arguments").global, true); + // Select arguments symbol in function + var symbol = ast.body[1].definitions[0].value.find_variable("arguments"); + assert.strictEqual(symbol.global, false); + assert.strictEqual(symbol.scope, ast. // From ast + body[1]. // Select 2nd statement (equals to `var f ...`) + definitions[0]. // First definition of selected statement + value // Select function as scope + ); + }); + + it("Should recognize when a function uses arguments", function() { + var ast = UglifyJS.parse("function a(){function b(){function c(){}; return arguments[0];}}"); + ast.figure_out_scope(); + assert.strictEqual(ast.body[0].uses_arguments, false); + assert.strictEqual(ast.body[0].body[0].uses_arguments, true); + assert.strictEqual(ast.body[0].body[0].body[0].uses_arguments, false); + }); +}); diff --git a/test/mocha/awaits.js b/test/mocha/awaits.js new file mode 100644 index 00000000000..f25c9f254e6 --- /dev/null +++ b/test/mocha/awaits.js @@ -0,0 +1,65 @@ +var assert = require("assert"); +var UglifyJS = require("../node"); + +describe("async", function() { + it("Should reject `await` as symbol name within async functions only", function() { + [ + "function await() {}", + "function(await) {}", + "function() { await; }", + "function() { await: {} }", + "function() { var await; }", + "function() { function await() {} }", + "function() { try {} catch (await) {} }", + ].forEach(function(code) { + var ast = UglifyJS.parse("(" + code + ")();"); + assert.strictEqual(ast.TYPE, "Toplevel"); + assert.strictEqual(ast.body.length, 1); + assert.strictEqual(ast.body[0].TYPE, "SimpleStatement"); + assert.strictEqual(ast.body[0].body.TYPE, "Call"); + assert.strictEqual(ast.body[0].body.expression.TYPE, "Function"); + assert.throws(function() { + UglifyJS.parse("(async " + code + ")();"); + }, function(e) { + return e instanceof UglifyJS.JS_Parse_Error; + }, code); + }); + }); + it("Should reject `await` expression outside of async functions", function() { + [ + "await 42;", + "function f() { await 42; }", + "async function f() { function g() { await 42; } }", + ].forEach(function(code) { + assert.throws(function() { + UglifyJS.parse(code); + }, function(e) { + return e instanceof UglifyJS.JS_Parse_Error; + }, code); + }); + }); + it("Should reject `await` expression directly on computed key of function argument", function() { + [ + "function f({ [await 42]: a }) {}", + "async function f({ [await 42]: a }) {}", + ].forEach(function(code) { + assert.throws(function() { + UglifyJS.parse(code); + }, function(e) { + return e instanceof UglifyJS.JS_Parse_Error; + }, code); + }); + }); + it("Should accept `await` expression nested within computed key of function argument", function() { + [ + "function f({ [async function() { await 42; }()]: a }) {}", + "async function f({ [async function() { await 42; }()]: a }) {}", + ].forEach(function(code) { + var ast = UglifyJS.parse(code); + assert.strictEqual(ast.TYPE, "Toplevel"); + assert.strictEqual(ast.body.length, 1); + assert.strictEqual(ast.body[0].argnames.length, 1); + assert.strictEqual(ast.body[0].argnames[0].TYPE, "DestructuredObject"); + }); + }); +}); diff --git a/test/mocha/bug-report.js b/test/mocha/bug-report.js new file mode 100644 index 00000000000..480850df943 --- /dev/null +++ b/test/mocha/bug-report.js @@ -0,0 +1,40 @@ +var assert = require("assert"); +var exec = require("child_process").exec; + +describe("UGLIFY_BUG_REPORT", function() { + var env = Object.create(process.env); + env.UGLIFY_BUG_REPORT = 1; + it("Should generate bug report via API", function(done) { + exec('"' + process.argv[0] + '"', { env: env }, function(err, stdout) { + if (err) throw err; + assert.strictEqual(stdout, [ + "// UGLIFY_BUG_REPORT", + "// <>", + "", + "//-------------------------------------------------------------", + "// INPUT CODE", + "...---...", + "", + ].join("\n")); + done(); + }).stdin.end('console.log(require("./").minify("...---...").code);'); + }); + it("Should generate bug report via CLI", function(done) { + exec('"' + process.argv[0] + '" bin/uglifyjs -mc', { env: env }, function(err, stdout) { + if (err) throw err; + assert.strictEqual(stdout, [ + "// UGLIFY_BUG_REPORT", + "// {", + '// "mangle": {},', + '// "compress": {}', + "// }", + "", + "//-------------------------------------------------------------", + "// STDIN", + "...---...", + "", + ].join("\n")); + done(); + }).stdin.end("...---..."); + }); +}); diff --git a/test/mocha/cli.js b/test/mocha/cli.js new file mode 100644 index 00000000000..2e7f8fe517f --- /dev/null +++ b/test/mocha/cli.js @@ -0,0 +1,1076 @@ +var assert = require("assert"); +var exec = require("child_process").exec; +var fs = require("fs"); +var run_code = require("../sandbox").run_code; +var semver = require("semver"); +var to_ascii = require("../node").to_ascii; + +function read(path) { + return fs.readFileSync(path, "utf8"); +} + +describe("bin/uglifyjs", function() { + var uglifyjscmd = '"' + process.argv[0] + '" bin/uglifyjs'; + it("Should produce a functional build when using --self", function(done) { + this.timeout(30000); + var command = [ + uglifyjscmd, + "--self", + semver.satisfies(process.version, "<=0.12") ? "-mc hoist_funs" : "-mc", + "--wrap WrappedUglifyJS", + ].join(" "); + exec(command, { maxBuffer: 1048576 }, function(err, stdout) { + if (err) throw err; + eval(stdout); + assert.strictEqual(typeof WrappedUglifyJS, "object"); + var result = WrappedUglifyJS.minify("foo([true,,2+3]);"); + assert.strictEqual(result.error, undefined); + assert.strictEqual(result.code, "foo([!0,,5]);"); + done(); + }); + }); + it("Should be able to filter comments correctly with `--comments all`", function(done) { + var command = uglifyjscmd + ' test/input/comments/filter.js --comments all'; + exec(command, function(err, stdout) { + if (err) throw err; + assert.strictEqual(stdout, "// foo\n/*@preserve*/\n// bar\n"); + done(); + }); + }); + it("Should be able to filter comments correctly with `--comment `", function(done) { + var command = uglifyjscmd + ' test/input/comments/filter.js --comments /r/'; + exec(command, function(err, stdout) { + if (err) throw err; + assert.strictEqual(stdout, "/*@preserve*/\n// bar\n"); + done(); + }); + }); + it("Should be able to filter comments correctly with just `--comment`", function(done) { + var command = uglifyjscmd + ' test/input/comments/filter.js --comments'; + exec(command, function(err, stdout) { + if (err) throw err; + assert.strictEqual(stdout, "/*@preserve*/\n"); + done(); + }); + }); + it("Should work with --expression", function(done) { + exec([ + uglifyjscmd, + "--expression", + "--compress", + "--mangle", + ].join(" "), function(err, stdout) { + if (err) throw err; + assert.strictEqual(stdout, "function(n){for(;n(););return 42}(A)\n"); + done(); + }).stdin.end([ + "function(x) {", + " while (x()) {}", + " return 42;", + "}(A)", + ].join("\n")); + }); + it("Should work with --source-map names=true", function(done) { + exec([ + uglifyjscmd, + "--beautify", + "--source-map", [ + "names=true", + "url=inline", + ].join(), + ].join(" "), function(err, stdout) { + if (err) throw err; + var expected = [ + "var obj = {", + " p: a,", + " q: b", + "};", + "//# sourceMappingURL=data:application/json;charset=utf-8;base64,", + ].join("\n"); + assert.strictEqual(stdout.slice(0, expected.length), expected); + var map = JSON.parse(to_ascii(stdout.slice(expected.length).trim())); + assert.deepEqual(map.names, [ "obj", "p", "a", "q", "b" ]); + done(); + }).stdin.end([ + "var obj = {", + " p: a,", + " q: b", + "};", + ].join("\n")); + }); + it("Should work with --source-map names=false", function(done) { + exec([ + uglifyjscmd, + "--beautify", + "--source-map", [ + "names=false", + "url=inline", + ].join(), + ].join(" "), function(err, stdout) { + if (err) throw err; + var expected = [ + "var obj = {", + " p: a,", + " q: b", + "};", + "//# sourceMappingURL=data:application/json;charset=utf-8;base64,", + ].join("\n"); + assert.strictEqual(stdout.slice(0, expected.length), expected); + var map = JSON.parse(to_ascii(stdout.slice(expected.length).trim())); + assert.deepEqual(map.names, []); + done(); + }).stdin.end([ + "var obj = {", + " p: a,", + " q: b", + "};", + ].join("\n")); + }); + it("Should give sensible error against invalid input source map", function(done) { + var command = uglifyjscmd + " test/mocha.js --source-map content=blah,url=inline --verbose"; + exec(command, function(err, stdout, stderr) { + assert.ok(err); + assert.deepEqual(stderr.split(/\n/).slice(0, 2), [ + "INFO: Using input source map: blah", + "ERROR: invalid input source map: blah", + ]); + done(); + }); + }); + it("Should append source map to output when using --source-map url=inline", function(done) { + var command = uglifyjscmd + " test/input/issue-1323/sample.js --source-map url=inline"; + exec(command, function(err, stdout) { + if (err) throw err; + assert.strictEqual(stdout, [ + "var bar=function(){function foo(bar){return bar}return foo}();", + "//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbInRlc3QvaW5wdXQvaXNzdWUtMTMyMy9zYW1wbGUuanMiXSwibmFtZXMiOlsiYmFyIiwiZm9vIl0sIm1hcHBpbmdzIjoiQUFBQSxJQUFJQSxJQUFNLFdBQ04sU0FBU0MsSUFBS0QsS0FDVixPQUFPQSxHQUNYLENBRUEsT0FBT0MsR0FDVixFQUFFIn0=", + "", + ].join("\n")); + done(); + }); + }); + it("Should not append source map to output when not using --source-map url=inline", function(done) { + var command = uglifyjscmd + ' test/input/issue-1323/sample.js'; + exec(command, function(err, stdout) { + if (err) throw err; + assert.strictEqual(stdout, "var bar=function(){function foo(bar){return bar}return foo}();\n"); + done(); + }); + }); + it("Should not consider source map file content as source map file name (issue #2082)", function(done) { + var command = [ + uglifyjscmd, + "test/input/issue-2082/sample.js", + "--source-map", "content=test/input/issue-2082/sample.js.map", + "--source-map", "url=inline", + "--verbose", + ].join(" "); + exec(command, function(err, stdout, stderr) { + if (err) throw err; + var stderrLines = stderr.split("\n"); + assert.strictEqual(stderrLines[0], "INFO: Using input source map: test/input/issue-2082/sample.js.map"); + assert.notStrictEqual(stderrLines[1], 'INFO: Using input source map: {"version": 3,"sources": ["index.js"],"mappings": ";"}'); + done(); + }); + }); + it("Should not load source map before finish reading from STDIN", function(done) { + var mapFile = "tmp/input.js.map"; + try { + fs.mkdirSync("./tmp"); + } catch (e) { + if (e.code != "EEXIST") throw e; + } + try { + fs.unlinkSync(mapFile); + } catch (e) { + if (e.code != "ENOENT") throw e; + } + var command = [ + uglifyjscmd, + "--beautify", + "--source-map", [ + "content=" + mapFile, + "includeSources", + "url=inline", + ].join(), + ].join(" "); + + var child = exec(command, function(err, stdout) { + if (err) throw err; + assert.strictEqual(stdout, read("test/input/issue-3040/expect.js")); + done(); + }); + setTimeout(function() { + fs.writeFileSync(mapFile, read("test/input/issue-3040/input.js.map")); + child.stdin.end(read("test/input/issue-3040/input.js")); + }, 1000); + }); + it("Should work with --keep-fargs (mangle only)", function(done) { + var command = [ + uglifyjscmd, + "test/input/issue-1431/sample.js", + "--keep-fargs", + "--mangle", + "--no-module", + ].join(" "); + exec(command, function(err, stdout) { + if (err) throw err; + assert.strictEqual(stdout, "function f(x){return function(){function n(a){return a*a}return x(n)}}function g(op){return op(1)+op(2)}console.log(f(g)()==5);\n"); + done(); + }); + }); + it("Should work with --keep-fargs (mangle & compress)", function(done) { + var command = [ + uglifyjscmd, + "test/input/issue-1431/sample.js", + "--keep-fargs", + "--mangle", + "--no-module", + "--compress", + ].join(" "); + exec(command, function(err, stdout) { + if (err) throw err; + assert.strictEqual(stdout, "function f(x){return function(){return x(function(a){return a*a})}}function g(op){return op(1)+op(2)}console.log(5==f(g)());\n"); + done(); + }); + }); + it("Should work with keep_fargs under mangler options", function(done) { + var command = [ + uglifyjscmd, + "test/input/issue-1431/sample.js", + "--mangle", "keep_fargs=true", + "--no-module", + ].join(" "); + exec(command, function(err, stdout) { + if (err) throw err; + assert.strictEqual(stdout, "function f(x){return function(){function n(a){return a*a}return x(n)}}function g(op){return op(1)+op(2)}console.log(f(g)()==5);\n"); + done(); + }); + }); + it("Should work with --keep-fnames (mangle only)", function(done) { + var command = [ + uglifyjscmd, + "test/input/issue-1431/sample.js", + "--keep-fnames", + "--mangle", + ].join(" "); + exec(command, function(err, stdout) { + if (err) throw err; + assert.strictEqual(stdout, "function f(r){return function(){function n(n){return n*n}return r(n)}}function g(n){return n(1)+n(2)}console.log(f(g)()==5);\n"); + done(); + }); + }); + it("Should work with --keep-fnames (mangle & compress)", function(done) { + var command = [ + uglifyjscmd, + "test/input/issue-1431/sample.js", + "--keep-fnames", + "--mangle", + "--no-module", + "--compress", + ].join(" "); + exec(command, function(err, stdout) { + if (err) throw err; + assert.strictEqual(stdout, "function f(n){return function(){return n(function n(r){return r*r})}}function g(n){return n(1)+n(2)}console.log(5==f(g)());\n"); + done(); + }); + }); + it("Should work with keep_fnames under mangler options", function(done) { + var command = uglifyjscmd + ' test/input/issue-1431/sample.js -m keep_fnames=true'; + exec(command, function(err, stdout) { + if (err) throw err; + assert.strictEqual(stdout, "function f(r){return function(){function n(n){return n*n}return r(n)}}function g(n){return n(1)+n(2)}console.log(f(g)()==5);\n"); + done(); + }); + }); + it("Should work with --define (simple)", function(done) { + var command = uglifyjscmd + ' test/input/global_defs/simple.js --define D=5 -c'; + exec(command, function(err, stdout) { + if (err) throw err; + assert.strictEqual(stdout, "console.log(5);\n"); + done(); + }); + }); + it("Should work with --define (nested)", function(done) { + var command = uglifyjscmd + ' test/input/global_defs/nested.js --define C.D=5,C.V=3 -c'; + exec(command, function(err, stdout) { + if (err) throw err; + assert.strictEqual(stdout, "console.log(3,5);\n"); + done(); + }); + }); + it("Should work with --define (AST_Node)", function(done) { + var command = uglifyjscmd + ' test/input/global_defs/simple.js --define console.log=stdout.println -c'; + exec(command, function(err, stdout) { + if (err) throw err; + assert.strictEqual(stdout, "stdout.println(D);\n"); + done(); + }); + }); + it("Should work with `--beautify`", function(done) { + var command = uglifyjscmd + ' test/input/issue-1482/input.js -b'; + exec(command, function(err, stdout) { + if (err) throw err; + assert.strictEqual(stdout, read("test/input/issue-1482/beautify.js")); + done(); + }); + }); + it("Should work with `--beautify braces`", function(done) { + var command = uglifyjscmd + ' test/input/issue-1482/input.js -b braces'; + exec(command, function(err, stdout) { + if (err) throw err; + assert.strictEqual(stdout, read("test/input/issue-1482/braces.js")); + done(); + }); + }); + it("Should work with `--output-opts`", function(done) { + var command = uglifyjscmd + ' test/input/issue-1482/input.js -O ascii_only'; + exec(command, function(err, stdout) { + if (err) throw err; + assert.strictEqual(stdout, read("test/input/issue-1482/default.js")); + done(); + }); + }); + it("Should fail when both --beautify & --output-opts are specified", function(done) { + var command = uglifyjscmd + " test/input/issue-520/input.js -bO ascii_only"; + exec(command, function(err, stdout, stderr) { + assert.ok(err); + assert.strictEqual(stderr, "ERROR: --beautify cannot be used with --output-opts\n"); + done(); + }); + }); + it("Should process inline source map", function(done) { + var command = [ + uglifyjscmd, + "test/input/issue-520/input.js", + "-mc", "toplevel", + "--source-map", "content=inline", + "--source-map", "includeSources=true", + "--source-map", "url=inline", + ].join(" "); + exec(command, function(err, stdout) { + if (err) throw err; + assert.strictEqual(stdout, read("test/input/issue-520/output.js")); + done(); + }); + }); + it("Should warn for missing inline source map", function(done) { + var command = uglifyjscmd + " test/input/issue-1323/sample.js --source-map content=inline,url=inline --warn"; + exec(command, function(err, stdout, stderr) { + if (err) throw err; + assert.strictEqual(stdout, [ + "var bar=function(){function foo(bar){return bar}return foo}();", + "//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbInRlc3QvaW5wdXQvaXNzdWUtMTMyMy9zYW1wbGUuanMiXSwibmFtZXMiOlsiYmFyIiwiZm9vIl0sIm1hcHBpbmdzIjoiQUFBQSxJQUFJQSxJQUFNLFdBQ04sU0FBU0MsSUFBS0QsS0FDVixPQUFPQSxHQUNYLENBRUEsT0FBT0MsR0FDVixFQUFFIn0=", + "", + ].join("\n")); + var stderrLines = stderr.split("\n"); + assert.strictEqual(stderrLines[0], "WARN: inline source map not found: test/input/issue-1323/sample.js"); + done(); + }); + }); + it("Should handle multiple input and inline source map", function(done) { + var command = [ + uglifyjscmd, + "test/input/issue-520/input.js", + "test/input/issue-1323/sample.js", + "--source-map", "content=inline,url=inline", + "--warn", + ].join(" "); + exec(command, function(err, stdout, stderr) { + if (err) throw err; + assert.strictEqual(stdout, [ + "var Foo=function Foo(){console.log(1+2)};new Foo;var bar=function(){function foo(bar){return bar}return foo}();", + "//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbInN0ZGluIiwidGVzdC9pbnB1dC9pc3N1ZS0xMzIzL3NhbXBsZS5qcyJdLCJuYW1lcyI6WyJGb28iLCJjb25zb2xlIiwibG9nIiwiYmFyIiwiZm9vIl0sIm1hcHBpbmdzIjoiQUFBQSxJQUFNQSxJQUFJLFNBQUVBLE1BQWNDLFFBQVFDLElBQUksRUFBRSxDQUFDLENBQUUsRUFBSSxJQUFJRixJQ0FuRCxJQUFJRyxJQUFNLFdBQ04sU0FBU0MsSUFBS0QsS0FDVixPQUFPQSxHQUNYLENBRUEsT0FBT0MsR0FDVixFQUFFIn0=", + "", + ].join("\n")); + var stderrLines = stderr.split("\n"); + assert.strictEqual(stderrLines[0], "WARN: inline source map not found: test/input/issue-1323/sample.js"); + done(); + }); + }); + it("Should fail with acorn and inline source map", function(done) { + var command = uglifyjscmd + " test/input/issue-520/input.js --source-map content=inline,url=inline -p acorn"; + exec(command, function(err, stdout, stderr) { + assert.ok(err); + assert.strictEqual(stderr, "ERROR: inline source map only works with built-in parser\n"); + done(); + }); + }); + it("Should fail with SpiderMonkey and inline source map", function(done) { + var command = uglifyjscmd + " test/input/issue-520/input.js --source-map content=inline,url=inline -p spidermonkey"; + exec(command, function(err, stdout, stderr) { + assert.ok(err); + assert.strictEqual(stderr, "ERROR: inline source map only works with built-in parser\n"); + done(); + }); + }); + it("Should fail with invalid syntax", function(done) { + var command = uglifyjscmd + " test/input/invalid/simple.js"; + exec(command, function(err, stdout, stderr) { + assert.ok(err); + assert.strictEqual(stdout, ""); + assert.strictEqual(stderr.split(/\n/).slice(0, 4).join("\n"), [ + "Parse error at test/input/invalid/simple.js:1,12", + "function f(a{}", + " ^", + "ERROR: Unexpected token: punc «{», expected: punc «,»", + ].join("\n")); + done(); + }); + }); + it("Should fail with correct marking of tabs", function(done) { + var command = uglifyjscmd + " test/input/invalid/tab.js"; + exec(command, function(err, stdout, stderr) { + assert.ok(err); + assert.strictEqual(stdout, ""); + assert.strictEqual(stderr.split(/\n/).slice(0, 4).join("\n"), [ + "Parse error at test/input/invalid/tab.js:1,12", + "\t\tfoo(\txyz, 0abc);", + "\t\t \t ^", + "ERROR: Invalid syntax: 0abc", + ].join("\n")); + done(); + }); + }); + it("Should fail with correct marking at start of line", function(done) { + var command = uglifyjscmd + " test/input/invalid/eof.js"; + exec(command, function(err, stdout, stderr) { + assert.ok(err); + assert.strictEqual(stdout, ""); + assert.strictEqual(stderr.split(/\n/).slice(0, 4).join("\n"), [ + "Parse error at test/input/invalid/eof.js:2,0", + "foo, bar(", + " ^", + "ERROR: Unexpected token: eof", + ].join("\n")); + done(); + }); + }); + it("Should fail with a missing loop body", function(done) { + var command = uglifyjscmd + " test/input/invalid/loop-no-body.js"; + exec(command, function(err, stdout, stderr) { + assert.ok(err); + assert.strictEqual(stdout, ""); + assert.strictEqual(stderr.split(/\n/).slice(0, 4).join("\n"), [ + "Parse error at test/input/invalid/loop-no-body.js:2,0", + "for (var i = 0; i < 1; i++) ", + " ^", + "ERROR: Unexpected token: eof", + ].join("\n")); + done(); + }); + }); + it("Should throw syntax error (5--)", function(done) { + var command = uglifyjscmd + " test/input/invalid/assign_1.js"; + exec(command, function(err, stdout, stderr) { + assert.ok(err); + assert.strictEqual(stdout, ""); + assert.strictEqual(stderr.split(/\n/).slice(0, 4).join("\n"), [ + "Parse error at test/input/invalid/assign_1.js:1,18", + "console.log(1 || 5--);", + " ^", + "ERROR: Invalid use of -- operator", + ].join("\n")); + done(); + }); + }); + it("Should throw syntax error (Math.random() /= 2)", function(done) { + var command = uglifyjscmd + " test/input/invalid/assign_2.js"; + exec(command, function(err, stdout, stderr) { + assert.ok(err); + assert.strictEqual(stdout, ""); + assert.strictEqual(stderr.split(/\n/).slice(0, 4).join("\n"), [ + "Parse error at test/input/invalid/assign_2.js:1,32", + "console.log(2 || (Math.random() /= 2));", + " ^", + "ERROR: Invalid assignment", + ].join("\n")); + done(); + }); + }); + it("Should throw syntax error (++this)", function(done) { + var command = uglifyjscmd + " test/input/invalid/assign_3.js"; + exec(command, function(err, stdout, stderr) { + assert.ok(err); + assert.strictEqual(stdout, ""); + assert.strictEqual(stderr.split(/\n/).slice(0, 4).join("\n"), [ + "Parse error at test/input/invalid/assign_3.js:1,17", + "console.log(3 || ++this);", + " ^", + "ERROR: Invalid use of ++ operator", + ].join("\n")); + done(); + }); + }); + it("Should throw syntax error (null = 4)", function(done) { + var command = uglifyjscmd + " test/input/invalid/assign_4.js"; + exec(command, function(err, stdout, stderr) { + assert.ok(err); + assert.strictEqual(stdout, ""); + assert.strictEqual(stderr.split(/\n/).slice(0, 4).join("\n"), [ + "Parse error at test/input/invalid/assign_4.js:1,23", + "console.log(4 || (null = 4));", + " ^", + "ERROR: Invalid assignment", + ].join("\n")); + done(); + }); + }); + it("Should throw syntax error ([]?.length ^= 5)", function(done) { + var command = uglifyjscmd + " test/input/invalid/assign_5.js"; + exec(command, function(err, stdout, stderr) { + assert.ok(err); + assert.strictEqual(stdout, ""); + assert.strictEqual(stderr.split(/\n/).slice(0, 4).join("\n"), [ + "Parse error at test/input/invalid/assign_5.js:1,29", + "console.log(5 || ([]?.length ^= 5));", + " ^", + "ERROR: Invalid assignment", + ].join("\n")); + done(); + }); + }); + it("Should throw syntax error (a.=)", function(done) { + var command = uglifyjscmd + " test/input/invalid/dot_1.js"; + exec(command, function(err, stdout, stderr) { + assert.ok(err); + assert.strictEqual(stdout, ""); + assert.strictEqual(stderr.split(/\n/).slice(0, 4).join("\n"), [ + "Parse error at test/input/invalid/dot_1.js:1,2", + "a.=", + " ^", + "ERROR: Unexpected token: operator «=», expected: name", + ].join("\n")); + done(); + }); + }); + it("Should throw syntax error (%.a)", function(done) { + var command = uglifyjscmd + " test/input/invalid/dot_2.js"; + exec(command, function(err, stdout, stderr) { + assert.ok(err); + assert.strictEqual(stdout, ""); + assert.strictEqual(stderr.split(/\n/).slice(0, 4).join("\n"), [ + "Parse error at test/input/invalid/dot_2.js:1,0", + "%.a;", + "^", + "ERROR: Unexpected token: operator «%»", + ].join("\n")); + done(); + }); + }); + it("Should throw syntax error (a./();)", function(done) { + var command = uglifyjscmd + " test/input/invalid/dot_3.js"; + exec(command, function(err, stdout, stderr) { + assert.ok(err); + assert.strictEqual(stdout, ""); + assert.strictEqual(stderr.split(/\n/).slice(0, 4).join("\n"), [ + "Parse error at test/input/invalid/dot_3.js:1,2", + "a./();", + " ^", + "ERROR: Unexpected token: operator «/», expected: name", + ].join("\n")); + done(); + }); + }); + it("Should throw syntax error ({%: 1})", function(done) { + var command = uglifyjscmd + " test/input/invalid/object.js"; + exec(command, function(err, stdout, stderr) { + assert.ok(err); + assert.strictEqual(stdout, ""); + assert.strictEqual(stderr.split(/\n/).slice(0, 4).join("\n"), [ + "Parse error at test/input/invalid/object.js:1,13", + "console.log({%: 1});", + " ^", + "ERROR: Unexpected token: operator «%»", + ].join("\n")); + done(); + }); + }); + it("Should throw syntax error (delete x)", function(done) { + var command = [ + uglifyjscmd, + "test/input/invalid/delete.js", + "--no-module", + ].join(" "); + exec(command, function(err, stdout, stderr) { + assert.ok(err); + assert.strictEqual(stdout, ""); + assert.strictEqual(stderr.split(/\n/).slice(0, 4).join("\n"), [ + "Parse error at test/input/invalid/delete.js:13,11", + " delete x;", + " ^", + "ERROR: Calling delete on expression not allowed in strict mode", + ].join("\n")); + done(); + }); + }); + it("Should throw syntax error (function g(arguments))", function(done) { + var command = [ + uglifyjscmd, + "test/input/invalid/function_1.js", + "--no-module", + ].join(" "); + exec(command, function(err, stdout, stderr) { + assert.ok(err); + assert.strictEqual(stdout, ""); + assert.strictEqual(stderr.split(/\n/).slice(0, 4).join("\n"), [ + "Parse error at test/input/invalid/function_1.js:4,11", + "function g(arguments) {", + " ^", + "ERROR: Unexpected arguments in strict mode", + ].join("\n")); + done(); + }); + }); + it("Should throw syntax error (function eval())", function(done) { + var command = [ + uglifyjscmd, + "test/input/invalid/function_2.js", + "--no-module", + ].join(" "); + exec(command, function(err, stdout, stderr) { + assert.ok(err); + assert.strictEqual(stdout, ""); + assert.strictEqual(stderr.split(/\n/).slice(0, 4).join("\n"), [ + "Parse error at test/input/invalid/function_2.js:4,9", + "function eval() {", + " ^", + "ERROR: Unexpected eval in strict mode", + ].join("\n")); + done(); + }); + }); + it("Should throw syntax error (iife arguments())", function(done) { + var command = [ + uglifyjscmd, + "test/input/invalid/function_3.js", + "--no-module", + ].join(" "); + exec(command, function(err, stdout, stderr) { + assert.ok(err); + assert.strictEqual(stdout, ""); + assert.strictEqual(stderr.split(/\n/).slice(0, 4).join("\n"), [ + "Parse error at test/input/invalid/function_3.js:4,10", + "!function arguments() {", + " ^", + "ERROR: Unexpected arguments in strict mode", + ].join("\n")); + done(); + }); + }); + it("Should throw syntax error (catch (eval))", function(done) { + var command = [ + uglifyjscmd, + "test/input/invalid/try.js", + "--no-module", + ].join(" "); + exec(command, function(err, stdout, stderr) { + assert.ok(err); + assert.strictEqual(stdout, ""); + assert.strictEqual(stderr.split(/\n/).slice(0, 4).join("\n"), [ + "Parse error at test/input/invalid/try.js:7,18", + " try {} catch (eval) {}", + " ^", + "ERROR: Unexpected eval in strict mode", + ].join("\n")); + done(); + }); + }); + it("Should throw syntax error (var eval)", function(done) { + var command = [ + uglifyjscmd, + "test/input/invalid/var.js", + "--no-module", + ].join(" "); + exec(command, function(err, stdout, stderr) { + assert.ok(err); + assert.strictEqual(stdout, ""); + assert.strictEqual(stderr.split(/\n/).slice(0, 4).join("\n"), [ + "Parse error at test/input/invalid/var.js:7,8", + " var eval;", + " ^", + "ERROR: Unexpected eval in strict mode", + ].join("\n")); + done(); + }); + }); + it("Should throw syntax error (var { eval })", function(done) { + var command = [ + uglifyjscmd, + "test/input/invalid/destructured_var.js", + "--no-module", + ].join(" "); + exec(command, function(err, stdout, stderr) { + assert.ok(err); + assert.strictEqual(stdout, ""); + assert.strictEqual(stderr.split(/\n/).slice(0, 4).join("\n"), [ + "Parse error at test/input/invalid/destructured_var.js:7,10", + " var { eval } = 42;", + " ^", + "ERROR: Unexpected eval in strict mode", + ].join("\n")); + done(); + }); + }); + it("Should throw syntax error (else)", function(done) { + var command = uglifyjscmd + " test/input/invalid/else.js"; + exec(command, function(err, stdout, stderr) { + assert.ok(err); + assert.strictEqual(stdout, ""); + assert.strictEqual(stderr.split(/\n/).slice(0, 4).join("\n"), [ + "Parse error at test/input/invalid/else.js:1,7", + "if (0) else 1;", + " ^", + "ERROR: Unexpected token: keyword «else»", + ].join("\n")); + done(); + }); + }); + it("Should throw syntax error (return)", function(done) { + var command = uglifyjscmd + " test/input/invalid/return.js"; + exec(command, function(err, stdout, stderr) { + assert.ok(err); + assert.strictEqual(stdout, ""); + assert.strictEqual(stderr.split(/\n/).slice(0, 4).join("\n"), [ + "Parse error at test/input/invalid/return.js:1,0", + "return 42;", + "^", + "ERROR: 'return' outside of function", + ].join("\n")); + done(); + }); + }); + it("Should throw syntax error (for-in init)", function(done) { + var command = uglifyjscmd + " test/input/invalid/for-in_1.js"; + exec(command, function(err, stdout, stderr) { + assert.ok(err); + assert.strictEqual(stdout, ""); + assert.strictEqual(stderr.split(/\n/).slice(0, 4).join("\n"), [ + "Parse error at test/input/invalid/for-in_1.js:2,5", + "for (1, 2, a in b) {", + " ^", + "ERROR: Invalid left-hand side in for..in/of loop", + ].join("\n")); + done(); + }); + }); + it("Should throw syntax error (for-in var)", function(done) { + var command = uglifyjscmd + " test/input/invalid/for-in_2.js"; + exec(command, function(err, stdout, stderr) { + assert.ok(err); + assert.strictEqual(stdout, ""); + assert.strictEqual(stderr.split(/\n/).slice(0, 4).join("\n"), [ + "Parse error at test/input/invalid/for-in_2.js:2,5", + "for (var a, b in c) {", + " ^", + "ERROR: Only one variable declaration allowed in for..in/of loop", + ].join("\n")); + done(); + }); + }); + it("Should throw syntax error (for-of init)", function(done) { + var command = uglifyjscmd + " test/input/invalid/for-of_1.js"; + exec(command, function(err, stdout, stderr) { + assert.ok(err); + assert.strictEqual(stdout, ""); + assert.strictEqual(stderr.split(/\n/).slice(0, 4).join("\n"), [ + "Parse error at test/input/invalid/for-of_1.js:2,5", + "for (b = 2 of a)", + " ^", + "ERROR: Invalid left-hand side in for..in/of loop", + ].join("\n")); + done(); + }); + }); + it("Should throw syntax error (for-of var)", function(done) { + var command = uglifyjscmd + " test/input/invalid/for-of_2.js"; + exec(command, function(err, stdout, stderr) { + assert.ok(err); + assert.strictEqual(stdout, ""); + assert.strictEqual(stderr.split(/\n/).slice(0, 4).join("\n"), [ + "Parse error at test/input/invalid/for-of_2.js:2,13", + "for (var b = 2 of a)", + " ^", + "ERROR: No initializers allowed in for..of loop", + ].join("\n")); + done(); + }); + }); + it("Should throw syntax error (for-await)", function(done) { + var command = uglifyjscmd + " test/input/invalid/for-await.js"; + exec(command, function(err, stdout, stderr) { + assert.ok(err); + assert.strictEqual(stdout, ""); + assert.strictEqual(stderr.split(/\n/).slice(0, 4).join("\n"), [ + "Parse error at test/input/invalid/for-await.js:1,11", + "for await (; console.log(42););", + " ^", + "ERROR: Unexpected token: punc «;»", + ].join("\n")); + done(); + }); + }); + it("Should throw syntax error (switch defaults)", function(done) { + var command = uglifyjscmd + " test/input/invalid/switch.js"; + exec(command, function(err, stdout, stderr) { + assert.ok(err); + assert.strictEqual(stdout, ""); + assert.strictEqual(stderr.split(/\n/).slice(0, 4).join("\n"), [ + "Parse error at test/input/invalid/switch.js:3,2", + " default:", + " ^", + "ERROR: More than one default clause in switch statement", + ].join("\n")); + done(); + }); + }); + it("Should throw syntax error (console?.log``)", function(done) { + var command = uglifyjscmd + " test/input/invalid/optional-template.js"; + exec(command, function(err, stdout, stderr) { + assert.ok(err); + assert.strictEqual(stdout, ""); + assert.strictEqual(stderr.split(/\n/).slice(0, 4).join("\n"), [ + "Parse error at test/input/invalid/optional-template.js:1,12", + "console?.log``;", + " ^", + "ERROR: Invalid template on optional chain", + ].join("\n")); + done(); + }); + }); + it("Should handle literal string as source map input", function(done) { + var command = [ + uglifyjscmd, + "test/input/issue-1236/simple.js", + "--source-map", + 'content="' + read_map() + '",url=inline' + ].join(" "); + exec(command, function(err, stdout) { + if (err) throw err; + assert.strictEqual(stdout, [ + '"use strict";var foo=function foo(x){return"foo "+x};console.log(foo("bar"));', + "//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbImluZGV4LmpzIl0sIm5hbWVzIjpbImZvbyIsIngiLCJjb25zb2xlIiwibG9nIl0sIm1hcHBpbmdzIjoiYUFBQSxJQUFJQSxJQUFNLFNBQU5BLElBQU1DLEdBQUEsTUFBSyxPQUFTQSxDQUFkLEVBQ1ZDLFFBQVFDLElBQUlILElBQUksS0FBSixDQUFaIn0=", + "" + ].join("\n")); + done(); + }); + + function read_map() { + var map = JSON.parse(read("test/input/issue-1236/simple.js.map")); + delete map.sourcesContent; + return JSON.stringify(map).replace(/"/g, '\\"'); + } + }); + it("Should include function calls in source map", function(done) { + var command = [ + uglifyjscmd, + "test/input/issue-2310/input.js", + "--compress", + "--no-module", + "--source-map", "url=inline", + ].join(" "); + exec(command, function(err, stdout, stderr) { + if (err) throw err; + assert.strictEqual(stdout, [ + 'function foo(){return function(){console.log("PASS")}}foo()();', + "//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbInRlc3QvaW5wdXQvaXNzdWUtMjMxMC9pbnB1dC5qcyJdLCJuYW1lcyI6WyJmb28iLCJjb25zb2xlIiwibG9nIl0sIm1hcHBpbmdzIjoiQUFBQSxTQUFTQSxNQUNMLE9BQU8sV0FDSEMsUUFBUUMsSUFBSSxNQUFNLENBQ3RCLENBQ0osQ0FHWUYsSUFBSSxFQUNWIn0=", + "" + ].join("\n")); + done(); + }); + }); + it("Should dump AST as JSON", function(done) { + var command = uglifyjscmd + " test/input/global_defs/simple.js -mco ast"; + exec(command, function(err, stdout) { + if (err) throw err; + var ast = JSON.parse(stdout); + assert.strictEqual(ast._class, "AST_Toplevel"); + assert.ok(Array.isArray(ast.body)); + done(); + }); + }); + it("Should print supported options on invalid option syntax", function(done) { + var command = uglifyjscmd + " test/input/comments/filter.js -b ascii-only"; + exec(command, function(err, stdout, stderr) { + assert.ok(err); + assert.strictEqual(stdout, ""); + assert.ok(/^Supported options:\n[\s\S]*?\nERROR: `ascii-only` is not a supported option/.test(stderr), stderr); + done(); + }); + }); + it("Should work with --mangle reserved=[]", function(done) { + var command = [ + uglifyjscmd, + "test/input/issue-505/input.js", + "--mangle", "reserved=[callback]", + "--no-module", + ].join(" "); + exec(command, function(err, stdout) { + if (err) throw err; + assert.strictEqual(stdout, 'function test(callback){"aaaaaaaaaaaaaaaa";callback(err,data);callback(err,data)}\n'); + done(); + }); + }); + it("Should work with --mangle reserved=false", function(done) { + var command = [ + uglifyjscmd, + "test/input/issue-505/input.js", + "--mangle", "reserved=false", + "--no-module", + ].join(" "); + exec(command, function(err, stdout) { + if (err) throw err; + assert.strictEqual(stdout, 'function test(a){"aaaaaaaaaaaaaaaa";a(err,data);a(err,data)}\n'); + done(); + }); + }); + it("Should fail with --mangle-props reserved=[in]", function(done) { + var command = uglifyjscmd + " test/input/issue-505/input.js --mangle-props reserved=[in]"; + exec(command, function(err, stdout, stderr) { + assert.ok(err); + assert.strictEqual(stdout, ""); + assert.ok(/^Supported options:\n[\s\S]*?\nERROR: `reserved=\[in]` is not a supported option/.test(stderr), stderr); + done(); + }); + }); + it("Should work with mangle.properties.regex from --config-file", function(done) { + var command = [ + uglifyjscmd, + "test/input/issue-3315/input.js", + "--config-file", "test/input/issue-3315/config.json", + "--no-module", + ].join(" "); + exec(command, function(err, stdout) { + if (err) throw err; + assert.strictEqual(stdout, 'function f(){"aaaaaaaaaa";var a={prop:1,t:2};return a.prop+a.t}\n'); + done(); + }); + }); + it("Should fail with --define a-b", function(done) { + var command = uglifyjscmd + " test/input/issue-505/input.js --define a-b"; + exec(command, function(err, stdout, stderr) { + assert.ok(err); + assert.strictEqual(stdout, ""); + assert.strictEqual(stderr, "ERROR: cannot parse arguments for 'define': a-b\n"); + done(); + }); + }); + it("Should work with explicit --rename", function(done) { + var command = [ + uglifyjscmd, + "test/input/rename/input.js", + "--no-module", + "--rename", + ].join(" "); + exec(command, function(err, stdout, stderr) { + if (err) throw err; + assert.strictEqual(stdout, "function f(a){return b(a);function b(c){return c+c}}\n"); + done(); + }); + }); + it("Should work with explicit --no-rename", function(done) { + var command = [ + uglifyjscmd, + "test/input/rename/input.js", + "--compress", "passes=2", + "--mangle", + "--no-module", + "--no-rename", + ].join(" "); + exec(command, function(err, stdout, stderr) { + if (err) throw err; + assert.strictEqual(stdout, "function f(n){return function(n){return n+n}(n)}\n"); + done(); + }); + }); + it("Should work with implicit --rename", function(done) { + var command = [ + uglifyjscmd, + "test/input/rename/input.js", + "--compress", "passes=2", + "--mangle", + "--no-module", + ].join(" "); + exec(command, function(err, stdout, stderr) { + if (err) throw err; + assert.strictEqual(stdout, "function f(n){return n+n}\n"); + done(); + }); + }); + it("Should work with implicit --no-rename", function(done) { + var command = [ + uglifyjscmd, + "test/input/rename/input.js", + "--compress", "passes=2", + "--no-module", + ].join(" "); + exec(command, function(err, stdout, stderr) { + if (err) throw err; + assert.strictEqual(stdout, "function f(x){return function(x){return x+x}(x)}\n"); + done(); + }); + }); + it("Should work with --enclose", function(done) { + var command = uglifyjscmd + " test/input/enclose/input.js --enclose"; + exec(command, function(err, stdout, stderr) { + if (err) throw err; + assert.strictEqual(stdout, '(function(){function enclose(){console.log("test enclose")}enclose()})();\n'); + done(); + }); + }); + it("Should work with --enclose arg", function(done) { + var command = uglifyjscmd + " test/input/enclose/input.js --enclose undefined"; + exec(command, function(err, stdout, stderr) { + if (err) throw err; + assert.strictEqual(stdout, '(function(undefined){function enclose(){console.log("test enclose")}enclose()})();\n'); + done(); + }); + }); + it("Should work with --enclose arg:value", function(done) { + var command = uglifyjscmd + " test/input/enclose/input.js --enclose window,undefined:window"; + exec(command, function(err, stdout, stderr) { + if (err) throw err; + assert.strictEqual(stdout, '(function(window,undefined){function enclose(){console.log("test enclose")}enclose()})(window);\n'); + done(); + }); + }); + it("Should work with --enclose & --wrap", function(done) { + var command = uglifyjscmd + " test/input/enclose/input.js --enclose window,undefined:window --wrap exports"; + exec(command, function(err, stdout, stderr) { + if (err) throw err; + assert.strictEqual(stdout, '(function(exports){(function(window,undefined){function enclose(){console.log("test enclose")}enclose()})(window)})(typeof exports=="undefined"?exports={}:exports);\n'); + done(); + }); + }); + it("Should work with --module", function(done) { + var command = uglifyjscmd + " test/input/module/input.js --module -mc"; + exec(command, function(err, stdout, stderr) { + if (err) throw err; + assert.strictEqual(stdout, read("test/input/module/expect.js")); + done(); + }); + }); + it("Should compress swarm of unused variables with reasonable performance", function(done) { + var code = [ + "console.log(function() {", + ]; + for (var i = 0; i < 10000; i++) { + code.push("var obj" + i + " = {p: " + i + "};"); + } + code.push("var map = {"); + for (var i = 0; i < 10000; i++) { + code.push("obj" + i + ": obj" + i + ","); + } + code = code.concat([ + "};", + "return obj25.p + obj121.p + obj1024.p;", + "}());", + ]).join("\n"); + exec(uglifyjscmd + " -mc", function(err, stdout) { + if (err) throw err; + assert.strictEqual(stdout, "console.log(function(){var p={p:25},n={p:121},o={p:1024};return p.p+n.p+o.p}());\n"); + assert.strictEqual(run_code(stdout), run_code(code)); + done(); + }).stdin.end(code); + }); +}); diff --git a/test/mocha/comments.js b/test/mocha/comments.js new file mode 100644 index 00000000000..86a6a004362 --- /dev/null +++ b/test/mocha/comments.js @@ -0,0 +1,485 @@ +var assert = require("assert"); +var UglifyJS = require("../node"); + +describe("comments", function() { + it("Should recognize eol of single line comments", function() { + var tests = [ + "//Some comment 1\n>", + "//Some comment 2\r>", + "//Some comment 3\r\n>", + "//Some comment 4\u2028>", + "//Some comment 5\u2029>" + ]; + + var fail = function(e) { + return e instanceof UglifyJS.JS_Parse_Error + && e.message === "Unexpected token: operator «>»" + && e.line === 2 + && e.col === 0; + } + + for (var i = 0; i < tests.length; i++) { + assert.throws(function() { + UglifyJS.parse(tests[i]); + }, fail, tests[i]); + } + }); + + it("Should update the position of a multiline comment correctly", function() { + var tests = [ + "/*Some comment 1\n\n\n*/\n>\n\n\n\n\n\n", + "/*Some comment 2\r\n\r\n\r\n*/\r\n>\n\n\n\n\n\n", + "/*Some comment 3\r\r\r*/\r>\n\n\n\n\n\n", + "/*Some comment 4\u2028\u2028\u2028*/\u2028>\n\n\n\n\n\n", + "/*Some comment 5\u2029\u2029\u2029*/\u2029>\n\n\n\n\n\n" + ]; + + var fail = function(e) { + return e instanceof UglifyJS.JS_Parse_Error + && e.message === "Unexpected token: operator «>»" + && e.line === 5 + && e.col === 0; + } + + for (var i = 0; i < tests.length; i++) { + assert.throws(function() { + UglifyJS.parse(tests[i]); + }, fail, tests[i]); + } + }); + + describe("comment within return", function() { + it("Should handle leading return", function() { + var result = UglifyJS.minify([ + "function unequal(x, y) {", + " return (", + " // Either one", + " x < y", + " ||", + " y < x", + " );", + "}", + ].join("\n"), { + compress: false, + mangle: false, + output: { + beautify: true, + comments: "all", + }, + }); + if (result.error) throw result.error; + assert.strictEqual(result.code, [ + "function unequal(x, y) {", + " // Either one", + " return x < y || y < x;", + "}", + ].join("\n")); + }); + + it("Should handle trailing return", function() { + var result = UglifyJS.minify([ + "function unequal(x) {", + " var y;", + " return (", + " // Either one", + " x < y", + " ||", + " y < x", + " );", + "}", + ].join("\n"), { + compress: false, + mangle: false, + output: { + beautify: true, + comments: "all", + }, + }); + if (result.error) throw result.error; + assert.strictEqual(result.code, [ + "function unequal(x) {", + " var y;", + " // Either one", + " return x < y || y < x;", + "}", + ].join("\n")); + }); + + it("Should handle comment folded into return", function() { + var result = UglifyJS.minify([ + "function f() {", + " /* boo */ x();", + " return y();", + "}", + ].join("\n"), { + mangle: false, + output: { + beautify: true, + comments: "all", + }, + }); + if (result.error) throw result.error; + assert.strictEqual(result.code, [ + "function f() {", + " /* boo */", + " return x(), y();", + "}", + ].join("\n")); + }); + }); + + it("Should not drop comments after first OutputStream", function() { + var code = "/* boo */\nx();"; + var ast = UglifyJS.parse(code); + var out1 = UglifyJS.OutputStream({ + beautify: true, + comments: "all", + }); + ast.print(out1); + var out2 = UglifyJS.OutputStream({ + beautify: true, + comments: "all", + }); + ast.print(out2); + assert.strictEqual(out1.get(), code); + assert.strictEqual(out2.get(), out1.get()); + }); + + it("Should retain trailing comments", function() { + var code = [ + "if (foo /* lost comment */ && bar /* lost comment */) {", + " // this one is kept", + " {/* lost comment */}", + " !function() {", + " // lost comment", + " }();", + " function baz() {/* lost comment */}", + " // lost comment", + "}", + "// comments right before EOF are lost as well", + ].join("\n"); + var result = UglifyJS.minify(code, { + compress: false, + mangle: false, + output: { + beautify: true, + comments: "all", + }, + }); + if (result.error) throw result.error; + assert.strictEqual(result.code, code); + }); + + it("Should retain comments within braces", function() { + var code = [ + "{/* foo */}", + "a({/* foo */});", + "while (a) {/* foo */}", + "switch (a) {/* foo */}", + "if (a) {/* foo */} else {/* bar */}", + ].join("\n\n"); + var result = UglifyJS.minify(code, { + compress: false, + mangle: false, + output: { + beautify: true, + comments: "all", + }, + }); + if (result.error) throw result.error; + assert.strictEqual(result.code, code); + }); + + it("Should correctly preserve new lines around comments", function() { + [ + [ + "// foo", + "// bar", + "x();", + ].join("\n"), + [ + "// foo", + "/* bar */", + "x();", + ].join("\n"), + [ + "// foo", + "/* bar */ x();", + ].join("\n"), + [ + "/* foo */", + "// bar", + "x();", + ].join("\n"), + [ + "/* foo */ // bar", + "x();", + ].join("\n"), + [ + "/* foo */", + "/* bar */", + "x();", + ].join("\n"), + [ + "/* foo */", + "/* bar */ x();", + ].join("\n"), + [ + "/* foo */ /* bar */", + "x();", + ].join("\n"), + "/* foo */ /* bar */ x();", + ].forEach(function(code) { + var result = UglifyJS.minify(code, { + compress: false, + mangle: false, + output: { + beautify: true, + comments: "all", + }, + }); + if (result.error) throw result.error; + assert.strictEqual(result.code, code); + }); + }); + + it("Should preserve new line before comment without beautify", function() { + var code = [ + "function f(){", + "/* foo */bar()}", + ].join("\n"); + var result = UglifyJS.minify(code, { + compress: false, + mangle: false, + output: { + comments: "all", + }, + }); + if (result.error) throw result.error; + assert.strictEqual(result.code, code); + }); + + it("Should handle comments around parentheses correctly", function() { + var code = [ + "a();", + "/* foo */", + "(b())", + "/* bar */", + "c();", + ].join("\n"); + var result = UglifyJS.minify(code, { + compress: false, + mangle: false, + output: { + comments: "all", + }, + }); + if (result.error) throw result.error; + assert.strictEqual(result.code, [ + "a();", + "/* foo */", + "b()", + "/* bar */;c();", + ].join("\n")); + }); + + it("Should preserve comments around IIFE", function() { + var result = UglifyJS.minify("/*a*/(/*b*/function(){/*c*/}/*d*/)/*e*/();", { + compress: false, + mangle: false, + output: { + comments: "all", + }, + }); + if (result.error) throw result.error; + assert.strictEqual(result.code, "/*a*/ /*b*/(function(){/*c*/}/*d*/ /*e*/)();"); + }); + + it("Should output line comments after statements", function() { + var result = UglifyJS.minify([ + "x()//foo", + "{y()//bar", + "}", + ].join("\n"), { + compress: false, + mangle: false, + output: { + comments: "all", + }, + }); + if (result.error) throw result.error; + assert.strictEqual(result.code, [ + "x();//foo", + "{y();//bar", + "}", + ].join("\n")); + }); + + it("Should handle programmatic AST insertions gracefully", function() { + var ast = UglifyJS.parse([ + "function f() {", + " //foo", + " bar;", + " return;", + "}", + ].join("\n")); + ast.body[0].body[0] = new UglifyJS.AST_Throw({value: ast.body[0].body[0].body}); + ast.body[0].body[1].value = new UglifyJS.AST_Number({value: 42}); + assert.strictEqual(ast.print_to_string({comments: "all"}), [ + "function f(){", + "//foo", + "throw bar;return 42}", + ].join("\n")); + }); + + it("Should not duplicate sourceMappingURL", function() { + var code = [ + "//# sourceMappingURL=input.map", + "print(42);", + "//# sourceMappingURL=input.map", + "", + "//# sourceMappingURL=input.map", + "//# sourceMappingURL=input.map", + "", + ].join("\n"); + + var result = UglifyJS.minify(code, { + output: { comments: true }, + }); + if (result.error) throw result.error; + assert.strictEqual(result.code, [ + "//# sourceMappingURL=input.map", + "print(42);", + "//# sourceMappingURL=input.map", + "//# sourceMappingURL=input.map", + "//# sourceMappingURL=input.map", + ].join("\n")); + + result = UglifyJS.minify(code, { + output: { comments: true }, + sourceMap: { url: "output.map" }, + }); + if (result.error) throw result.error; + assert.strictEqual(result.code, [ + "//# sourceMappingURL=input.map", + "print(42);", + "//# sourceMappingURL=input.map", + "//# sourceMappingURL=input.map", + "//# sourceMappingURL=output.map", + ].join("\n")); + }); + + describe("comment before constant", function() { + var js = 'function f() { /*c1*/ var /*c2*/ foo = /*c3*/ false; return foo; }'; + + it("Should test comment before constant is retained and output after mangle.", function() { + var result = UglifyJS.minify(js, { + compress: { collapse_vars: false, reduce_vars: false }, + output: { comments: true }, + }); + assert.strictEqual(result.code, 'function f(){/*c1*/var/*c2*/n=/*c3*/!1;return n}'); + }); + + it("Should test code works when comments disabled.", function() { + var result = UglifyJS.minify(js, { + compress: { collapse_vars: false, reduce_vars: false }, + output: { comments: false }, + }); + assert.strictEqual(result.code, 'function f(){var n=!1;return n}'); + }); + }); + + describe("comment filters", function() { + it("Should be able to filter comments by passing regexp", function() { + var ast = UglifyJS.parse("/*!test1*/\n/*test2*/\n//!test3\n//test4\ntest7\n-->!test8"); + assert.strictEqual(ast.print_to_string({comments: /^!/}), "/*!test1*/\n//!test3\n//!test6\n//!test8"); + }); + + it("Should be able to filter comments with the 'all' option", function() { + var ast = UglifyJS.parse("/*!test1*/\n/*test2*/\n//!test3\n//test4\ntest7\n-->!test8"); + assert.strictEqual(ast.print_to_string({comments: "all"}), "/*!test1*/\n/*test2*/\n//!test3\n//test4\n//test5\n//!test6\n//test7\n//!test8"); + }); + + it("Should be able to filter comments with the 'some' option", function() { + var ast = UglifyJS.parse("// foo\n/*@preserve*/\n// bar\n/*@license*/\n//@license with the wrong comment type\n/*@cc_on something*/"); + assert.strictEqual(ast.print_to_string({comments: "some"}), "/*@preserve*/\n/*@license*/\n/*@cc_on something*/"); + }); + + it("Should be able to filter comments by passing a function", function() { + var ast = UglifyJS.parse("/*TEST 123*/\n//An other comment\n//8 chars."); + var f = function(node, comment) { + return comment.value.length === 8; + }; + assert.strictEqual(ast.print_to_string({comments: f}), "/*TEST 123*/\n//8 chars."); + }); + + it("Should be able to filter comments by passing regex in string format", function() { + var ast = UglifyJS.parse("/*!test1*/\n/*test2*/\n//!test3\n//test4\ntest7\n-->!test8"); + assert.strictEqual(ast.print_to_string({comments: "/^!/"}), "/*!test1*/\n//!test3\n//!test6\n//!test8"); + }); + + it("Should be able to get the comment and comment type when using a function", function() { + var ast = UglifyJS.parse("/*!test1*/\n/*test2*/\n//!test3\n//test4\ntest7\n-->!test8"); + var f = function(node, comment) { + return comment.type == "comment1" || comment.type == "comment3"; + }; + assert.strictEqual(ast.print_to_string({comments: f}), "//!test3\n//test4\n//test5\n//!test6"); + }); + + it("Should be able to filter comments by passing a boolean", function() { + var ast = UglifyJS.parse("/*!test1*/\n/*test2*/\n//!test3\n//test4\ntest7\n-->!test8"); + assert.strictEqual(ast.print_to_string({comments: true}), "/*!test1*/\n/*test2*/\n//!test3\n//test4\n//test5\n//!test6\n//test7\n//!test8"); + assert.strictEqual(ast.print_to_string({comments: false}), ""); + }); + + it("Should never be able to filter comment5 (shebangs)", function() { + var ast = UglifyJS.parse("#!Random comment\n//test1\n/*test2*/"); + var f = function(node, comment) { + assert.strictEqual(comment.type === "comment5", false); + return true; + }; + assert.strictEqual(ast.print_to_string({comments: f}), "#!Random comment\n//test1\n/*test2*/"); + }); + + it("Should never be able to filter comment5 when using 'some' as filter", function() { + var ast = UglifyJS.parse("#!foo\n//foo\n/*@preserve*/\n/* please hide me */"); + assert.strictEqual(ast.print_to_string({comments: "some"}), "#!foo\n/*@preserve*/"); + }); + + it("Should have no problem on multiple calls", function() { + const options = { + comments: /ok/, + }; + assert.strictEqual(UglifyJS.parse("/* ok */ function a(){}").print_to_string(options), "/* ok */function a(){}"); + assert.strictEqual(UglifyJS.parse("/* ok */ function a(){}").print_to_string(options), "/* ok */function a(){}"); + assert.strictEqual(UglifyJS.parse("/* ok */ function a(){}").print_to_string(options), "/* ok */function a(){}"); + }); + + it("Should handle shebang and preamble correctly", function() { + var code = UglifyJS.minify("#!/usr/bin/node\nvar x = 10;", { + output: { preamble: "/* Build */" }, + }).code; + assert.strictEqual(code, "#!/usr/bin/node\n/* Build */\nvar x=10;"); + }); + + it("Should handle preamble without shebang correctly", function() { + var code = UglifyJS.minify("var x = 10;", { + output: { preamble: "/* Build */" }, + }).code; + assert.strictEqual(code, "/* Build */\nvar x=10;"); + }); + }); + + describe("Huge number of comments.", function() { + it("Should parse and compress code with thousands of consecutive comments", function() { + var js = "function lots_of_comments(x) { return 7 -"; + for (var i = 1; i <= 5000; ++i) js += "// " + i + "\n"; + for (; i <= 10000; ++i) js += "/* " + i + " */ /**/"; + js += "x; }"; + var result = UglifyJS.minify(js, { + mangle: false, + }); + assert.strictEqual(result.code, "function lots_of_comments(x){return 7-x}"); + }); + }); +}); diff --git a/test/mocha/directives.js b/test/mocha/directives.js new file mode 100644 index 00000000000..3d8dfa58542 --- /dev/null +++ b/test/mocha/directives.js @@ -0,0 +1,383 @@ +var assert = require("assert"); +var UglifyJS = require("../node"); + +describe("Directives", function() { + it("Should allow tokenizer to store directives state", function() { + var tokenizer = UglifyJS.tokenizer("", "foo.js"); + // Stack level 0 + assert.strictEqual(tokenizer.has_directive("use strict"), false); + assert.strictEqual(tokenizer.has_directive("use asm"), false); + assert.strictEqual(tokenizer.has_directive("use thing"), false); + // Stack level 2 + tokenizer.push_directives_stack(); + tokenizer.push_directives_stack(); + tokenizer.add_directive("use strict"); + assert.strictEqual(tokenizer.has_directive("use strict"), true); + assert.strictEqual(tokenizer.has_directive("use asm"), false); + assert.strictEqual(tokenizer.has_directive("use thing"), false); + // Stack level 3 + tokenizer.push_directives_stack(); + tokenizer.add_directive("use strict"); + tokenizer.add_directive("use asm"); + assert.strictEqual(tokenizer.has_directive("use strict"), true); + assert.strictEqual(tokenizer.has_directive("use asm"), true); + assert.strictEqual(tokenizer.has_directive("use thing"), false); + // Stack level 2 + tokenizer.pop_directives_stack(); + assert.strictEqual(tokenizer.has_directive("use strict"), true); + assert.strictEqual(tokenizer.has_directive("use asm"), false); + assert.strictEqual(tokenizer.has_directive("use thing"), false); + // Stack level 3 + tokenizer.push_directives_stack(); + tokenizer.add_directive("use thing"); + tokenizer.add_directive("use\\\nasm"); + assert.strictEqual(tokenizer.has_directive("use strict"), true); + assert.strictEqual(tokenizer.has_directive("use asm"), false); // Directives are strict! + assert.strictEqual(tokenizer.has_directive("use thing"), true); + // Stack level 2 + tokenizer.pop_directives_stack(); + assert.strictEqual(tokenizer.has_directive("use strict"), true); + assert.strictEqual(tokenizer.has_directive("use asm"), false); + assert.strictEqual(tokenizer.has_directive("use thing"), false); + // Stack level 1 + tokenizer.pop_directives_stack(); + assert.strictEqual(tokenizer.has_directive("use strict"), false); + assert.strictEqual(tokenizer.has_directive("use asm"), false); + assert.strictEqual(tokenizer.has_directive("use thing"), false); + // Stack level 0 + tokenizer.pop_directives_stack(); + assert.strictEqual(tokenizer.has_directive("use strict"), false); + assert.strictEqual(tokenizer.has_directive("use asm"), false); + assert.strictEqual(tokenizer.has_directive("use thing"), false); + }); + it("Should know which strings are directive and which ones are not", function() { + [ + [ + '"use strict"\n', + [ "use strict" ], + [ "use asm" ] + ], + [ + '"use\\\nstrict";', + [], + [ "use strict", "use\nstrict", "use \nstrict", "use asm" ] + ], + [ + '"use strict"\n"use asm"\n"use bar"\n', + [ "use strict", "use asm", "use bar" ], + [ "use foo", "use\\x20strict" ] + ], + [ + '"use \\\nstrict";"use strict";', + [ "use strict" ], + [ "use\nstrict", "use \nstrict", "use asm" ] + ], + [ + '"\\76";', + [ "\\76" ], + [ ">" ] + ], + [ + // no ; or newline + '"use strict"', + [ "use strict" ], + [ "use\nstrict", "use \nstrict", "use asm" ] + ], + [ + ';"use strict"', + [], + [ "use strict", "use\nstrict", "use \nstrict", "use asm" ] + ], + // Duplicate above code but put it in a function + [ + 'function foo() {"use strict"\n', + [ "use strict" ], + [ "use asm" ] + ], + [ + 'function foo() {"use\\\nstrict";', + [], + [ "use strict", "use\nstrict", "use \nstrict", "use asm" ] + ], + [ + 'function foo() {"use strict"\n"use asm"\n"use bar"\n', + [ "use strict", "use asm", "use bar" ], + [ "use foo", "use\\x20strict" ] + ], + [ + 'function foo() {"use \\\nstrict";"use strict";', + [ "use strict" ], + [ "use\nstrict", "use \nstrict", "use asm" ] + ], + [ + 'var foo = function() {"\\76";', + [ "\\76" ], + [ ">" ] + ], + [ + 'var foo = function() {"use strict"', // no ; or newline + [ "use strict" ], + [ "use\nstrict", "use \nstrict", "use asm" ] + ], + [ + 'var foo = function() {;"use strict"', + [], + [ "use strict", "use\nstrict", "use \nstrict", "use asm" ] + ], + // Special cases + [ + '"1";"2";"3";"4";;"5"', + [ "1", "2", "3", "4" ], + [ "5", "6", "use strict", "use asm" ] + ], + [ + 'if(1){"use strict";', + [], + [ "use strict", "use\nstrict", "use \nstrict", "use asm" ] + ], + [ + '"use strict";try{"use asm";', + [ "use strict" ], + [ "use\nstrict", "use \nstrict", "use asm" ] + ], + ].forEach(function(test) { + var tokenizer = UglifyJS.tokenizer(test[0] + "]", "foo.js"); + assert.throws(function() { + UglifyJS.parse(tokenizer); + }, function(e) { + return e instanceof UglifyJS.JS_Parse_Error + && /^Unexpected token: punc «]»/.test(e.message) + }, test[0]); + test[1].forEach(function(directive) { + assert.strictEqual(tokenizer.has_directive(directive), true, directive + " in " + test[0]); + }); + test[2].forEach(function(fake_directive) { + assert.strictEqual(tokenizer.has_directive(fake_directive), false, fake_directive + " in " + test[0]); + }); + }); + }); + it("Should print semicolon to separate strings from directives", function() { + [ + [ "", ';"";' ], + [ '"test";', '"test";;"";' ], + [ '"test";;', '"test";;"";' ], + [ '"tests";\n', '"tests";;"";' ], + [ '"tests"', '"tests";;"";' ], + [ '"tests"; \n\t', '"tests";;"";' ], + [ '"tests";\n\n', '"tests";;"";' ], + [ '\n\n"use strict";\n\n', '"use strict";;"";' ], + ].forEach(function(test) { + var ast = UglifyJS.parse(test[0]); + ast.body.push(new UglifyJS.AST_SimpleStatement({ + body: new UglifyJS.AST_String({ value: "" }) + })); + var out = UglifyJS.OutputStream(); + ast.print(out); + assert.strictEqual(out.get(), test[1], test[0]); + }); + }); + it("Should only print 2 semicolons spread over 2 lines in beautify mode", function() { + var result = UglifyJS.minify([ + '"use strict";', + "'use strict';", + '"use strict";', + '"use strict";', + ";'use strict';", + "console.log('use strict');" + ].join(""), { + compress: false, + output: { + beautify: true, + quote_style: 3 + } + }); + if (result.error) throw result.error; + assert.strictEqual(result.code, [ + '"use strict";', + "'use strict';", + '"use strict";', + '"use strict";', + ";'use strict';", + "console.log('use strict');" + ].join("\n\n")); + }); + it("Should not add double semicolons in non-scoped block statements to avoid strings becoming directives", function() { + [ + [ + '"use strict";"use\\x20strict";', + '"use strict";"use\\x20strict";' + ], + [ + '{"use\\x20strict"}', + '{"use strict"}' + ], + [ + 'function foo(){"use\\x20strict";}', // Valid place for directives + 'function foo(){"use\\x20strict"}' + ], + [ + 'try{"use\\x20strict"}catch(e){}finally{"use\\x20strict"}', + 'try{"use strict"}catch(e){}finally{"use strict"}' + ], + [ + 'if(1){"use\\x20strict"} else {"use strict"}', + 'if(1){"use strict"}else{"use strict"}' + ] + ].forEach(function(test) { + var result = UglifyJS.minify(test[0], { + compress: false, + mangle: false + }); + if (result.error) throw result.error; + assert.strictEqual(result.code, test[1], test[0]); + }); + }); + it("Should check quote style of directives", function() { + [ + // 0. Prefer double quotes, unless string contains more double quotes than single quotes + [ + '"testing something";', + 0, + '"testing something";' + ], + [ + "'use strict';", + 0, + '"use strict";' + ], + [ + '"\\\'use strict\\\'";', + 0, + '"\\\'use strict\\\'";', + ], + [ + "'\"use strict\"';", + 0, + "'\"use strict\"';", + ], + // 1. Always use single quote + [ + '"testing something";', + 1, + "'testing something';" + ], + [ + "'use strict';", + 1, + "'use strict';" + ], + [ + '"\'use strict\'";', + 1, + // Intentionally causes directive breakage at cost of less logic, usage should be rare anyway + '"\'use strict\'";', + ], + [ + "'\\'use strict\\'';", // Not a valid directive + 1, + "'\\'use strict\\'';" // But no ; necessary as directive stays invalid + ], + [ + "'\"use strict\"';", + 1, + "'\"use strict\"';", + ], + // 2. Always use double quote + [ + '"testing something";', + 2, + '"testing something";' + ], + [ + "'use strict';", + 2, + '"use strict";' + ], + [ + '"\'use strict\'";', + 2, + "\"'use strict'\";", + ], + [ + "'\"use strict\"';", + 2, + // Intentionally causes directive breakage at cost of less logic, usage should be rare anyway + "'\"use strict\"';", + ], + [ + '"\\"use strict\\"";', // Not a valid directive + 2, + '"\\"use strict\\"";' // But no ; necessary as directive stays invalid + ], + // 3. Always use original + [ + '"testing something";', + 3, + '"testing something";' + ], + [ + "'use strict';", + 3, + "'use strict';", + ], + [ + '"\'use strict\'";', + 3, + '"\'use strict\'";', + ], + [ + "'\"use strict\"';", + 3, + "'\"use strict\"';", + ], + ].forEach(function(test) { + var result = UglifyJS.minify(test[0], { + compress: false, + output: { + quote_style: test[1] + } + }); + if (result.error) throw result.error; + assert.strictEqual(result.code, test[2], test[0] + " using mode " + test[1]); + }); + }); + it("Should be able to compress without side effects", function() { + [ + [ + '"use strict";"use strict";"use strict";"use foo";"use strict";;"use sloppy";doSomething("foo");', + '"use strict";doSomething("foo");' + ], + [ + // Nothing gets optimized in the compressor because "use asm" is the first statement + '"use asm";"use\\x20strict";1+1;', + '"use asm";"use\\x20strict";1+1;' + ], + [ + 'function f(){ "use strict" }', + 'function f(){}' + ], + [ + 'function f(){ "use asm" }', + 'function f(){"use asm"}' + ], + [ + 'function f(){ "use nondirective" }', + 'function f(){}' + ], + [ + 'function f(){ ;"use strict" }', + 'function f(){}' + ], + [ + 'function f(){ "use \\n"; }', + 'function f(){}' + ], + ].forEach(function(test) { + var result = UglifyJS.minify(test[0], { + module: false, + }); + if (result.error) throw result.error; + assert.strictEqual(result.code, test[1], test[0]); + }); + }); +}); diff --git a/test/mocha/exports.js b/test/mocha/exports.js new file mode 100644 index 00000000000..6f1bf204d49 --- /dev/null +++ b/test/mocha/exports.js @@ -0,0 +1,181 @@ +var acorn = require("acorn"); +var assert = require("assert"); +var UglifyJS = require("../node"); + +describe("export", function() { + it("Should reject invalid `export ...` statement syntax", function() { + [ + "export *;", + "export A;", + "export 42;", + "export var;", + "export * as A;", + "export A as B;", + "export const A;", + "export function(){};", + ].forEach(function(code) { + assert.throws(function() { + UglifyJS.parse(code); + }, function(e) { + return e instanceof UglifyJS.JS_Parse_Error; + }, code); + }); + }); + it("Should reject invalid `export { ... }` statement syntax", function() { + [ + "export { * };", + "export { * as A };", + "export { 42 as A };", + "export { 'A' as B };", + "export { A as B-C };", + "export { default as A };", + ].forEach(function(code) { + assert.throws(function() { + UglifyJS.parse(code); + }, function(e) { + return e instanceof UglifyJS.JS_Parse_Error; + }, code); + }); + }); + it("Should reject invalid `export default ...` statement syntax", function() { + [ + "export default *;", + "export default var;", + "export default A as B;", + ].forEach(function(code) { + assert.throws(function() { + UglifyJS.parse(code); + }, function(e) { + return e instanceof UglifyJS.JS_Parse_Error; + }, code); + }); + }); + it("Should reject invalid `export ... from ...` statement syntax", function() { + [ + "export from 'path';", + "export A from 'path';", + "export * from `path`;", + "export 'A' from 'path';", + "export A as B from 'path';", + "export 'A' as B from 'path';", + "export default from 'path';", + "export { A }, B from 'path';", + "export * as A, B from 'path';", + "export * as A, {} from 'path';", + "export { * as A } from 'path';", + "export { 42 as A } from 'path';", + "export { A-B as C } from 'path';", + ].forEach(function(code) { + assert.throws(function() { + UglifyJS.parse(code); + }, function(e) { + return e instanceof UglifyJS.JS_Parse_Error; + }, code); + }); + }); + it("Should reject `export` statement not under top-level scope", function() { + [ + "{ export {}; }", + "if (0) export var A;", + "function f() { export default 42; }", + ].forEach(function(code) { + assert.throws(function() { + UglifyJS.parse(code); + }, function(e) { + return e instanceof UglifyJS.JS_Parse_Error; + }, code); + }); + }); + it("Should compare `export` statements correctly", function() { + var stats = { + Declaration: [ + "export let A;", + "export const A = 42;", + "export var { A, B: [] } = C;", + "export function A() { return B(A); }", + "export async function* A({}, ...[]) { return B(A); }", + ], + Default: [ + "export default 42;", + "export default A => A(B);", + "export default class A extends B {}", + "export default (class A extends B {});", + "export default class A { static C = 42; }", + "export default class A extends B { static C = 42; }", + ], + Foreign: [ + "export * from 'path';", + "export {} from 'path';", + "export * as A from 'path';", + "export { default } from 'path';", + "export { A, B as C } from 'path';", + "export { A, default as C } from 'path';", + ], + References: [ + "export {};", + "export { A };", + "export { A as B };", + "export { A, B as C };", + "export { A as default };", + ], + }; + for (var k in stats) stats[k].forEach(function(c, i) { + var s = UglifyJS.parse(c); + assert.ok(s instanceof UglifyJS.AST_Toplevel, c); + assert.strictEqual(s.body.length, 1, c); + assert.strictEqual(s.body[0].TYPE, "Export" + k, c); + for (var l in stats) stats[l].forEach(function(d, j) { + var t = UglifyJS.parse(d); + assert.ok(t instanceof UglifyJS.AST_Toplevel, d); + assert.strictEqual(t.body.length, 1, d); + assert.strictEqual(t.body[0].TYPE, "Export" + l, d); + assert.strictEqual(s.equals(t), k === l && i === j, c + "\n" + d); + }); + }); + }); + it("Should interoperate with ESTree correctly", function() { + [ + "export var A = 42;", + "export default (class A {});", + "var A; export { A as '42' };", + "export { '42' } from 'path';", + "export * as '42' from 'path';", + ].forEach(function(code) { + var ast = UglifyJS.parse(code); + try { + var spidermonkey = ast.to_mozilla_ast(); + } catch (ex) { + assert.fail("[to_mozilla_ast] " + ex.stack); + } + try { + var ast2 = UglifyJS.AST_Node.from_mozilla_ast(spidermonkey); + } catch (ex) { + assert.fail("[from_mozilla_ast] " + ex.stack); + } + assert.strictEqual(ast2.print_to_string(), ast.print_to_string(), [ + "spidermonkey:", + ast.print_to_string(), + ast2.print_to_string(), + ].join("\n")); + try { + var acorn_est = acorn.parse(code, { + ecmaVersion: "latest", + locations: true, + sourceType: "module", + }); + } catch (ex) { + assert.fail("[acorn.parse] " + ex.stack); + } + try { + var ast3 = UglifyJS.AST_Node.from_mozilla_ast(acorn_est); + } catch (ex) { + assert.fail("[from_acorn] " + ex.stack); + } + assert.strictEqual(ast3.print_to_string(), ast.print_to_string(), [ + "acorn:", + ast.print_to_string(), + ast3.print_to_string(), + ].join("\n")); + }); + }); +}); diff --git a/test/mocha/getter-setter.js b/test/mocha/getter-setter.js new file mode 100644 index 00000000000..fc78a25f4cd --- /dev/null +++ b/test/mocha/getter-setter.js @@ -0,0 +1,67 @@ +var assert = require("assert"); +var UglifyJS = require("../node"); + +describe("Getters and setters", function() { + it("Should not accept operator symbols as getter/setter name", function() { + [ + "++", + "--", + "+", + "-", + "!", + "~", + "&", + "|", + "^", + "*", + "/", + "%", + ">>", + "<<", + ">>>", + "<", + ">", + "<=", + ">=", + "==", + "===", + "!=", + "!==", + "?", + "=", + "+=", + "-=", + "/=", + "*=", + "%=", + ">>=", + "<<=", + ">>>=", + "|=", + "^=", + "&=", + "&&", + "||" + ].reduce(function(tests, illegalOperator) { + tests.push({ + code: "var obj = { get " + illegalOperator + "() { return test; }};", + operator: illegalOperator, + }); + tests.push({ + code: "var obj = { set " + illegalOperator + "(value) { test = value; }};", + operator: illegalOperator, + }); + return tests; + }, []).forEach(function(test) { + assert.throws(function() { + UglifyJS.parse(test.code); + }, test.operator == "=" ? function(e) { + return e instanceof UglifyJS.JS_Parse_Error + && /^Unexpected token: punc «{», expected: punc «.*?»$/.test(e.message); + } : function(e) { + return e instanceof UglifyJS.JS_Parse_Error + && e.message === "Unexpected token: operator «" + test.operator + "»"; + }, "Expected but didn't get a syntax error while parsing following line:\n" + test.code); + }); + }); +}); diff --git a/test/mocha/glob.js b/test/mocha/glob.js new file mode 100644 index 00000000000..7fe92e65b4b --- /dev/null +++ b/test/mocha/glob.js @@ -0,0 +1,92 @@ +var assert = require("assert"); +var exec = require("child_process").exec; +var path = require("path"); + +describe("bin/uglifyjs with input file globs", function() { + var uglifyjscmd = '"' + process.argv[0] + '" bin/uglifyjs'; + it("bin/uglifyjs with one input file extension glob.", function(done) { + var command = [ + uglifyjscmd, + '"test/input/issue-1242/foo.*"', + "--compress", + "--mangle", + "--no-module", + ].join(" "); + exec(command, function(err, stdout) { + if (err) throw err; + assert.strictEqual(stdout, 'var print=console.log.bind(console);function foo(o){print("Foo:",2*o)}\n'); + done(); + }); + }); + it("bin/uglifyjs with one input file name glob.", function(done) { + var command = [ + uglifyjscmd, + '"test/input/issue-1242/b*.es5"', + "--compress", + "--mangle", + "--no-module", + ].join(" "); + exec(command, function(err, stdout) { + if (err) throw err; + assert.strictEqual(stdout, 'function bar(n){return 3*n}function baz(n){return n/2}\n'); + done(); + }); + }); + it("bin/uglifyjs with multiple input file globs.", function(done) { + var command = [ + uglifyjscmd, + '"test/input/issue-1242/???.es5"', + '"test/input/issue-1242/*.js"', + "--compress", "toplevel,passes=3", + "--mangle", + "--no-module", + ].join(" "); + exec(command, function(err, stdout) { + if (err) throw err; + assert.strictEqual(stdout, 'var print=console.log.bind(console);print("qux",9,6),print("Foo:",22);\n'); + done(); + }); + }); + it("Should throw with non-matching glob string", function(done) { + var command = uglifyjscmd + ' "test/input/issue-1242/blah.*"'; + + exec(command, function(err, stdout, stderr) { + assert.ok(err); + assert.ok(/^ERROR: ENOENT/.test(stderr)); + done(); + }); + }); + it('"?" in glob string should not match "/"', function(done) { + var command = uglifyjscmd + ' "test/input?issue-1242/foo.*"'; + + exec(command, function(err, stdout, stderr) { + assert.ok(err); + assert.ok(/^ERROR: ENOENT/.test(stderr)); + done(); + }); + }); + it("Should handle special characters in glob string", function(done) { + var command = uglifyjscmd + ' "test/input/issue-1632/^{*}[???](*)+$.??" -cm'; + + exec(command, function(err, stdout) { + if (err) throw err; + + assert.strictEqual(stdout, "console.log(x);\n"); + done(); + }); + }); + it("Should handle array of glob strings - matching and otherwise", function(done) { + var dir = "test/input/issue-1242"; + var command = uglifyjscmd + ' "' + [ + path.join(dir, "b*.es5"), + path.join(dir, "z*.es5"), + path.join(dir, "*.js") + ].join('" "') + '"'; + + exec(command, function(err, stdout, stderr) { + assert.ok(err); + assert.ok(/^ERROR: ENOENT.*?z\*\.es5/.test(stderr)); + done(); + }); + }); +}); diff --git a/test/mocha/imports.js b/test/mocha/imports.js new file mode 100644 index 00000000000..3b2298eefab --- /dev/null +++ b/test/mocha/imports.js @@ -0,0 +1,109 @@ +var acorn = require("acorn"); +var assert = require("assert"); +var UglifyJS = require("../node"); + +describe("import", function() { + it("Should reject invalid `import` statement syntax", function() { + [ + "import *;", + "import A;", + "import {};", + "import `path`;", + "{ import 'path'; }", + "import from 'path';", + "if (0) import 'path';", + "import * from 'path';", + "import 'A' from 'path';", + "import A-B from 'path';", + "import A as B from 'path';", + "import { A }, B from 'path';", + "import * as 'A' from 'path';", + "import * as A-B from 'path';", + "import * as A, B from 'path';", + "import * as A, {} from 'path';", + "import { * as A } from 'path';", + "import { * as 'A' } from 'path';", + "import { * as A-B } from 'path';", + "function f() { import 'path'; }", + "import { 42 as A } from 'path';", + "import { A-B as C } from 'path';", + "import { 'A' as 'B' } from 'path';", + ].forEach(function(code) { + assert.throws(function() { + UglifyJS.parse(code); + }, function(e) { + return e instanceof UglifyJS.JS_Parse_Error; + }, code); + }); + }); + it("Should compare `import` statements correctly", function() { + [ + "import 'foo';", + "import 'path';", + "import A from 'path';", + "import { A } from 'path';", + "import * as A from 'path';", + "import A, { B } from 'path';", + "import A, * as B from 'path';", + "import { A as B } from 'path';", + "import A, { B, C as D } from 'path';", + ].forEach(function(c, i, stats) { + var s = UglifyJS.parse(c); + assert.ok(s instanceof UglifyJS.AST_Toplevel, c); + assert.strictEqual(s.body.length, 1, c); + assert.ok(s.body[0] instanceof UglifyJS.AST_Import, c); + stats.forEach(function(d, j) { + var t = UglifyJS.parse(d); + assert.ok(t instanceof UglifyJS.AST_Toplevel, d); + assert.strictEqual(t.body.length, 1, d); + assert.ok(t.body[0] instanceof UglifyJS.AST_Import, d); + assert.strictEqual(s.equals(t), i === j, c + "\n" + d); + }); + }); + }); + it("Should interoperate with ESTree correctly", function() { + [ + "import A from 'path';", + "import * as A from 'path';", + "import A, * as B from 'path';", + "import { '42' as A, B } from 'path';", + "import A, { '42' as B } from 'path';", + ].forEach(function(code) { + var ast = UglifyJS.parse(code); + try { + var spidermonkey = ast.to_mozilla_ast(); + } catch (ex) { + assert.fail("[to_mozilla_ast] " + ex.stack); + } + try { + var ast2 = UglifyJS.AST_Node.from_mozilla_ast(spidermonkey); + } catch (ex) { + assert.fail("[from_mozilla_ast] " + ex.stack); + } + assert.strictEqual(ast2.print_to_string(), ast.print_to_string(), [ + "spidermonkey:", + ast.print_to_string(), + ast2.print_to_string(), + ].join("\n")); + try { + var acorn_est = acorn.parse(code, { + ecmaVersion: "latest", + locations: true, + sourceType: "module", + }); + } catch (ex) { + assert.fail("[acorn.parse] " + ex.stack); + } + try { + var ast3 = UglifyJS.AST_Node.from_mozilla_ast(acorn_est); + } catch (ex) { + assert.fail("[from_acorn] " + ex.stack); + } + assert.strictEqual(ast3.print_to_string(), ast.print_to_string(), [ + "acorn:", + ast.print_to_string(), + ast3.print_to_string(), + ].join("\n")); + }); + }); +}); diff --git a/test/mocha/let.js b/test/mocha/let.js new file mode 100644 index 00000000000..7826520a2d0 --- /dev/null +++ b/test/mocha/let.js @@ -0,0 +1,97 @@ +var assert = require("assert"); +var UglifyJS = require("../node"); + +describe("let", function() { + this.timeout(30000); + it("Should not produce reserved keywords as variable name in mangle", function() { + // Produce a lot of variables in a function and run it through mangle. + var s = '"dddddeeeeelllllooooottttt"; function foo() {'; + for (var i = 0; i < 18000; i++) { + s += "var v" + i + "=[];"; + } + s += '}'; + var result = UglifyJS.minify(s, { + compress: false, + }); + if (result.error) throw result.error; + // Verify that select keywords and reserved keywords not produced + [ + "do", + "let", + "var", + ].forEach(function(name) { + assert.strictEqual(result.code.indexOf("var " + name + "="), -1); + }); + // Verify that the variable names that appeared immediately before + // and after the erroneously generated variable name still exist + // to show the test generated enough symbols. + [ + "to", "eo", + "eet", "fet", + "rar", "oar", + ].forEach(function(name) { + assert.notStrictEqual(result.code.indexOf("var " + name + "="), -1); + }); + }); + it("Should quote mangled properties that are reserved keywords", function() { + var s = '"rrrrrnnnnniiiiiaaaaa";'; + for (var i = 0; i < 18000; i++) { + s += "v.b" + i + "=v;"; + } + var result = UglifyJS.minify(s, { + compress: false, + ie: true, + mangle: { + properties: { + domprops: true, + }, + }, + }); + if (result.error) throw result.error; + [ + "in", + "var", + ].forEach(function(name) { + assert.notStrictEqual(result.code.indexOf('v["' + name + '"]'), -1); + }); + }); + it("Should parse `let` as name correctly", function() { + [ + "for(var let;let;let)let;", + "function let(let){let}", + ].forEach(function(code) { + var ast = UglifyJS.parse(code); + assert.strictEqual(ast.print_to_string(), code); + assert.throws(function() { + UglifyJS.parse('"use strict";' + code); + }, function(e) { + return e instanceof UglifyJS.JS_Parse_Error && e.message == "Unexpected let in strict mode"; + }, code); + }); + }); + it("Should throw on ambiguous use of `let`", function() { + [ + "export let", + [ + "let", + "console.log(42)", + ].join("\n"), + [ + "let", + "[ console.log(42) ]", + ].join("\n"), + [ + "let", + "{", + " console.log(42)", + "}", + ].join("\n"), + ].forEach(function(code) { + assert.throws(function() { + UglifyJS.parse(code); + }, function(e) { + return e instanceof UglifyJS.JS_Parse_Error; + }, code); + }); + }); +}); diff --git a/test/mocha/line-endings.js b/test/mocha/line-endings.js new file mode 100644 index 00000000000..07e85faff4f --- /dev/null +++ b/test/mocha/line-endings.js @@ -0,0 +1,58 @@ +var assert = require("assert"); +var UglifyJS = require("../node"); + +describe("line-endings", function() { + var options = { + compress: false, + mangle: false, + output: { + beautify: false, + comments: /^!/, + } + }; + var expected_code = '/*!one\n2\n3*/\nfunction f(x){if(x)return 3}'; + + it("Should parse LF line endings", function() { + var js = '/*!one\n2\n3*///comment\nfunction f(x) {\n if (x)\n//comment\n return 3;\n}\n'; + var result = UglifyJS.minify(js, options); + assert.strictEqual(result.code, expected_code); + }); + + it("Should parse CR/LF line endings", function() { + var js = '/*!one\r\n2\r\n3*///comment\r\nfunction f(x) {\r\n if (x)\r\n//comment\r\n return 3;\r\n}\r\n'; + var result = UglifyJS.minify(js, options); + assert.strictEqual(result.code, expected_code); + }); + + it("Should parse CR line endings", function() { + var js = '/*!one\r2\r3*///comment\rfunction f(x) {\r if (x)\r//comment\r return 3;\r}\r'; + var result = UglifyJS.minify(js, options); + assert.strictEqual(result.code, expected_code); + }); + + it("Should not allow line terminators in regexp", function() { + var inputs = [ + "/\n/", + "/\r/", + "/\u2028/", + "/\u2029/", + "/\\\n/", + "/\\\r/", + "/\\\u2028/", + "/\\\u2029/", + "/someRandomTextLike[]()*AndThen\n/" + ] + var test = function(input) { + return function() { + UglifyJS.parse(input); + } + } + var fail = function(e) { + return e instanceof UglifyJS.JS_Parse_Error + && e.message === "Unexpected line terminator"; + } + for (var i = 0; i < inputs.length; i++) { + assert.throws(test(inputs[i]), fail); + } + }); +}); diff --git a/test/mocha/minify-file-map.js b/test/mocha/minify-file-map.js new file mode 100644 index 00000000000..29005845f02 --- /dev/null +++ b/test/mocha/minify-file-map.js @@ -0,0 +1,54 @@ +var assert = require("assert"); +var UglifyJS = require("../.."); + +describe("Input file as map", function() { + it("Should accept object", function() { + var jsMap = { + '/scripts/foo.js': 'var foo = {"x": 1, y: 2, \'z\': 3};' + }; + var result = UglifyJS.minify(jsMap, { + sourceMap: true, + }); + if (result.error) throw result.error; + var map = JSON.parse(result.map); + assert.strictEqual(result.code, 'var foo={x:1,y:2,z:3};'); + assert.deepEqual(map.sources, ['/scripts/foo.js']); + assert.strictEqual(map.file, undefined); + + result = UglifyJS.minify(jsMap); + assert.strictEqual(result.map, undefined); + + result = UglifyJS.minify(jsMap, {sourceMap: {filename: 'out.js'}}); + map = JSON.parse(result.map); + assert.strictEqual(map.file, 'out.js'); + }); + + it("Should accept array of strings", function() { + var jsSeq = [ + 'var foo = {"x": 1, y: 2, \'z\': 3};', + 'var bar = 15;' + ]; + var result = UglifyJS.minify(jsSeq, { + sourceMap: true, + }); + if (result.error) throw result.error; + var map = JSON.parse(result.map); + assert.strictEqual(result.code, 'var foo={x:1,y:2,z:3},bar=15;'); + assert.deepEqual(map.sources, ['0', '1']); + }); + + it("Should correctly include source", function() { + var jsMap = { + '/scripts/foo.js': 'var foo = {"x": 1, y: 2, \'z\': 3};' + }; + var result = UglifyJS.minify(jsMap, { + sourceMap: { + includeSources: true, + }, + }); + if (result.error) throw result.error; + var map = JSON.parse(result.map); + assert.strictEqual(result.code, 'var foo={x:1,y:2,z:3};'); + assert.deepEqual(map.sourcesContent, ['var foo = {"x": 1, y: 2, \'z\': 3};']); + }); +}); diff --git a/test/mocha/minify.js b/test/mocha/minify.js new file mode 100644 index 00000000000..e63acd82d26 --- /dev/null +++ b/test/mocha/minify.js @@ -0,0 +1,399 @@ +var assert = require("assert"); +var readFileSync = require("fs").readFileSync; +var run_code = require("../sandbox").run_code; +var semver = require("semver"); +var UglifyJS = require("../.."); + +function read(path) { + return readFileSync(path, "utf8"); +} + +describe("minify", function() { + it("Should test basic sanity of minify with default options", function() { + var js = "function foo(bar) { if (bar) return 3; else return 7; var u = not_called(); }"; + var result = UglifyJS.minify(js); + if (result.error) throw result.error; + assert.strictEqual(result.code, "function foo(n){return n?3:7}"); + }); + it("Should not mutate minify `options`", function() { + var options = { + compress: true, + mangle: false, + output: {}, + }; + var value = JSON.stringify(options); + var result = UglifyJS.minify("print(6 * 7);", options); + if (result.error) throw result.error; + assert.strictEqual(result.code, "print(42);"); + assert.strictEqual(JSON.stringify(options), value); + }); + it("Should skip inherited keys from `files`", function() { + var files = Object.create({ skip: this }); + files[0] = "alert(1 + 1)"; + var result = UglifyJS.minify(files); + if (result.error) throw result.error; + assert.strictEqual(result.code, "alert(2);"); + }); + it("Should work with mangle.cache", function() { + var cache = {}; + var original = ""; + var compressed = ""; + [ + "bar.es5", + "baz.es5", + "foo.es5", + "qux.js", + ].forEach(function(file) { + var code = read("test/input/issue-1242/" + file); + var result = UglifyJS.minify(code, { + mangle: { + cache: cache, + toplevel: true, + }, + }); + if (result.error) throw result.error; + original += code; + compressed += result.code; + }); + assert.strictEqual(JSON.stringify(cache).slice(0, 10), '{"props":{'); + assert.strictEqual(compressed, [ + "function n(n){return 3*n}", + "function r(n){return n/2}", + "var c=console.log.bind(console);", + 'function o(o){c("Foo:",2*o)}', + "var a=n(3),b=r(12);", + 'c("qux",a,b),o(11);', + ].join("")); + assert.strictEqual(run_code(compressed, true), run_code(original, true)); + }); + it("Should work with nameCache", function() { + var cache = {}; + var original = ""; + var compressed = ""; + [ + "bar.es5", + "baz.es5", + "foo.es5", + "qux.js", + ].forEach(function(file) { + var code = read("test/input/issue-1242/" + file); + var result = UglifyJS.minify(code, { + mangle: { + toplevel: true, + }, + nameCache: cache, + }); + if (result.error) throw result.error; + original += code; + compressed += result.code; + }); + assert.strictEqual(JSON.stringify(cache).slice(0, 18), '{"vars":{"props":{'); + assert.strictEqual(compressed, [ + "function n(n){return 3*n}", + "function r(n){return n/2}", + "var c=console.log.bind(console);", + 'function o(o){c("Foo:",2*o)}', + "var a=n(3),b=r(12);", + 'c("qux",a,b),o(11);', + ].join("")); + assert.strictEqual(run_code(compressed, true), run_code(original, true)); + }); + it("Should avoid cached names when mangling top-level variables", function() { + var cache = {}; + var original = ""; + var compressed = ""; + [ + '"xxxyy";var i={s:1};', + '"xxyyy";var j={t:2,u:3},k=4;', + 'console.log(i.s,j.t,j.u,k);', + ].forEach(function(code) { + var result = UglifyJS.minify(code, { + compress: false, + mangle: { + properties: { + domprops: true, + }, + toplevel: true, + }, + nameCache: cache, + }); + if (result.error) throw result.error; + original += code; + compressed += result.code; + }); + assert.strictEqual(compressed, [ + '"xxxyy";var x={x:1};', + '"xxyyy";var y={y:2,a:3},a=4;', + 'console.log(x.x,y.y,y.a,a);', + ].join("")); + assert.strictEqual(run_code(compressed, true), run_code(original, true)); + }); + it("Should avoid cached names when mangling inner-scoped variables", function() { + var cache = {}; + var original = ""; + var compressed = ""; + [ + 'var extend = function(a, b) { console.log("extend"); a(); b(); }; function A() { console.log("A"); };', + 'var B = function(A) { function B() { console.log("B") }; extend(B, A); return B; }(A);', + ].forEach(function(code) { + var result = UglifyJS.minify(code, { + compress: false, + nameCache: cache, + toplevel: true, + }); + if (result.error) throw result.error; + original += code; + compressed += result.code; + }); + assert.strictEqual(compressed, [ + 'var o=function(o,n){console.log("extend");o();n()};function n(){console.log("A")}', + 'var e=function(n){function e(){console.log("B")}o(e,n);return e}(n);', + ].join("")); + assert.strictEqual(run_code(compressed, true), run_code(original, true)); + }); + it("Should not parse invalid use of reserved words", function() { + assert.strictEqual(UglifyJS.minify("function enum(){}").error, undefined); + assert.strictEqual(UglifyJS.minify("function static(){}").error, undefined); + assert.strictEqual(UglifyJS.minify("function this(){}").error.message, "Unexpected token: name «this»"); + }); + + describe("keep_quoted_props", function() { + it("Should preserve quotes in object literals", function() { + var js = 'var foo = {"x": 1, y: 2, \'z\': 3};'; + var result = UglifyJS.minify(js, { + output: { + keep_quoted_props: true, + }, + }); + assert.strictEqual(result.code, 'var foo={"x":1,y:2,"z":3};'); + }); + it("Should preserve quote styles when quote_style is 3", function() { + var js = 'var foo = {"x": 1, y: 2, \'z\': 3};'; + var result = UglifyJS.minify(js, { + output: { + keep_quoted_props: true, + quote_style: 3, + }, + }); + assert.strictEqual(result.code, 'var foo={"x":1,y:2,\'z\':3};'); + }); + it("Should not preserve quotes in object literals when disabled", function() { + var js = 'var foo = {"x": 1, y: 2, \'z\': 3};'; + var result = UglifyJS.minify(js, { + output: { + keep_quoted_props: false, + quote_style: 3, + }, + }); + assert.strictEqual(result.code, 'var foo={x:1,y:2,z:3};'); + }); + }); + + describe("mangleProperties", function() { + it("Shouldn't mangle quoted properties", function() { + var js = 'a["foo"] = "bar"; a.color = "red"; x = {"bar": 10};'; + var result = UglifyJS.minify(js, { + compress: true, + mangle: { + properties: { + domprops: true, + keep_quoted: true, + }, + }, + output: { + keep_quoted_props: true, + quote_style: 3, + }, + }); + assert.strictEqual(result.code, 'a["foo"]="bar",a.a="red",x={"bar":10};'); + }); + it("Should not mangle quoted property within dead code", function() { + var result = UglifyJS.minify('({ "keep": 1 }); g.keep = g.change = 42;', { + mangle: { + properties: { + keep_quoted: true, + }, + }, + }); + if (result.error) throw result.error; + assert.strictEqual(result.code, "g.keep=g.g=42;"); + }); + }); + + describe("#__PURE__", function() { + it("Should drop #__PURE__ hint after use", function() { + var result = UglifyJS.minify('//@__PURE__ comment1 #__PURE__ comment2\n foo(), bar();', { + output: { + comments: "all", + beautify: false, + }, + }); + var code = result.code; + assert.strictEqual(code, "// comment1 comment2\nbar();"); + }); + it("Should drop #__PURE__ hint if function is retained", function() { + var result = UglifyJS.minify("var a = /*#__PURE__*/(function(){ foo(); })();", { + output: { + comments: "all", + beautify: false, + }, + }); + var code = result.code; + assert.strictEqual(code, "var a=function(){foo()}();"); + }); + }); + + describe("JS_Parse_Error", function() { + it("Should return syntax error", function() { + var result = UglifyJS.minify("function f(a{}"); + var err = result.error; + assert.ok(err instanceof Error); + assert.strictEqual(err.stack.split(/\n/)[0], "SyntaxError: Unexpected token: punc «{», expected: punc «,»"); + assert.strictEqual(err.filename, "0"); + assert.strictEqual(err.line, 1); + assert.strictEqual(err.col, 12); + }); + it("Should reject duplicated label name", function() { + var result = UglifyJS.minify("L:{L:{}}"); + var err = result.error; + assert.ok(err instanceof Error); + assert.strictEqual(err.stack.split(/\n/)[0], "SyntaxError: Label L defined twice"); + assert.strictEqual(err.filename, "0"); + assert.strictEqual(err.line, 1); + assert.strictEqual(err.col, 4); + }); + }); + + describe("global_defs", function() { + it("Should throw for non-trivial expressions", function() { + var result = UglifyJS.minify("alert(42);", { + compress: { + global_defs: { + "@alert": "debugger" + } + } + }); + var err = result.error; + assert.ok(err instanceof Error); + assert.strictEqual(err.stack.split(/\n/)[0], "SyntaxError: Unexpected token: keyword «debugger»"); + }); + it("Should skip inherited properties", function() { + var foo = Object.create({ skip: this }); + foo.bar = 42; + var result = UglifyJS.minify("alert(FOO);", { + compress: { + global_defs: { + FOO: foo + } + } + }); + assert.strictEqual(result.code, "alert({bar:42});"); + }); + }); + + describe("collapse_vars", function() { + it("Should not produce invalid AST", function() { + var code = [ + "function f(a) {", + " a = x();", + " return a;", + "}", + "f();", + ].join("\n"); + var ast = UglifyJS.minify(code, { + compress: false, + mangle: false, + output: { + ast: true, + }, + }).ast; + assert.strictEqual(ast.TYPE, "Toplevel"); + assert.strictEqual(ast.body.length, 2); + assert.strictEqual(ast.body[0].TYPE, "Defun"); + assert.strictEqual(ast.body[0].body.length, 2); + assert.strictEqual(ast.body[0].body[0].TYPE, "SimpleStatement"); + var stat = ast.body[0].body[0]; + UglifyJS.minify(ast, { + compress: { + sequences: false, + }, + mangle: false, + }); + assert.ok(stat.body); + assert.strictEqual(stat.print_to_string(), "a=x()"); + }); + }); + + describe("module", function() { + it("Should not inline `await` variables", function() { + if (semver.satisfies(process.version, "<8")) return; + var code = [ + "console.log(function() {", + " return typeof await;", + "}());", + ].join("\n"); + assert.strictEqual(run_code("(async function(){" + code + "})();"), "undefined\n"); + var result = UglifyJS.minify(code, { + module: true, + }); + if (result.error) throw result.error; + assert.strictEqual(result.code, "console.log(function(){return typeof await}());"); + assert.strictEqual(run_code("(async function(){" + result.code + "})();"), "undefined\n"); + }); + }); + + describe("rename", function() { + it("Should be repeatable", function() { + var code = "!function(x){return x(x)}(y);"; + for (var i = 0; i < 2; i++) { + assert.strictEqual(UglifyJS.minify(code, { + compress: { + toplevel: true, + }, + rename: true, + }).code, "var a;(a=y)(a);"); + } + }); + }); + + describe("enclose", function() { + var code = read("test/input/enclose/input.js"); + it("Should work with true", function() { + var result = UglifyJS.minify(code, { + compress: false, + enclose: true, + mangle: false, + }); + if (result.error) throw result.error; + assert.strictEqual(result.code, '(function(){function enclose(){console.log("test enclose")}enclose()})();'); + }); + it("Should work with arg", function() { + var result = UglifyJS.minify(code, { + compress: false, + enclose: 'undefined', + mangle: false, + }); + if (result.error) throw result.error; + assert.strictEqual(result.code, '(function(undefined){function enclose(){console.log("test enclose")}enclose()})();'); + }); + it("Should work with arg:value", function() { + var result = UglifyJS.minify(code, { + compress: false, + enclose: 'window,undefined:window', + mangle: false, + }); + if (result.error) throw result.error; + assert.strictEqual(result.code, '(function(window,undefined){function enclose(){console.log("test enclose")}enclose()})(window);'); + }); + it("Should work alongside wrap", function() { + var result = UglifyJS.minify(code, { + compress: false, + enclose: 'window,undefined:window', + mangle: false, + wrap: 'exports', + }); + if (result.error) throw result.error; + assert.strictEqual(result.code, '(function(exports){(function(window,undefined){function enclose(){console.log("test enclose")}enclose()})(window)})(typeof exports=="undefined"?exports={}:exports);'); + }); + }); +}); diff --git a/test/mocha/number-literal.js b/test/mocha/number-literal.js new file mode 100644 index 00000000000..e17e5ef917e --- /dev/null +++ b/test/mocha/number-literal.js @@ -0,0 +1,99 @@ +var assert = require("assert"); +var UglifyJS = require("../node"); + +describe("Number literals", function() { + it("Should allow legacy octal literals in non-strict mode", function() { + [ + "'use strict'\n.slice()\n00", + '"use strict"\n.slice()\nvar foo = 00', + ].forEach(function(input) { + UglifyJS.parse(input); + }); + }); + it("Should not allow legacy octal literals in strict mode", function() { + var inputs = [ + '"use strict";00;', + '"use strict"; var foo = 00;', + ]; + var test = function(input) { + return function() { + UglifyJS.parse(input); + } + }; + var error = function(e) { + return e instanceof UglifyJS.JS_Parse_Error + && e.message === "Legacy octal literals are not allowed in strict mode"; + }; + for (var i = 0; i < inputs.length; i++) { + assert.throws(test(inputs[i]), error, inputs[i]); + } + }); + it("Should parse binary, hexadecimal, octal and underscore correctly", function() { + [ + "42", + "4_2", + "052", + "0o52", + "0O52", + "0o5_2", + "0x2a", + "0X2A", + "0x2_a", + "0b101010", + "0B101010", + "0b101_010", + "0.0000000042e+10", + "0.0000000042E+10", + "0.0_000000042e+10", + "0.0000000042e+1_0", + "0.000_000_004_2e+1_0", + "0.000_000_004_2e+1_0-0B101_010+0x2_A-0o5_2+4_2", + ].forEach(function(code) { + var result = UglifyJS.minify(code, { + expression: true, + }); + if (result.error) throw result.error; + assert.strictEqual(result.code, "42"); + }); + }); + it("Should reject invalid use of underscore", function() { + [ + "_42", + "_+42", + "+_42", + ].forEach(function(code) { + var node = UglifyJS.parse(code, { + expression: true, + }); + assert.ok(!node.is_constant(), code); + assert.ok(!(node instanceof UglifyJS.AST_Statement), code); + }); + [ + "42_", + "4__2", + "0_52", + "05_2", + "0_o52", + "0o_52", + "0.0000000042_e10", + "0.0000000042e_10", + "0.0000000042e_+10", + "0.0000000042e+_10", + ].forEach(function(code) { + assert.throws(function() { + UglifyJS.parse(code); + }, function(e) { + return e instanceof UglifyJS.JS_Parse_Error; + }, code); + }); + }); + it("Should reject invalid syntax under expression=true", function() { + assert.throws(function() { + UglifyJS.parse("42.g", { + expression: true, + }); + }, function(e) { + return e instanceof UglifyJS.JS_Parse_Error; + }); + }); +}); diff --git a/test/mocha/operator.js b/test/mocha/operator.js new file mode 100644 index 00000000000..8dfea547c1a --- /dev/null +++ b/test/mocha/operator.js @@ -0,0 +1,504 @@ +var assert = require("assert"); +var UglifyJS = require("../.."); + +describe("operator", function() { + it("Should handle mixing of ++/+/--/- correctly", function() { + function evaluate(exp) { + return new Function("var a=1,b=2,c=" + exp + ";return{a:a,b:b,c:c}")(); + } + + [ "", "+", "-" ].forEach(function(p) { + [ "++a", "--a", "a", "a--", "a++" ].forEach(function(a) { + [ "+", "-" ].forEach(function(o) { + [ "", "+", "-" ].forEach(function(q) { + [ "++b", "--b", "b", "b--", "b++" ].forEach(function(b) { + var exp = [p, a, o, q, b].join(" "); + var orig = evaluate(exp); + var uglify = evaluate(UglifyJS.parse(exp).print_to_string()); + assert.strictEqual(orig.a, uglify.a); + assert.strictEqual(orig.b, uglify.b); + assert.strictEqual(orig.c, uglify.c); + var beautify = evaluate(UglifyJS.parse(exp).print_to_string({ + beautify: true + })); + assert.strictEqual(orig.a, beautify.a); + assert.strictEqual(orig.b, beautify.b); + assert.strictEqual(orig.c, beautify.c); + }); + }); + }); + }); + }); + }); + it("Should remove extraneous spaces", function() { + [ + [ "++a + ++b", "++a+ ++b" ], + [ "++a + --b", "++a+--b" ], + [ "++a + b", "++a+b" ], + [ "++a + b--", "++a+b--" ], + [ "++a + b++", "++a+b++" ], + [ "++a + + ++b", "++a+ + ++b" ], + [ "++a + + --b", "++a+ +--b" ], + [ "++a + + b", "++a+ +b" ], + [ "++a + + b--", "++a+ +b--" ], + [ "++a + + b++", "++a+ +b++" ], + [ "++a + - ++b", "++a+-++b" ], + [ "++a + - --b", "++a+- --b" ], + [ "++a + - b", "++a+-b" ], + [ "++a + - b--", "++a+-b--" ], + [ "++a + - b++", "++a+-b++" ], + [ "++a - ++b", "++a-++b" ], + [ "++a - --b", "++a- --b" ], + [ "++a - b", "++a-b" ], + [ "++a - b--", "++a-b--" ], + [ "++a - b++", "++a-b++" ], + [ "++a - + ++b", "++a-+ ++b" ], + [ "++a - + --b", "++a-+--b" ], + [ "++a - + b", "++a-+b" ], + [ "++a - + b--", "++a-+b--" ], + [ "++a - + b++", "++a-+b++" ], + [ "++a - - ++b", "++a- -++b" ], + [ "++a - - --b", "++a- - --b" ], + [ "++a - - b", "++a- -b" ], + [ "++a - - b--", "++a- -b--" ], + [ "++a - - b++", "++a- -b++" ], + [ "--a + ++b", "--a+ ++b" ], + [ "--a + --b", "--a+--b" ], + [ "--a + b", "--a+b" ], + [ "--a + b--", "--a+b--" ], + [ "--a + b++", "--a+b++" ], + [ "--a + + ++b", "--a+ + ++b" ], + [ "--a + + --b", "--a+ +--b" ], + [ "--a + + b", "--a+ +b" ], + [ "--a + + b--", "--a+ +b--" ], + [ "--a + + b++", "--a+ +b++" ], + [ "--a + - ++b", "--a+-++b" ], + [ "--a + - --b", "--a+- --b" ], + [ "--a + - b", "--a+-b" ], + [ "--a + - b--", "--a+-b--" ], + [ "--a + - b++", "--a+-b++" ], + [ "--a - ++b", "--a-++b" ], + [ "--a - --b", "--a- --b" ], + [ "--a - b", "--a-b" ], + [ "--a - b--", "--a-b--" ], + [ "--a - b++", "--a-b++" ], + [ "--a - + ++b", "--a-+ ++b" ], + [ "--a - + --b", "--a-+--b" ], + [ "--a - + b", "--a-+b" ], + [ "--a - + b--", "--a-+b--" ], + [ "--a - + b++", "--a-+b++" ], + [ "--a - - ++b", "--a- -++b" ], + [ "--a - - --b", "--a- - --b" ], + [ "--a - - b", "--a- -b" ], + [ "--a - - b--", "--a- -b--" ], + [ "--a - - b++", "--a- -b++" ], + [ "a + ++b", "a+ ++b" ], + [ "a + --b", "a+--b" ], + [ "a + b", "a+b" ], + [ "a + b--", "a+b--" ], + [ "a + b++", "a+b++" ], + [ "a + + ++b", "a+ + ++b" ], + [ "a + + --b", "a+ +--b" ], + [ "a + + b", "a+ +b" ], + [ "a + + b--", "a+ +b--" ], + [ "a + + b++", "a+ +b++" ], + [ "a + - ++b", "a+-++b" ], + [ "a + - --b", "a+- --b" ], + [ "a + - b", "a+-b" ], + [ "a + - b--", "a+-b--" ], + [ "a + - b++", "a+-b++" ], + [ "a - ++b", "a-++b" ], + [ "a - --b", "a- --b" ], + [ "a - b", "a-b" ], + [ "a - b--", "a-b--" ], + [ "a - b++", "a-b++" ], + [ "a - + ++b", "a-+ ++b" ], + [ "a - + --b", "a-+--b" ], + [ "a - + b", "a-+b" ], + [ "a - + b--", "a-+b--" ], + [ "a - + b++", "a-+b++" ], + [ "a - - ++b", "a- -++b" ], + [ "a - - --b", "a- - --b" ], + [ "a - - b", "a- -b" ], + [ "a - - b--", "a- -b--" ], + [ "a - - b++", "a- -b++" ], + [ "a-- + ++b", "a--+ ++b" ], + [ "a-- + --b", "a--+--b" ], + [ "a-- + b", "a--+b" ], + [ "a-- + b--", "a--+b--" ], + [ "a-- + b++", "a--+b++" ], + [ "a-- + + ++b", "a--+ + ++b" ], + [ "a-- + + --b", "a--+ +--b" ], + [ "a-- + + b", "a--+ +b" ], + [ "a-- + + b--", "a--+ +b--" ], + [ "a-- + + b++", "a--+ +b++" ], + [ "a-- + - ++b", "a--+-++b" ], + [ "a-- + - --b", "a--+- --b" ], + [ "a-- + - b", "a--+-b" ], + [ "a-- + - b--", "a--+-b--" ], + [ "a-- + - b++", "a--+-b++" ], + [ "a-- - ++b", "a---++b" ], + [ "a-- - --b", "a--- --b" ], + [ "a-- - b", "a---b" ], + [ "a-- - b--", "a---b--" ], + [ "a-- - b++", "a---b++" ], + [ "a-- - + ++b", "a---+ ++b" ], + [ "a-- - + --b", "a---+--b" ], + [ "a-- - + b", "a---+b" ], + [ "a-- - + b--", "a---+b--" ], + [ "a-- - + b++", "a---+b++" ], + [ "a-- - - ++b", "a--- -++b" ], + [ "a-- - - --b", "a--- - --b" ], + [ "a-- - - b", "a--- -b" ], + [ "a-- - - b--", "a--- -b--" ], + [ "a-- - - b++", "a--- -b++" ], + [ "a++ + ++b", "a+++ ++b" ], + [ "a++ + --b", "a+++--b" ], + [ "a++ + b", "a+++b" ], + [ "a++ + b--", "a+++b--" ], + [ "a++ + b++", "a+++b++" ], + [ "a++ + + ++b", "a+++ + ++b" ], + [ "a++ + + --b", "a+++ +--b" ], + [ "a++ + + b", "a+++ +b" ], + [ "a++ + + b--", "a+++ +b--" ], + [ "a++ + + b++", "a+++ +b++" ], + [ "a++ + - ++b", "a+++-++b" ], + [ "a++ + - --b", "a+++- --b" ], + [ "a++ + - b", "a+++-b" ], + [ "a++ + - b--", "a+++-b--" ], + [ "a++ + - b++", "a+++-b++" ], + [ "a++ - ++b", "a++-++b" ], + [ "a++ - --b", "a++- --b" ], + [ "a++ - b", "a++-b" ], + [ "a++ - b--", "a++-b--" ], + [ "a++ - b++", "a++-b++" ], + [ "a++ - + ++b", "a++-+ ++b" ], + [ "a++ - + --b", "a++-+--b" ], + [ "a++ - + b", "a++-+b" ], + [ "a++ - + b--", "a++-+b--" ], + [ "a++ - + b++", "a++-+b++" ], + [ "a++ - - ++b", "a++- -++b" ], + [ "a++ - - --b", "a++- - --b" ], + [ "a++ - - b", "a++- -b" ], + [ "a++ - - b--", "a++- -b--" ], + [ "a++ - - b++", "a++- -b++" ], + [ "+ ++a + ++b", "+ ++a+ ++b" ], + [ "+ ++a + --b", "+ ++a+--b" ], + [ "+ ++a + b", "+ ++a+b" ], + [ "+ ++a + b--", "+ ++a+b--" ], + [ "+ ++a + b++", "+ ++a+b++" ], + [ "+ ++a + + ++b", "+ ++a+ + ++b" ], + [ "+ ++a + + --b", "+ ++a+ +--b" ], + [ "+ ++a + + b", "+ ++a+ +b" ], + [ "+ ++a + + b--", "+ ++a+ +b--" ], + [ "+ ++a + + b++", "+ ++a+ +b++" ], + [ "+ ++a + - ++b", "+ ++a+-++b" ], + [ "+ ++a + - --b", "+ ++a+- --b" ], + [ "+ ++a + - b", "+ ++a+-b" ], + [ "+ ++a + - b--", "+ ++a+-b--" ], + [ "+ ++a + - b++", "+ ++a+-b++" ], + [ "+ ++a - ++b", "+ ++a-++b" ], + [ "+ ++a - --b", "+ ++a- --b" ], + [ "+ ++a - b", "+ ++a-b" ], + [ "+ ++a - b--", "+ ++a-b--" ], + [ "+ ++a - b++", "+ ++a-b++" ], + [ "+ ++a - + ++b", "+ ++a-+ ++b" ], + [ "+ ++a - + --b", "+ ++a-+--b" ], + [ "+ ++a - + b", "+ ++a-+b" ], + [ "+ ++a - + b--", "+ ++a-+b--" ], + [ "+ ++a - + b++", "+ ++a-+b++" ], + [ "+ ++a - - ++b", "+ ++a- -++b" ], + [ "+ ++a - - --b", "+ ++a- - --b" ], + [ "+ ++a - - b", "+ ++a- -b" ], + [ "+ ++a - - b--", "+ ++a- -b--" ], + [ "+ ++a - - b++", "+ ++a- -b++" ], + [ "+ --a + ++b", "+--a+ ++b" ], + [ "+ --a + --b", "+--a+--b" ], + [ "+ --a + b", "+--a+b" ], + [ "+ --a + b--", "+--a+b--" ], + [ "+ --a + b++", "+--a+b++" ], + [ "+ --a + + ++b", "+--a+ + ++b" ], + [ "+ --a + + --b", "+--a+ +--b" ], + [ "+ --a + + b", "+--a+ +b" ], + [ "+ --a + + b--", "+--a+ +b--" ], + [ "+ --a + + b++", "+--a+ +b++" ], + [ "+ --a + - ++b", "+--a+-++b" ], + [ "+ --a + - --b", "+--a+- --b" ], + [ "+ --a + - b", "+--a+-b" ], + [ "+ --a + - b--", "+--a+-b--" ], + [ "+ --a + - b++", "+--a+-b++" ], + [ "+ --a - ++b", "+--a-++b" ], + [ "+ --a - --b", "+--a- --b" ], + [ "+ --a - b", "+--a-b" ], + [ "+ --a - b--", "+--a-b--" ], + [ "+ --a - b++", "+--a-b++" ], + [ "+ --a - + ++b", "+--a-+ ++b" ], + [ "+ --a - + --b", "+--a-+--b" ], + [ "+ --a - + b", "+--a-+b" ], + [ "+ --a - + b--", "+--a-+b--" ], + [ "+ --a - + b++", "+--a-+b++" ], + [ "+ --a - - ++b", "+--a- -++b" ], + [ "+ --a - - --b", "+--a- - --b" ], + [ "+ --a - - b", "+--a- -b" ], + [ "+ --a - - b--", "+--a- -b--" ], + [ "+ --a - - b++", "+--a- -b++" ], + [ "+ a + ++b", "+a+ ++b" ], + [ "+ a + --b", "+a+--b" ], + [ "+ a + b", "+a+b" ], + [ "+ a + b--", "+a+b--" ], + [ "+ a + b++", "+a+b++" ], + [ "+ a + + ++b", "+a+ + ++b" ], + [ "+ a + + --b", "+a+ +--b" ], + [ "+ a + + b", "+a+ +b" ], + [ "+ a + + b--", "+a+ +b--" ], + [ "+ a + + b++", "+a+ +b++" ], + [ "+ a + - ++b", "+a+-++b" ], + [ "+ a + - --b", "+a+- --b" ], + [ "+ a + - b", "+a+-b" ], + [ "+ a + - b--", "+a+-b--" ], + [ "+ a + - b++", "+a+-b++" ], + [ "+ a - ++b", "+a-++b" ], + [ "+ a - --b", "+a- --b" ], + [ "+ a - b", "+a-b" ], + [ "+ a - b--", "+a-b--" ], + [ "+ a - b++", "+a-b++" ], + [ "+ a - + ++b", "+a-+ ++b" ], + [ "+ a - + --b", "+a-+--b" ], + [ "+ a - + b", "+a-+b" ], + [ "+ a - + b--", "+a-+b--" ], + [ "+ a - + b++", "+a-+b++" ], + [ "+ a - - ++b", "+a- -++b" ], + [ "+ a - - --b", "+a- - --b" ], + [ "+ a - - b", "+a- -b" ], + [ "+ a - - b--", "+a- -b--" ], + [ "+ a - - b++", "+a- -b++" ], + [ "+ a-- + ++b", "+a--+ ++b" ], + [ "+ a-- + --b", "+a--+--b" ], + [ "+ a-- + b", "+a--+b" ], + [ "+ a-- + b--", "+a--+b--" ], + [ "+ a-- + b++", "+a--+b++" ], + [ "+ a-- + + ++b", "+a--+ + ++b" ], + [ "+ a-- + + --b", "+a--+ +--b" ], + [ "+ a-- + + b", "+a--+ +b" ], + [ "+ a-- + + b--", "+a--+ +b--" ], + [ "+ a-- + + b++", "+a--+ +b++" ], + [ "+ a-- + - ++b", "+a--+-++b" ], + [ "+ a-- + - --b", "+a--+- --b" ], + [ "+ a-- + - b", "+a--+-b" ], + [ "+ a-- + - b--", "+a--+-b--" ], + [ "+ a-- + - b++", "+a--+-b++" ], + [ "+ a-- - ++b", "+a---++b" ], + [ "+ a-- - --b", "+a--- --b" ], + [ "+ a-- - b", "+a---b" ], + [ "+ a-- - b--", "+a---b--" ], + [ "+ a-- - b++", "+a---b++" ], + [ "+ a-- - + ++b", "+a---+ ++b" ], + [ "+ a-- - + --b", "+a---+--b" ], + [ "+ a-- - + b", "+a---+b" ], + [ "+ a-- - + b--", "+a---+b--" ], + [ "+ a-- - + b++", "+a---+b++" ], + [ "+ a-- - - ++b", "+a--- -++b" ], + [ "+ a-- - - --b", "+a--- - --b" ], + [ "+ a-- - - b", "+a--- -b" ], + [ "+ a-- - - b--", "+a--- -b--" ], + [ "+ a-- - - b++", "+a--- -b++" ], + [ "+ a++ + ++b", "+a+++ ++b" ], + [ "+ a++ + --b", "+a+++--b" ], + [ "+ a++ + b", "+a+++b" ], + [ "+ a++ + b--", "+a+++b--" ], + [ "+ a++ + b++", "+a+++b++" ], + [ "+ a++ + + ++b", "+a+++ + ++b" ], + [ "+ a++ + + --b", "+a+++ +--b" ], + [ "+ a++ + + b", "+a+++ +b" ], + [ "+ a++ + + b--", "+a+++ +b--" ], + [ "+ a++ + + b++", "+a+++ +b++" ], + [ "+ a++ + - ++b", "+a+++-++b" ], + [ "+ a++ + - --b", "+a+++- --b" ], + [ "+ a++ + - b", "+a+++-b" ], + [ "+ a++ + - b--", "+a+++-b--" ], + [ "+ a++ + - b++", "+a+++-b++" ], + [ "+ a++ - ++b", "+a++-++b" ], + [ "+ a++ - --b", "+a++- --b" ], + [ "+ a++ - b", "+a++-b" ], + [ "+ a++ - b--", "+a++-b--" ], + [ "+ a++ - b++", "+a++-b++" ], + [ "+ a++ - + ++b", "+a++-+ ++b" ], + [ "+ a++ - + --b", "+a++-+--b" ], + [ "+ a++ - + b", "+a++-+b" ], + [ "+ a++ - + b--", "+a++-+b--" ], + [ "+ a++ - + b++", "+a++-+b++" ], + [ "+ a++ - - ++b", "+a++- -++b" ], + [ "+ a++ - - --b", "+a++- - --b" ], + [ "+ a++ - - b", "+a++- -b" ], + [ "+ a++ - - b--", "+a++- -b--" ], + [ "+ a++ - - b++", "+a++- -b++" ], + [ "- ++a + ++b", "-++a+ ++b" ], + [ "- ++a + --b", "-++a+--b" ], + [ "- ++a + b", "-++a+b" ], + [ "- ++a + b--", "-++a+b--" ], + [ "- ++a + b++", "-++a+b++" ], + [ "- ++a + + ++b", "-++a+ + ++b" ], + [ "- ++a + + --b", "-++a+ +--b" ], + [ "- ++a + + b", "-++a+ +b" ], + [ "- ++a + + b--", "-++a+ +b--" ], + [ "- ++a + + b++", "-++a+ +b++" ], + [ "- ++a + - ++b", "-++a+-++b" ], + [ "- ++a + - --b", "-++a+- --b" ], + [ "- ++a + - b", "-++a+-b" ], + [ "- ++a + - b--", "-++a+-b--" ], + [ "- ++a + - b++", "-++a+-b++" ], + [ "- ++a - ++b", "-++a-++b" ], + [ "- ++a - --b", "-++a- --b" ], + [ "- ++a - b", "-++a-b" ], + [ "- ++a - b--", "-++a-b--" ], + [ "- ++a - b++", "-++a-b++" ], + [ "- ++a - + ++b", "-++a-+ ++b" ], + [ "- ++a - + --b", "-++a-+--b" ], + [ "- ++a - + b", "-++a-+b" ], + [ "- ++a - + b--", "-++a-+b--" ], + [ "- ++a - + b++", "-++a-+b++" ], + [ "- ++a - - ++b", "-++a- -++b" ], + [ "- ++a - - --b", "-++a- - --b" ], + [ "- ++a - - b", "-++a- -b" ], + [ "- ++a - - b--", "-++a- -b--" ], + [ "- ++a - - b++", "-++a- -b++" ], + [ "- --a + ++b", "- --a+ ++b" ], + [ "- --a + --b", "- --a+--b" ], + [ "- --a + b", "- --a+b" ], + [ "- --a + b--", "- --a+b--" ], + [ "- --a + b++", "- --a+b++" ], + [ "- --a + + ++b", "- --a+ + ++b" ], + [ "- --a + + --b", "- --a+ +--b" ], + [ "- --a + + b", "- --a+ +b" ], + [ "- --a + + b--", "- --a+ +b--" ], + [ "- --a + + b++", "- --a+ +b++" ], + [ "- --a + - ++b", "- --a+-++b" ], + [ "- --a + - --b", "- --a+- --b" ], + [ "- --a + - b", "- --a+-b" ], + [ "- --a + - b--", "- --a+-b--" ], + [ "- --a + - b++", "- --a+-b++" ], + [ "- --a - ++b", "- --a-++b" ], + [ "- --a - --b", "- --a- --b" ], + [ "- --a - b", "- --a-b" ], + [ "- --a - b--", "- --a-b--" ], + [ "- --a - b++", "- --a-b++" ], + [ "- --a - + ++b", "- --a-+ ++b" ], + [ "- --a - + --b", "- --a-+--b" ], + [ "- --a - + b", "- --a-+b" ], + [ "- --a - + b--", "- --a-+b--" ], + [ "- --a - + b++", "- --a-+b++" ], + [ "- --a - - ++b", "- --a- -++b" ], + [ "- --a - - --b", "- --a- - --b" ], + [ "- --a - - b", "- --a- -b" ], + [ "- --a - - b--", "- --a- -b--" ], + [ "- --a - - b++", "- --a- -b++" ], + [ "- a + ++b", "-a+ ++b" ], + [ "- a + --b", "-a+--b" ], + [ "- a + b", "-a+b" ], + [ "- a + b--", "-a+b--" ], + [ "- a + b++", "-a+b++" ], + [ "- a + + ++b", "-a+ + ++b" ], + [ "- a + + --b", "-a+ +--b" ], + [ "- a + + b", "-a+ +b" ], + [ "- a + + b--", "-a+ +b--" ], + [ "- a + + b++", "-a+ +b++" ], + [ "- a + - ++b", "-a+-++b" ], + [ "- a + - --b", "-a+- --b" ], + [ "- a + - b", "-a+-b" ], + [ "- a + - b--", "-a+-b--" ], + [ "- a + - b++", "-a+-b++" ], + [ "- a - ++b", "-a-++b" ], + [ "- a - --b", "-a- --b" ], + [ "- a - b", "-a-b" ], + [ "- a - b--", "-a-b--" ], + [ "- a - b++", "-a-b++" ], + [ "- a - + ++b", "-a-+ ++b" ], + [ "- a - + --b", "-a-+--b" ], + [ "- a - + b", "-a-+b" ], + [ "- a - + b--", "-a-+b--" ], + [ "- a - + b++", "-a-+b++" ], + [ "- a - - ++b", "-a- -++b" ], + [ "- a - - --b", "-a- - --b" ], + [ "- a - - b", "-a- -b" ], + [ "- a - - b--", "-a- -b--" ], + [ "- a - - b++", "-a- -b++" ], + [ "- a-- + ++b", "-a--+ ++b" ], + [ "- a-- + --b", "-a--+--b" ], + [ "- a-- + b", "-a--+b" ], + [ "- a-- + b--", "-a--+b--" ], + [ "- a-- + b++", "-a--+b++" ], + [ "- a-- + + ++b", "-a--+ + ++b" ], + [ "- a-- + + --b", "-a--+ +--b" ], + [ "- a-- + + b", "-a--+ +b" ], + [ "- a-- + + b--", "-a--+ +b--" ], + [ "- a-- + + b++", "-a--+ +b++" ], + [ "- a-- + - ++b", "-a--+-++b" ], + [ "- a-- + - --b", "-a--+- --b" ], + [ "- a-- + - b", "-a--+-b" ], + [ "- a-- + - b--", "-a--+-b--" ], + [ "- a-- + - b++", "-a--+-b++" ], + [ "- a-- - ++b", "-a---++b" ], + [ "- a-- - --b", "-a--- --b" ], + [ "- a-- - b", "-a---b" ], + [ "- a-- - b--", "-a---b--" ], + [ "- a-- - b++", "-a---b++" ], + [ "- a-- - + ++b", "-a---+ ++b" ], + [ "- a-- - + --b", "-a---+--b" ], + [ "- a-- - + b", "-a---+b" ], + [ "- a-- - + b--", "-a---+b--" ], + [ "- a-- - + b++", "-a---+b++" ], + [ "- a-- - - ++b", "-a--- -++b" ], + [ "- a-- - - --b", "-a--- - --b" ], + [ "- a-- - - b", "-a--- -b" ], + [ "- a-- - - b--", "-a--- -b--" ], + [ "- a-- - - b++", "-a--- -b++" ], + [ "- a++ + ++b", "-a+++ ++b" ], + [ "- a++ + --b", "-a+++--b" ], + [ "- a++ + b", "-a+++b" ], + [ "- a++ + b--", "-a+++b--" ], + [ "- a++ + b++", "-a+++b++" ], + [ "- a++ + + ++b", "-a+++ + ++b" ], + [ "- a++ + + --b", "-a+++ +--b" ], + [ "- a++ + + b", "-a+++ +b" ], + [ "- a++ + + b--", "-a+++ +b--" ], + [ "- a++ + + b++", "-a+++ +b++" ], + [ "- a++ + - ++b", "-a+++-++b" ], + [ "- a++ + - --b", "-a+++- --b" ], + [ "- a++ + - b", "-a+++-b" ], + [ "- a++ + - b--", "-a+++-b--" ], + [ "- a++ + - b++", "-a+++-b++" ], + [ "- a++ - ++b", "-a++-++b" ], + [ "- a++ - --b", "-a++- --b" ], + [ "- a++ - b", "-a++-b" ], + [ "- a++ - b--", "-a++-b--" ], + [ "- a++ - b++", "-a++-b++" ], + [ "- a++ - + ++b", "-a++-+ ++b" ], + [ "- a++ - + --b", "-a++-+--b" ], + [ "- a++ - + b", "-a++-+b" ], + [ "- a++ - + b--", "-a++-+b--" ], + [ "- a++ - + b++", "-a++-+b++" ], + [ "- a++ - - ++b", "-a++- -++b" ], + [ "- a++ - - --b", "-a++- - --b" ], + [ "- a++ - - b", "-a++- -b" ], + [ "- a++ - - b--", "-a++- -b--" ], + [ "- a++ - - b++", "-a++- -b++" ], + ].forEach(function(exp) { + assert.strictEqual(UglifyJS.parse(exp[0]).print_to_string(), exp[1] + ";"); + }); + }); + it("Should preserve space between /regex/ and `in`", function() { + [ + "/regex/ in {}", + "/regex/g in {}", + "0 + /regex/ in {}", + "0 + /regex/g in {}", + ].forEach(function(exp) { + var code = UglifyJS.parse(exp).print_to_string(); + try { + assert.strictEqual(UglifyJS.parse(code).print_to_string(), code); + } catch (ex) { + assert.fail("Failed to reparse: " + exp + "\n" + ex); + } + }); + }); +}); diff --git a/test/mocha/parentheses.js b/test/mocha/parentheses.js new file mode 100644 index 00000000000..0fbb4c95970 --- /dev/null +++ b/test/mocha/parentheses.js @@ -0,0 +1,105 @@ +var assert = require("assert"); +var UglifyJS = require("../.."); + +describe("parentheses", function() { + it("Should add trailing parentheses for new expressions with zero arguments in beautify mode", function() { + var tests = [ + "new x(1);", + "new x;", + "new new x;", + "new (function(foo){this.foo=foo;})(1);", + "new (function(foo){this.foo=foo;})();", + "new (function test(foo){this.foo=foo;})(1);", + "new (function test(foo){this.foo=foo;})();", + "new true;", + "new (0);", + "new (!0);", + "new (bar = function(foo) {this.foo=foo;})(123);", + "new (bar = function(foo) {this.foo=foo;})();" + ]; + var expected = [ + "new x(1);", + "new x();", + "new new x()();", + "new function(foo) {\n this.foo = foo;\n}(1);", + "new function(foo) {\n this.foo = foo;\n}();", + "new function test(foo) {\n this.foo = foo;\n}(1);", + "new function test(foo) {\n this.foo = foo;\n}();", + "new true();", + "new 0();", + "new (!0)();", + "new (bar = function(foo) {\n this.foo = foo;\n})(123);", + "new (bar = function(foo) {\n this.foo = foo;\n})();" + ]; + for (var i = 0; i < tests.length; i++) { + assert.strictEqual( + UglifyJS.minify(tests[i], { + output: {beautify: true}, + compress: false, + mangle: false + }).code, + expected[i] + ); + } + }); + + it("Should not add trailing parentheses for new expressions with zero arguments in non-beautify mode", function() { + var tests = [ + "new x(1);", + "new x;", + "new new x;", + "new (function(foo){this.foo=foo;})(1);", + "new (function(foo){this.foo=foo;})();", + "new (function test(foo){this.foo=foo;})(1);", + "new (function test(foo){this.foo=foo;})();", + "new true;", + "new (0);", + "new (!0);", + "new (bar = function(foo) {this.foo=foo;})(123);", + "new (bar = function(foo) {this.foo=foo;})();" + ]; + var expected = [ + "new x(1);", + "new x;", + "new(new x);", + "new function(foo){this.foo=foo}(1);", + "new function(foo){this.foo=foo};", + "new function test(foo){this.foo=foo}(1);", + "new function test(foo){this.foo=foo};", + "new true;", + "new 0;", + "new(!0);", + "new(bar=function(foo){this.foo=foo})(123);", + "new(bar=function(foo){this.foo=foo});" + ]; + for (var i = 0; i < tests.length; i++) { + assert.strictEqual( + UglifyJS.minify(tests[i], { + output: {beautify: false}, + compress: false, + mangle: false + }).code, + expected[i] + ); + } + }); + + it("Should compress leading parentheses with reasonable performance", function() { + this.timeout(30000); + var code = [ + "({}?0:1)&&x();", + "(function(){}).name;", + ]; + for (var i = 16; --i >= 0;) { + code = code.concat(code); + } + code = code.join(""); + var result = UglifyJS.minify(code, { + compress: false, + mangle: false, + }); + if (result.error) throw result.error; + // Dismal performance for `assert.strictEqual()` in Node.js 6 + assert.ok(result.code === code); + }); +}); diff --git a/test/mocha/reduce.js b/test/mocha/reduce.js new file mode 100644 index 00000000000..528d365e77a --- /dev/null +++ b/test/mocha/reduce.js @@ -0,0 +1,511 @@ +var assert = require("assert"); +var fs = require("fs"); +var reduce_test = require("../reduce"); +var semver = require("semver"); + +function read(path) { + return fs.readFileSync(path, "utf8"); +} + +describe("test/reduce.js", function() { + this.timeout(60000); + it("Should reduce test case", function() { + var result = reduce_test(read("test/input/reduce/unsafe_math.js"), { + compress: { + unsafe_math: true, + }, + mangle: false, + }, { + verbose: false, + }); + if (result.error) throw result.error; + assert.strictEqual(result.code, read("test/input/reduce/unsafe_math.reduced.js")); + }); + it("Should eliminate unreferenced labels", function() { + var result = reduce_test(read("test/input/reduce/label.js"), { + compress: { + unsafe_math: true, + }, + mangle: false, + }, { + verbose: false, + }); + if (result.error) throw result.error; + assert.strictEqual(result.code, read("test/input/reduce/label.reduced.js")); + }); + it("Should retain setter arguments", function() { + var result = reduce_test(read("test/input/reduce/setter.js"), { + compress: { + unsafe_math: true, + }, + mangle: false, + }, { + verbose: false, + }); + if (result.error) throw result.error; + assert.strictEqual(result.code, read("test/input/reduce/setter.reduced.js")); + }); + it("Should handle test cases with --toplevel", function() { + var result = reduce_test([ + "var Infinity = 42;", + "console.log(Infinity);", + ].join("\n"), { + toplevel: true, + }); + if (result.error) throw result.error; + assert.strictEqual(result.code, [ + "// Can't reproduce test failure", + "// minify options: {", + '// "toplevel": true', + "// }", + ].join("\n")); + }); + it("Should handle test cases with --compress toplevel", function() { + var result = reduce_test([ + "var NaN = 42;", + "console.log(NaN);", + ].join("\n"), { + compress: { + toplevel: true, + }, + }); + if (result.error) throw result.error; + assert.strictEqual(result.code, [ + "// Can't reproduce test failure", + "// minify options: {", + '// "compress": {', + '// "toplevel": true', + "// }", + "// }", + ].join("\n")); + }); + it("Should handle test cases with --mangle toplevel", function() { + var result = reduce_test([ + "var undefined = 42;", + "console.log(undefined);", + ].join("\n"), { + mangle: { + toplevel: true, + }, + }); + if (result.error) throw result.error; + assert.strictEqual(result.code, [ + "// Can't reproduce test failure", + "// minify options: {", + '// "mangle": {', + '// "toplevel": true', + "// }", + "// }", + ].join("\n")); + }); + it("Should handle test result of NaN", function() { + var result = reduce_test("throw 0 / 0;"); + if (result.error) throw result.error; + assert.strictEqual(result.code, [ + "// Can't reproduce test failure", + "// minify options: {}", + ].join("\n")); + }); + it("Should print correct output for irreducible test case", function() { + var result = reduce_test([ + "console.log(1 + .1 + .1);", + ].join("\n"), { + compress: { + unsafe_math: true, + }, + mangle: false, + }); + if (result.error) throw result.error; + assert.strictEqual(result.code, [ + "// (beautified)", + "console.log(1 + .1 + .1);", + "// output: 1.2000000000000002", + "// ", + "// minify: 1.2", + "// ", + "// options: {", + '// "compress": {', + '// "unsafe_math": true', + "// },", + '// "mangle": false', + "// }", + ].join("\n")); + }); + it("Should fail when invalid option is supplied", function() { + var result = reduce_test("", { + compress: { + unsafe_regex: true, + }, + }); + var err = result.error; + assert.ok(err instanceof Error); + assert.strictEqual(err.stack.split(/\n/)[0], "DefaultsError: `unsafe_regex` is not a supported option"); + }); + it("Should report on test case with invalid syntax", function() { + var result = reduce_test("var 0 = 1;"); + var err = result.error; + assert.ok(err instanceof Error); + assert.strictEqual(err.stack.split(/\n/)[0], "SyntaxError: Name expected"); + }); + it("Should format multi-line output correctly", function() { + var code = [ + "var a = 0;", + "", + "for (var b in [ 1, 2, 3 ]) {", + " a = +a + 1 - .2;", + " console.log(a);", + "}", + ].join("\n"); + var result = reduce_test(code, { + compress: { + unsafe_math: true, + }, + mangle: false, + }); + if (result.error) throw result.error; + assert.strictEqual(result.code, [ + "// (beautified)", + code, + "// output: 0.8", + "// 1.6", + "// 2.4", + "// ", + "// minify: 0.8", + "// 1.6", + "// 2.4000000000000004", + "// ", + "// options: {", + '// "compress": {', + '// "unsafe_math": true', + "// },", + '// "mangle": false', + "// }", + ].join("\n")); + }); + it("Should reduce `for (const ... in ...)` without invalid intermediate AST", function() { + if (semver.satisfies(process.version, "<4")) return; + var code = [ + "var a = 0;", + "", + "for (const b in [ 1, 2, 3 ]) {", + " a = +a + 1 - .2;", + " console.log(a);", + "}", + ].join("\n"); + var result = reduce_test(code, { + compress: { + unsafe_math: true, + }, + }); + if (result.error) throw result.error; + assert.deepEqual(result.warnings, []); + }); + it("Should reduce infinite loops with reasonable performance", function() { + if (semver.satisfies(process.version, "<=0.10")) return; + this.timeout(120000); + var result = reduce_test("while (/9/.test(1 - .8));", { + compress: { + unsafe_math: true, + }, + mangle: false, + }); + if (result.error) throw result.error; + assert.strictEqual(result.code.replace(/ timed out after [0-9]+ms/, " timed out."), [ + "// (beautified)", + "while (/9/.test(1 - .8)) {}", + "// output: Error: Script execution timed out.", + "// minify: ", + "// options: {", + '// "compress": {', + '// "unsafe_math": true', + "// },", + '// "mangle": false', + "// }", + ].join("\n")); + }); + it("Should ignore difference in Error.message", function() { + var result = reduce_test("null[function() {\n}];"); + if (result.error) throw result.error; + assert.strictEqual(result.code, (semver.satisfies(process.version, "<=0.10") ? [ + "// Can't reproduce test failure", + "// minify options: {}", + ] : [ + "// No differences except in error message", + "// minify options: {}", + ]).join("\n")); + }); + it("Should report trailing whitespace difference in stringified format", function() { + var code = [ + "[].forEach.call((1 - .8).toString(), function() {", + " console.log();", + "});", + ].join("\n"); + var result = reduce_test(code, { + compress: { + unsafe_math: true, + }, + mangle: false, + }); + if (result.error) throw result.error; + assert.strictEqual(result.code, [ + "// (beautified)", + code, + "// (stringified)", + '// output: "\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n"', + '// minify: "\\n\\n\\n"', + "// options: {", + '// "compress": {', + '// "unsafe_math": true', + '// },', + '// "mangle": false', + "// }", + ].join("\n")); + }); + it("Should reduce test case which differs only in Error.message", function() { + var code = [ + "var a=0;", + "try{", + "null[function(){}]", + "}catch(e){", + "for(var i in e.toString())", + "a++,console.log()", + "}", + "console.log(a);", + ].join(""); + var result = reduce_test(code, { + compress: false, + mangle: false, + output: { + beautify: true, + }, + }); + if (result.error) throw result.error; + assert.deepEqual(result.warnings, []); + if (semver.satisfies(process.version, "<=0.10")) { + assert.strictEqual(result.code, [ + "// Can't reproduce test failure", + "// minify options: {", + '// "compress": false,', + '// "mangle": false,', + '// "output": {', + '// "beautify": true', + "// }", + "// }", + ].join("\n")); + } else { + var message = result.code.split(/\n/, 3)[1].slice("// output: ".length); + assert.strictEqual(result.code, [ + [ + "try{", + "null[function(){}]", + "}catch(e){", + "console.log(e)", + "}", + ].join(""), + "// output: " + message, + "// ", + "// minify: " + message.replace("(){}", "() {}"), + "// ", + "// options: {", + '// "compress": false,', + '// "mangle": false,', + '// "output": {', + '// "beautify": true', + "// }", + "// }", + ].join("\n")); + } + }); + it("Should maintain block-scope for const/let", function() { + if (semver.satisfies(process.version, "<4")) return; + this.timeout(120000); + var code = [ + '"use strict";', + "", + "L: for (let a = (1 - .8).toString(); ;) {", + " if (!console.log(a)) {", + " break L;", + " }", + "}", + ].join("\n"); + var result = reduce_test(code, { + compress: { + unsafe_math: true, + }, + mangle: false, + module: false, + }); + if (result.error) throw result.error; + assert.strictEqual(result.code, [ + "// (beautified)", + code, + "// output: 0.19999999999999996", + "// ", + "// minify: 0.2", + "// ", + "// options: {", + '// "compress": {', + '// "unsafe_math": true', + '// },', + '// "mangle": false,', + '// "module": false', + "// }", + ].join("\n")); + }); + it("Should handle corner cases when intermediate case differs only in Error.message", function() { + if (semver.satisfies(process.version, "<=0.10")) return; + var result = reduce_test(read("test/input/reduce/diff_error.js"), { + compress: { + unsafe_math: true, + }, + mangle: false, + }, { + verbose: false, + }); + if (result.error) throw result.error; + assert.strictEqual(result.code, read("test/input/reduce/diff_error.reduced.js")); + }); + it("Should maintain valid LHS in destructuring assignments", function() { + if (semver.satisfies(process.version, "<6")) return; + var result = reduce_test(read("test/input/reduce/destructured_assign.js"), { + compress: { + unsafe_math: true, + }, + mangle: false, + validate: true, + }); + if (result.error) throw result.error; + assert.strictEqual(result.code, read("test/input/reduce/destructured_assign.reduced.js")); + }); + it("Should handle destructured catch expressions", function() { + if (semver.satisfies(process.version, "<6")) return; + var result = reduce_test(read("test/input/reduce/destructured_catch.js"), { + mangle: false, + }); + if (result.error) throw result.error; + assert.strictEqual(result.code, read("test/input/reduce/destructured_catch.reduced.js")); + }); + it("Should not enumerate `toString` over global context", function() { + if (semver.satisfies(process.version, "<8")) return; + var code = [ + "(async function() {});", + "for (var k in this);", + "console.log(k, 42 + this);", + ].join("\n"); + var result = reduce_test(code, { + mangle: false, + }); + if (result.error) throw result.error; + assert.strictEqual(result.code, [ + "// Can't reproduce test failure", + "// minify options: {", + '// "mangle": false', + "// }", + ].join("\n")); + }); + it("Should reduce object with method syntax without invalid intermediate AST", function() { + if (semver.satisfies(process.version, "<4")) return; + var code = [ + "console.log({", + " f() {", + " return 1 - .8;", + " },", + "}.f());", + ].join("\n"); + var result = reduce_test(code, { + compress: { + unsafe_math: true, + }, + }); + if (result.error) throw result.error; + assert.deepEqual(result.warnings, []); + }); + it("Should handle thrown falsy values gracefully", function() { + var code = [ + "throw 0;", + "setTimeout(null, 42);", + ].join("\n"); + var result = reduce_test(code, { + mangle: false, + }); + if (result.error) throw result.error; + assert.strictEqual(result.code, [ + "// Can't reproduce test failure", + "// minify options: {", + '// "mangle": false', + "// }", + ].join("\n")); + }); + it("Should transform `export default class` correctly", function() { + var result = reduce_test(read("test/input/reduce/export_default.js"), { + compress: false, + toplevel: true, + }); + if (result.error) throw result.error; + assert.strictEqual(result.code, [ + "// Can't reproduce test failure", + "// minify options: {", + '// "compress": false,', + '// "toplevel": true', + "// }", + ].join("\n")); + }); + it("Should transform `export default function` correctly", function() { + if (semver.satisfies(process.version, "<8")) return; + var code = [ + "export default function f(a) {", + " for (var k in a)", + " console.log(k);", + " (async function() {})();", + "}", + "f(this);", + ].join("\n"); + var result = reduce_test(code, { + mangle: false, + }); + if (result.error) throw result.error; + assert.strictEqual(result.code, [ + "// Can't reproduce test failure", + "// minify options: {", + '// "mangle": false', + "// }", + ].join("\n")); + }); + it("Should transform `export default (function)` correctly", function() { + var code = [ + "for (var k in this)", + " console.log(k);", + "export default (function f() {});", + "console.log(k);", + ].join("\n"); + var result = reduce_test(code, { + mangle: false, + }); + if (result.error) throw result.error; + assert.strictEqual(result.code, [ + "// Can't reproduce test failure", + "// minify options: {", + '// "mangle": false', + "// }", + ].join("\n")); + }); + it("Should transform `export default (42)` correctly", function() { + var code = [ + "export default (42);", + "for (var k in this)", + " console.log(k);", + ].join("\n"); + var result = reduce_test(code, { + compress: false, + mangle: false, + }); + if (result.error) throw result.error; + assert.strictEqual(result.code, [ + "// Can't reproduce test failure", + "// minify options: {", + '// "compress": false,', + '// "mangle": false', + "// }", + ].join("\n")); + }); +}); diff --git a/test/mocha/sourcemaps.js b/test/mocha/sourcemaps.js new file mode 100644 index 00000000000..4efd42d13ce --- /dev/null +++ b/test/mocha/sourcemaps.js @@ -0,0 +1,401 @@ +var assert = require("assert"); +var fs = require("fs"); +var UglifyJS = require("../node"); + +function read(path) { + return fs.readFileSync(path, "utf8"); +} + +function source_map(code) { + var result = UglifyJS.minify(code, { + compress: false, + mangle: false, + sourceMap: true, + }); + if (result.error) throw result.error; + return JSON.parse(result.map); +} + +function get_map() { + return { + "version": 3, + "sources": [ "index.js" ], + "names": [], + "mappings": ";;AAAA,IAAI,MAAM,SAAN,GAAM;AAAA,SAAK,SAAS,CAAd;AAAA,CAAV;AACA,QAAQ,GAAR,CAAY,IAAI,KAAJ,CAAZ", + "file": "bundle.js", + "sourcesContent": [ "let foo = x => \"foo \" + x;\nconsole.log(foo(\"bar\"));" ], + }; +} + +function prepare_map(sourceMap) { + var code = [ + '"use strict";', + "", + "var foo = function foo(x) {", + ' return "foo " + x;', + "};", + 'console.log(foo("bar"));', + "", + "//# sourceMappingURL=bundle.js.map", + ].join("\n"); + var result = UglifyJS.minify(code, { + sourceMap: { + content: sourceMap, + includeSources: true, + }, + }); + if (result.error) throw result.error; + return JSON.parse(result.map); +} + +describe("sourcemaps", function() { + it("Should give correct version", function() { + var map = source_map("var x = 1 + 1;"); + assert.strictEqual(map.version, 3); + assert.deepEqual(map.names, [ "x" ]); + }); + it("Should give correct names", function() { + var map = source_map([ + "({", + " get enabled() {", + " return 3;", + " },", + " set enabled(x) {", + " ;", + " }", + "});", + ].join("\n")); + assert.deepEqual(map.names, [ "enabled", "x" ]); + }); + it("Should work with sourceMap.names=true", function() { + var result = UglifyJS.minify([ + "var obj = {", + " p: a,", + " q: b", + "};", + ].join("\n"), { + compress: false, + mangle: false, + sourceMap: { + names: true, + }, + }); + if (result.error) throw result.error; + var map = JSON.parse(result.map); + assert.deepEqual(map.names, [ "obj", "p", "a", "q", "b" ]); + }); + it("Should work with sourceMap.names=false", function() { + var result = UglifyJS.minify([ + "var obj = {", + " p: a,", + " q: b", + "};", + ].join("\n"), { + compress: false, + mangle: false, + sourceMap: { + names: false, + }, + }); + if (result.error) throw result.error; + var map = JSON.parse(result.map); + assert.deepEqual(map.names, []); + }); + it("Should mark class properties", function() { + var result = UglifyJS.minify([ + "class A {", + " static P = 42", + " set #q(v) {}", + "}", + ].join("\n"), { + sourceMap: true, + }); + if (result.error) throw result.error; + assert.strictEqual(result.code, "class A{static P=42;set#q(s){}}"); + assert.strictEqual(result.map, '{"version":3,"sources":["0"],"names":["A","P","#q","v"],"mappings":"MAAMA,EACFC,SAAW,GACXC,MAAOC,IACX"}'); + }); + it("Should mark array/object literals", function() { + var result = UglifyJS.minify([ + "var obj = {};", + "obj.wat([]);", + ].join("\n"), { + sourceMap: true, + toplevel: true, + }); + if (result.error) throw result.error; + assert.strictEqual(result.code, "({}).wat([]);"); + assert.strictEqual(result.map, '{"version":3,"sources":["0"],"names":["wat"],"mappings":"CAAU,IACNA,IAAI,EAAE"}'); + }); + it("Should give correct sourceRoot", function() { + var code = "console.log(42);"; + var result = UglifyJS.minify(code, { + sourceMap: { + root: "//foo.bar/", + }, + }); + if (result.error) throw result.error; + assert.strictEqual(result.code, code); + assert.strictEqual(result.map, '{"version":3,"sourceRoot":"//foo.bar/","sources":["0"],"names":["console","log"],"mappings":"AAAAA,QAAQC,IAAI,EAAE"}'); + }); + it("Should produce same source map with DOS or UNIX line endings", function() { + var code = [ + 'console.log("\\', + 'hello",', + '"world");', + ]; + var dos = UglifyJS.minify(code.join("\r\n"), { + sourceMap: true, + }); + if (dos.error) throw dos.error; + var unix = UglifyJS.minify(code.join("\n"), { + sourceMap: true, + }); + if (unix.error) throw unix.error; + assert.strictEqual(dos.map, unix.map); + }); + + describe("inSourceMap", function() { + it("Should read the given string filename correctly when sourceMapIncludeSources is enabled", function() { + var result = UglifyJS.minify(read("test/input/issue-1236/simple.js"), { + sourceMap: { + content: read("test/input/issue-1236/simple.js.map"), + filename: "simple.min.js", + includeSources: true, + }, + }); + if (result.error) throw result.error; + var map = JSON.parse(result.map); + assert.equal(map.file, "simple.min.js"); + assert.equal(map.sourcesContent.length, 1); + assert.equal(map.sourcesContent[0], 'let foo = x => "foo " + x;\nconsole.log(foo("bar"));'); + }); + it("Should process inline source map", function() { + var result = UglifyJS.minify(read("test/input/issue-520/input.js"), { + compress: { toplevel: true }, + sourceMap: { + content: "inline", + includeSources: true, + url: "inline", + }, + }); + if (result.error) throw result.error; + assert.strictEqual(result.code + "\n", read("test/input/issue-520/output.js")); + }); + it("Should warn for missing inline source map", function() { + var result = UglifyJS.minify(read("test/input/issue-1323/sample.js"), { + mangle: false, + sourceMap: { + content: "inline", + }, + warnings: true, + }); + assert.strictEqual(result.code, "var bar=function(bar){return bar};"); + assert.deepEqual(result.warnings, [ "WARN: inline source map not found: 0" ]); + }); + it("Should handle multiple input and inline source map", function() { + var result = UglifyJS.minify([ + read("test/input/issue-520/input.js"), + read("test/input/issue-1323/sample.js"), + ], { + sourceMap: { + content: "inline", + url: "inline", + }, + warnings: true, + }); + if (result.error) throw result.error; + assert.strictEqual(result.code, [ + "var Foo=function(){console.log(3)},bar=(new Foo,function(o){return o});", + "//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbInN0ZGluIiwiMSJdLCJuYW1lcyI6WyJGb28iLCJjb25zb2xlIiwibG9nIiwiYmFyIl0sIm1hcHBpbmdzIjoiQUFBQSxJQUFNQSxJQUFJLFdBQWdCQyxRQUFRQyxJQUFJLENBQUcsQ0FBRSxFQ0F2Q0MsS0RBMkMsSUFBSUgsSUNDL0MsU0FBY0csR0FDVixPQUFPQSxDQUNYIn0=", + ].join("\n")); + assert.deepEqual(result.warnings, [ "WARN: inline source map not found: 1" ]); + }); + it("Should drop source contents for includeSources=false", function() { + var result = UglifyJS.minify(read("test/input/issue-520/input.js"), { + compress: false, + mangle: false, + sourceMap: { + content: "inline", + includeSources: true, + }, + }); + if (result.error) throw result.error; + var map = JSON.parse(result.map); + assert.strictEqual(map.sourcesContent.length, 1); + result = UglifyJS.minify(result.code, { + compress: false, + mangle: false, + sourceMap: { + content: result.map, + }, + }); + if (result.error) throw result.error; + map = JSON.parse(result.map); + assert.ok(!("sourcesContent" in map)); + }); + it("Should parse the correct sourceMappingURL", function() { + var result = UglifyJS.minify(read("test/input/issue-3294/input.js"), { + compress: { toplevel: true }, + sourceMap: { + content: "inline", + includeSources: true, + url: "inline", + }, + }); + if (result.error) throw result.error; + assert.strictEqual(result.code + "\n", read("test/input/issue-3294/output.js")); + }); + it("Should work in presence of unrecognized annotations", function() { + var result = UglifyJS.minify(read("test/input/issue-3441/input.js"), { + compress: false, + mangle: false, + sourceMap: { + content: "inline", + }, + }); + if (result.error) throw result.error; + assert.strictEqual(result.code, '(function(){console.log("hello")}).call(this);'); + assert.strictEqual(result.map, '{"version":3,"sources":["main.coffee"],"names":["console","log"],"mappings":"CAAA,WAAAA,QAAQC,IAAI,OAAZ"}'); + }); + it("Should not overwrite existing sourcesContent", function() { + var result = UglifyJS.minify({ + "in.js": [ + '"use strict";', + "", + "var _window$foo = window.foo,", + " a = _window$foo[0],", + " b = _window$foo[1];", + ].join("\n"), + }, { + compress: false, + mangle: false, + sourceMap: { + content: { + version: 3, + sources: [ "in.js" ], + names: [ + "window", + "foo", + "a", + "b", + ], + mappings: ";;kBAAaA,MAAM,CAACC,G;IAAfC,C;IAAGC,C", + file: "in.js", + sourcesContent: [ "let [a, b] = window.foo;\n" ], + }, + includeSources: true, + }, + }); + if (result.error) throw result.error; + assert.strictEqual(result.code, '"use strict";var _window$foo=window.foo,a=_window$foo[0],b=_window$foo[1];'); + assert.strictEqual(result.map, '{"version":3,"sources":["in.js"],"sourcesContent":["let [a, b] = window.foo;\\n"],"names":["window","foo","a","b"],"mappings":"6BAAaA,OAAOC,IAAfC,E,eAAGC,E"}'); + }); + }); + + describe("sourceMapInline", function() { + it("Should append source map to output js when sourceMapInline is enabled", function() { + var result = UglifyJS.minify('var a = function(foo) { return foo; };', { + sourceMap: { + url: "inline", + }, + }); + if (result.error) throw result.error; + var code = result.code; + assert.strictEqual(code, "var a=function(n){return n};\n" + + "//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIjAiXSwibmFtZXMiOlsiYSIsImZvbyJdLCJtYXBwaW5ncyI6IkFBQUEsSUFBSUEsRUFBSSxTQUFTQyxHQUFPLE9BQU9BLENBQUsifQ=="); + }); + it("Should not append source map to output js when sourceMapInline is not enabled", function() { + var result = UglifyJS.minify("var a = function(foo) { return foo; };"); + if (result.error) throw result.error; + var code = result.code; + assert.strictEqual(code, "var a=function(n){return n};"); + }); + it("Should work with max_line_len", function() { + var result = UglifyJS.minify(read("test/input/issue-505/input.js"), { + compress: { + directives: false, + }, + output: { + max_line_len: 20, + }, + sourceMap: { + url: "inline", + }, + }); + if (result.error) throw result.error; + assert.strictEqual(result.code, read("test/input/issue-505/output.js")); + }); + it("Should work with unicode characters", function() { + var code = [ + "var tëst = '→unicøde←';", + "alert(tëst);", + ].join("\n"); + var result = UglifyJS.minify(code, { + sourceMap: { + includeSources: true, + url: "inline", + }, + }); + if (result.error) throw result.error; + var map = JSON.parse(result.map); + assert.strictEqual(map.sourcesContent.length, 1); + assert.strictEqual(map.sourcesContent[0], code); + var encoded = result.code.slice(result.code.lastIndexOf(",") + 1); + map = JSON.parse(UglifyJS.to_ascii(encoded)); + assert.strictEqual(map.sourcesContent.length, 1); + assert.strictEqual(map.sourcesContent[0], code); + result = UglifyJS.minify(result.code, { + sourceMap: { + content: "inline", + includeSources: true, + }, + }); + if (result.error) throw result.error; + map = JSON.parse(result.map); + assert.strictEqual(map.names.length, 2); + assert.strictEqual(map.names[0], "tëst"); + assert.strictEqual(map.names[1], "alert"); + }); + }); + + describe("input sourcemaps", function() { + it("Should not modify input source map", function() { + var orig = get_map(); + var original = JSON.stringify(orig); + prepare_map(orig); + assert.strictEqual(JSON.stringify(orig), original); + }); + it("Should copy over original sourcesContent", function() { + var orig = get_map(); + var map = prepare_map(orig); + assert.strictEqual(map.sources.length, 1); + assert.strictEqual(map.sources[0], "index.js"); + assert.strictEqual(map.sourcesContent.length, 1); + assert.equal(map.sourcesContent[0], orig.sourcesContent[0]); + }); + it("Should copy sourcesContent if sources are relative", function() { + var relativeMap = get_map(); + relativeMap.sources = ['./index.js']; + var map = prepare_map(relativeMap); + assert.strictEqual(map.sources.length, 1); + assert.strictEqual(map.sources[0], "./index.js"); + assert.strictEqual(map.sourcesContent.length, 1); + assert.equal(map.sourcesContent[0], relativeMap.sourcesContent[0]); + }); + it("Should not have invalid mappings from inputSourceMap", function() { + var map = prepare_map(get_map()); + // The original source has only 2 lines, check that mappings don't have more lines + var msg = "Mapping should not have higher line number than the original file had"; + var lines = map.mappings.split(/;/); + assert.ok(lines.length <= 2, msg); + var indices = [ 0, 0, 1, 0, 0]; + lines.forEach(function(segments) { + indices[0] = 0; + segments.split(/,/).forEach(function(segment) { + UglifyJS.vlq_decode(indices, segment); + assert.ok(indices[2] <= 2, msg); + }); + }); + }); + }); +}); diff --git a/test/mocha/spidermonkey.js b/test/mocha/spidermonkey.js new file mode 100644 index 00000000000..79b0f29fd76 --- /dev/null +++ b/test/mocha/spidermonkey.js @@ -0,0 +1,131 @@ +var assert = require("assert"); +var exec = require("child_process").exec; +var semver = require("semver"); +var UglifyJS = require("../.."); + +describe("spidermonkey export/import sanity test", function() { + it("Should produce a functional build when using --self with spidermonkey", function(done) { + this.timeout(120000); + var uglifyjs = '"' + process.argv[0] + '" bin/uglifyjs'; + var options = semver.satisfies(process.version, "<=0.12") ? "-mc hoist_funs" : "-mc"; + var command = [ + [ + uglifyjs, + "--self", + options, + "--wrap SpiderUglify", + "-o spidermonkey", + ].join(" "), + [ + uglifyjs, + "-p spidermonkey", + options, + ].join(" "), + ].join(" | "); + exec(command, { maxBuffer: 1048576 }, function(err, stdout) { + if (err) throw err; + eval(stdout); + assert.strictEqual(typeof SpiderUglify, "object"); + var result = SpiderUglify.minify("foo([true,,2+3]);"); + assert.strictEqual(result.error, undefined); + assert.strictEqual(result.code, "foo([!0,,5]);"); + done(); + }); + }); + + it("Should not add unnecessary escape slashes to RegExp", function() { + var input = "/[\\\\/]/;"; + var ast = UglifyJS.parse(input).to_mozilla_ast(); + assert.strictEqual(UglifyJS.AST_Node.from_mozilla_ast(ast).print_to_string(), input); + }); + + it("Should judge between directives and strings correctly on import", function() { + var tests = [ + { + input: '"use strict";;"use sloppy"', + directives: 1, + strings: 1 + }, + { + input: ';"use strict"', + directives: 0, + strings: 1 + }, + { + input: '"use strict"; "use something else";', + directives: 2, + strings: 0 + }, + { + input: 'function foo() {"use strict";;"use sloppy" }', + directives: 1, + strings: 1 + }, + { + input: 'function foo() {;"use strict" }', + directives: 0, + strings: 1 + }, + { + input: 'function foo() {"use strict"; "use something else"; }', + directives: 2, + strings: 0 + }, + { + input: 'var foo = function() {"use strict";;"use sloppy" }', + directives: 1, + strings: 1 + }, + { + input: 'var foo = function() {;"use strict" }', + directives: 0, + strings: 1 + }, + { + input: 'var foo = function() {"use strict"; "use something else"; }', + directives: 2, + strings: 0 + }, + { + input: '{"use strict";;"use sloppy" }', + directives: 0, + strings: 2 + }, + { + input: '{;"use strict" }', + directives: 0, + strings: 1 + }, + { + input: '{"use strict"; "use something else"; }', + directives: 0, + strings: 2 + } + ]; + + var counter_directives; + var counter_strings; + + var checkWalker = new UglifyJS.TreeWalker(function(node, descend) { + if (node instanceof UglifyJS.AST_String) { + counter_strings++; + } else if (node instanceof UglifyJS.AST_Directive) { + counter_directives++; + } + }); + + for (var i = 0; i < tests.length; i++) { + counter_directives = 0; + counter_strings = 0; + + var ast = UglifyJS.parse(tests[i].input); + var moz_ast = ast.to_mozilla_ast(); + var from_moz_ast = UglifyJS.AST_Node.from_mozilla_ast(moz_ast); + + from_moz_ast.walk(checkWalker); + + assert.strictEqual(counter_directives, tests[i].directives, "Directives count mismatch for test " + tests[i].input); + assert.strictEqual(counter_strings, tests[i].strings, "String count mismatch for test " + tests[i].input); + } + }); +}); diff --git a/test/mocha/string-literal.js b/test/mocha/string-literal.js new file mode 100644 index 00000000000..a9bac9fff3c --- /dev/null +++ b/test/mocha/string-literal.js @@ -0,0 +1,137 @@ +var assert = require("assert"); +var run_code = require("../sandbox").run_code; +var UglifyJS = require("../node"); + +describe("String literals", function() { + it("Should throw syntax error if a string literal contains a newline", function() { + [ + "'\n'", + "'\r'", + '"\r\n"', + "'\u2028'", + '"\u2029"', + ].forEach(function(input) { + assert.throws(function() { + UglifyJS.parse(input); + }, function(e) { + return e instanceof UglifyJS.JS_Parse_Error + && e.message === "Unterminated string constant"; + }); + }); + }); + it("Should handle line continuation correctly", function() { + [ + '"\\\r"', + '"\\\n"', + '"\\\r\n"', + ].forEach(function(str) { + var code = "console.log(" + str + ");"; + var result = UglifyJS.minify(code); + if (result.error) throw result.error; + assert.strictEqual(run_code(result.code), run_code(code)); + }); + }); + it("Should not throw syntax error if a string has a line continuation", function() { + var ast = UglifyJS.parse('var a = "a\\\nb";'); + assert.equal(ast.print_to_string(), 'var a="ab";'); + }); + it("Should throw error in strict mode if string contains escaped octalIntegerLiteral", function() { + [ + '"use strict";\n"\\76";', + '"use strict";\nvar foo = "\\76";', + '"use strict";\n"\\1";', + '"use strict";\n"\\07";', + '"use strict";\n"\\011"', + ].forEach(function(input) { + assert.throws(function() { + UglifyJS.parse(input); + }, function(e) { + return e instanceof UglifyJS.JS_Parse_Error + && e.message === "Legacy octal escape sequences are not allowed in strict mode"; + }); + }); + }); + it("Should not throw error outside strict mode if string contains escaped octalIntegerLiteral", function() { + [ + [ ';"\\76";', ';">";' ], + [ ';"\\0";', ';"\\0";' ], + [ ';"\\08"', ';"\\x008";' ], + [ ';"\\008"', ';"\\x008";' ], + [ ';"\\0008"', ';"\\x008";' ], + [ ';"use\\\n strict";\n"\\07";', ';"use strict";"\07";' ], + [ '"use strict" === "use strict";\n"\\76";', '"use strict"==="use strict";">";' ], + ].forEach(function(test) { + var ast = UglifyJS.parse(test[0]); + assert.equal(ast.print_to_string(), test[1]); + }); + }); + it("Should not throw error when digit is 8 or 9", function() { + assert.equal(UglifyJS.parse('"use strict";;"\\08"').print_to_string(), '"use strict";;"\\x008";'); + assert.equal(UglifyJS.parse('"use strict";;"\\09"').print_to_string(), '"use strict";;"\\x009";'); + }); + it("Should not unescape unpaired surrogates", function() { + var code = []; + for (var i = 0; i <= 0xF; i++) { + code.push("\\u000" + i.toString(16)); + } + for (;i <= 0xFF; i++) { + code.push("\\u00" + i.toString(16)); + } + for (;i <= 0xFFF; i++) { + code.push("\\u0" + i.toString(16)); + } + for (; i <= 0xFFFF; i++) { + code.push("\\u" + i.toString(16)); + } + code = ';"' + code.join() + '"'; + var normal = UglifyJS.minify(code, { + compress: false, + mangle: false, + output: { + ascii_only: false + } + }); + if (normal.error) throw normal.error; + assert.ok(code.length > normal.code.length); + assert.strictEqual(eval(code), eval(normal.code)); + var ascii = UglifyJS.minify(code, { + compress: false, + mangle: false, + output: { + ascii_only: false + } + }); + if (ascii.error) throw ascii.error; + assert.ok(code.length > ascii.code.length); + assert.strictEqual(eval(code), eval(ascii.code)); + }); + it("Should reject invalid Unicode escape sequence", function() { + [ + 'var foo = "\\u-111"', + 'var bar = "\\u{-1}"', + 'var baz = "\\ugggg"', + ].forEach(function(test) { + assert.throws(function() { + UglifyJS.parse(test); + }, function(e) { + return e instanceof UglifyJS.JS_Parse_Error + && /^Invalid escape sequence: \\u/.test(e.message); + }, test); + }); + }); + it("Should reject invalid code points in Unicode escape sequence", function() { + [ + // A bit over the valid range + '"\\u{110000}"', + // 32-bit overflow resulting in "a" + '"\\u{100000061}"', + ].forEach(function(test) { + assert.throws(function() { + UglifyJS.parse(test); + }, function(e) { + return e instanceof UglifyJS.JS_Parse_Error + && /^Invalid escape sequence: \\u{1/.test(e.message); + }, test); + }); + }); +}); diff --git a/test/mocha/templates.js b/test/mocha/templates.js new file mode 100644 index 00000000000..3e5134a8ecd --- /dev/null +++ b/test/mocha/templates.js @@ -0,0 +1,67 @@ +var assert = require("assert"); +var run_code = require("../sandbox").run_code; +var semver = require("semver"); +var UglifyJS = require("../node"); + +describe("Template literals", function() { + it("Should reject invalid literal", function() { + [ + "`foo\\`", + "`foo${bar`", + "`foo${bar}", + ].forEach(function(input) { + assert.throws(function() { + UglifyJS.parse(input); + }, function(e) { + return e instanceof UglifyJS.JS_Parse_Error + && e.message === "Unterminated template literal"; + }, input); + }); + }); + it("Should reject invalid expression", function() { + [ + "`foo${bar;}`", + "`foo${42bar}`", + ].forEach(function(input) { + assert.throws(function() { + UglifyJS.parse(input); + }, function(e) { + return e instanceof UglifyJS.JS_Parse_Error; + }, input); + }); + }); + it("Should process line-break characters correctly", function() { + [ + // native line breaks + [ "`foo\nbar`", "`foo\nbar`" ], + [ "`foo\rbar`", "`foo\nbar`" ], + [ "`foo\r\nbar`", "`foo\nbar`" ], + [ "`foo\r\n\rbar`", "`foo\n\nbar`" ], + // escaped line breaks + [ "`foo\\nbar`", "`foo\\nbar`" ], + [ "`foo\\rbar`", "`foo\\rbar`" ], + [ "`foo\r\\nbar`", "`foo\n\\nbar`" ], + [ "`foo\\r\nbar`", "`foo\\r\nbar`" ], + [ "`foo\\r\\nbar`", "`foo\\r\\nbar`" ], + // continuation + [ "`foo\\\nbar`", "`foo\\\nbar`" ], + [ "`foo\\\rbar`", "`foo\\\nbar`" ], + [ "`foo\\\r\nbar`", "`foo\\\nbar`" ], + [ "`foo\\\r\n\rbar`", "`foo\\\n\nbar`" ], + [ "`foo\\\\nbar`", "`foo\\\\nbar`" ], + [ "`foo\\\\rbar`", "`foo\\\\rbar`" ], + [ "`foo\\\\r\nbar`", "`foo\\\\r\nbar`" ], + ].forEach(function(test) { + var input = "console.log(" + test[0] + ");"; + var result = UglifyJS.minify(input, { + compress: false, + mangle: false, + }); + if (result.error) throw result.error; + var expected = "console.log(" + test[1] + ");"; + assert.strictEqual(result.code, expected, test[0]); + if (semver.satisfies(process.version, "<4")) return; + assert.strictEqual(run_code(result.code), run_code(input), test[0]); + }); + }); +}); diff --git a/test/mocha/tokens.js b/test/mocha/tokens.js new file mode 100644 index 00000000000..4a495b5b7a6 --- /dev/null +++ b/test/mocha/tokens.js @@ -0,0 +1,26 @@ +var assert = require("assert"); +var UglifyJS = require("../.."); + +describe("tokens", function() { + it("Should give correct positions for accessors", function() { + // location 0 1 2 3 4 + // 01234567890123456789012345678901234567890123456789 + var ast = UglifyJS.parse("var obj = { get [prop]() { return undefined; } }"); + // test all AST_ObjectProperty tokens are set as expected + var found = false; + ast.walk(new UglifyJS.TreeWalker(function(node) { + if (node instanceof UglifyJS.AST_ObjectProperty) { + found = true; + assert.equal(node.start.pos, 12); + assert.equal(node.end.endpos, 46); + assert(node.key instanceof UglifyJS.AST_SymbolRef); + assert.equal(node.key.start.pos, 17); + assert.equal(node.key.end.endpos, 21); + assert(node.value instanceof UglifyJS.AST_Accessor); + assert.equal(node.value.start.pos, 22); + assert.equal(node.value.end.endpos, 46); + } + })); + assert(found, "AST_ObjectProperty not found"); + }); +}); diff --git a/test/mocha/with.js b/test/mocha/with.js new file mode 100644 index 00000000000..7c17077e019 --- /dev/null +++ b/test/mocha/with.js @@ -0,0 +1,23 @@ +var assert = require("assert"); +var UglifyJS = require("../node"); + +describe("With", function() { + it("Should throw syntaxError when using with statement in strict mode", function() { + var code = '"use strict";\nthrow NotEarlyError;\nwith ({}) { }'; + var test = function() { + UglifyJS.parse(code); + } + var error = function(e) { + return e instanceof UglifyJS.JS_Parse_Error + && e.message === "Strict mode may not include a with statement"; + } + assert.throws(test, error); + }); + it("Should set uses_with for scopes involving With statements", function() { + var ast = UglifyJS.parse("with(e) {f(1, 2)}"); + ast.figure_out_scope(); + assert.equal(ast.uses_with, true); + assert.equal(ast.body[0].expression.scope.resolve().uses_with, true); + assert.equal(ast.body[0].body.body[0].body.expression.scope.resolve().uses_with, true); + }); +}); diff --git a/test/mocha/yields.js b/test/mocha/yields.js new file mode 100644 index 00000000000..e93d65ee6b1 --- /dev/null +++ b/test/mocha/yields.js @@ -0,0 +1,82 @@ +var assert = require("assert"); +var UglifyJS = require("../node"); + +describe("generator", function() { + it("Should reject `yield` as symbol name within generator functions only", function() { + [ + "function yield() {}", + "function(yield) {}", + "function() { yield: {} }", + "function() { var yield; }", + "function() { function yield() {} }", + "function() { try {} catch (yield) {} }", + ].forEach(function(code) { + var ast = UglifyJS.parse("(" + code + ")();"); + assert.strictEqual(ast.TYPE, "Toplevel"); + assert.strictEqual(ast.body.length, 1); + assert.strictEqual(ast.body[0].TYPE, "SimpleStatement"); + assert.strictEqual(ast.body[0].body.TYPE, "Call"); + assert.strictEqual(ast.body[0].body.expression.TYPE, "Function"); + assert.throws(function() { + UglifyJS.parse("(" + code.replace(/^function/, "function*") + ")();"); + }, function(e) { + return e instanceof UglifyJS.JS_Parse_Error; + }, code); + }); + }); + it("Should reject `yield` expression outside of generator functions", function() { + [ + "yield 42;", + "function f() { yield 42; }", + "function* f() { function g() { yield 42; } }", + ].forEach(function(code) { + assert.throws(function() { + UglifyJS.parse(code); + }, function(e) { + return e instanceof UglifyJS.JS_Parse_Error; + }, code); + }); + }); + it("Should reject `yield` expression directly on computed key of function argument", function() { + [ + "function f({ [yield 42]: a }) {}", + "function* f({ [yield 42]: a }) {}", + ].forEach(function(code) { + assert.throws(function() { + UglifyJS.parse(code); + }, function(e) { + return e instanceof UglifyJS.JS_Parse_Error; + }, code); + }); + }); + it("Should accept `yield` expression nested within computed key of function argument", function() { + [ + "function f({ [function*() { yield 42; }()]: a }) {}", + "function* f({ [function*() { yield 42; }()]: a }) {}", + ].forEach(function(code) { + var ast = UglifyJS.parse(code); + assert.strictEqual(ast.TYPE, "Toplevel"); + assert.strictEqual(ast.body.length, 1); + assert.strictEqual(ast.body[0].argnames.length, 1); + assert.strictEqual(ast.body[0].argnames[0].TYPE, "DestructuredObject"); + }); + }); + it("Should reject `yield*` without an expression", function() { + [ + "yield*", + "yield*;", + "yield*,", + "(yield*)", + "[ yield* ]", + "42[yield*]", + "yield* && 42", + ].forEach(function(code) { + code = "function* f() { " + code + " }"; + assert.throws(function() { + UglifyJS.parse(code); + }, function(e) { + return e instanceof UglifyJS.JS_Parse_Error; + }, code); + }); + }); +}); diff --git a/test/mozilla-ast.js b/test/mozilla-ast.js index 026286761e4..8be2f7fcd50 100644 --- a/test/mozilla-ast.js +++ b/test/mozilla-ast.js @@ -1,103 +1,190 @@ // Testing UglifyJS <-> SpiderMonkey AST conversion -// through generative testing. +"use strict"; -var UglifyJS = require(".."), - escodegen = require("escodegen"), - esfuzz = require("esfuzz"), - estraverse = require("estraverse"), - prefix = Array(20).join("\b") + " "; +var acorn = require("acorn"); +var ufuzz = require("./ufuzz"); +var UglifyJS = require(".."); -// Normalizes input AST for UglifyJS in order to get correct comparison. - -function normalizeInput(ast) { - return estraverse.replace(ast, { - enter: function(node, parent) { - switch (node.type) { - // Internally mark all the properties with semi-standard type "Property". - case "ObjectExpression": - node.properties.forEach(function (property) { - property.type = "Property"; - }); - break; - - // Since UglifyJS doesn"t recognize different types of property keys, - // decision on SpiderMonkey node type is based on check whether key - // can be valid identifier or not - so we do in input AST. - case "Property": - var key = node.key; - if (key.type === "Literal" && typeof key.value === "string" && UglifyJS.is_identifier(key.value)) { - node.key = { - type: "Identifier", - name: key.value - }; - } else if (key.type === "Identifier" && !UglifyJS.is_identifier(key.name)) { - node.key = { - type: "Literal", - value: key.name - }; - } - break; - - // UglifyJS internally flattens all the expression sequences - either - // to one element (if sequence contains only one element) or flat list. - case "SequenceExpression": - node.expressions = node.expressions.reduce(function flatten(list, expr) { - return list.concat(expr.type === "SequenceExpression" ? expr.expressions.reduce(flatten, []) : [expr]); - }, []); - if (node.expressions.length === 1) { - return node.expressions[0]; - } - break; - } - } +function beautify(ast) { + var beautified = UglifyJS.minify(ast, { + compress: false, + mangle: false, + module: ufuzz.module, + output: { + ast: true, + beautify: true, + braces: true, + }, }); + if (!beautified.error) { + var verify = UglifyJS.minify(beautified.code, { + compress: false, + mangle: false, + module: ufuzz.module, + }); + if (verify.error) return verify; + } + return beautified; } -module.exports = function(options) { - console.log("--- UglifyJS <-> Mozilla AST conversion"); +function validate(ast) { + try { + ast.walk(new UglifyJS.TreeWalker(function(node) { + node.validate(); + })); + } catch (e) { + return { error: e }; + } + return UglifyJS.minify(ast, { + compress: false, + mangle: false, + module: ufuzz.module, + output: { + ast: true, + }, + validate: true, + }); +} - for (var counter = 0; counter < options.iterations; counter++) { - process.stdout.write(prefix + counter + "/" + options.iterations); +function patch_import(code) { + return code.replace(/\bimport\s*\{\s*\}\s*from\s*(['"])/g, "import$1") + .replace(/\b(import\b.*?)\s*,\s*\{\s*\}\s*(from\s*['"])/g, "$1 $2"); +} - var ast1 = normalizeInput(esfuzz.generate({ - maxDepth: options.maxDepth - })); - - var ast2 = - UglifyJS - .AST_Node - .from_mozilla_ast(ast1) - .to_mozilla_ast(); +function equals(input, transformed) { + if (input.code === transformed.code) return true; + return patch_import(input.code) === patch_import(transformed.code); +} - var astPair = [ - {name: 'expected', value: ast1}, - {name: 'actual', value: ast2} - ]; +function test(input, to_moz, description, skip_on_error, beautified) { + try { + var ast = UglifyJS.AST_Node.from_mozilla_ast(to_moz(input)); + } catch (e) { + if (skip_on_error) return true; + console.error("//============================================================="); + console.error("//", description, "failed... round", round); + console.error(e); + console.error("// original code"); + if (beautified === true) console.error("// (beautified)"); + console.error(input.code); + return false; + } + var transformed = validate(ast); + if (transformed.error || !equals(input, transformed)) { + if (!beautified) { + beautified = beautify(input.ast); + if (!beautified.error) { + beautified.raw = beautified.code; + if (!test(beautified, to_moz, description, skip_on_error, true)) return false; + } + } + console.error("//============================================================="); + console.error("// !!!!!! Failed... round", round); + console.error("// original code"); + if (beautified.error) { + console.error("// !!! beautify failed !!!"); + console.error(beautified.error.stack); + } else if (beautified === true) { + console.error("// (beautified)"); + } + console.error(input.raw); + console.error(); + console.error(); + console.error("//-------------------------------------------------------------"); + console.error("//", description); + if (transformed.error) { + console.error(transformed.error.stack); + } else { + beautified = beautify(transformed.ast); + if (beautified.error) { + console.error("// !!! beautify failed !!!"); + console.error(beautified.error.stack); + console.error(transformed.code); + } else { + console.error("// (beautified)"); + console.error(beautified.code); + } + } + console.error("!!!!!! Failed... round", round); + return false; + } + return true; +} - var jsPair = astPair.map(function(item) { - return { - name: item.name, - value: escodegen.generate(item.value) +var num_iterations = ufuzz.num_iterations; +var minify_options = require("./ufuzz/options.json").map(JSON.stringify); +minify_options.unshift(null); +for (var round = 1; round <= num_iterations; round++) { + process.stdout.write(round + " of " + num_iterations + "\r"); + var code = ufuzz.createTopLevelCode(); + minify_options.forEach(function(options) { + var ok = true; + var minified; + if (options) { + var o = JSON.parse(options); + o.module = ufuzz.module; + minified = UglifyJS.minify(code, o); + if (minified.error) { + console.log("//============================================================="); + console.log("// minify() failed... round", round); + console.log("// original code"); + console.log(code); + console.log(); + console.log(); + console.log("//-------------------------------------------------------------"); + console.log("minify(options):"); + console.log(JSON.stringify(o, null, 2)); + return; } + minified = minified.code; + } + var input = UglifyJS.minify(minified || code, { + compress: false, + mangle: false, + module: ufuzz.module, + output: { + ast: true, + }, }); - - if (jsPair[0].value !== jsPair[1].value) { - var fs = require("fs"); - var acorn = require("acorn"); - - fs.existsSync("tmp") || fs.mkdirSync("tmp"); - - jsPair.forEach(function (item) { - var fileName = "tmp/dump_" + item.name; - var ast = acorn.parse(item.value); - fs.writeFileSync(fileName + ".js", item.value); - fs.writeFileSync(fileName + ".json", JSON.stringify(ast, null, 2)); + input.raw = options ? input.code : code; + if (input.error) { + ok = false; + console.error("//============================================================="); + console.error("// parse() failed... round", round); + console.error("// original code"); + console.error(code); + console.error(); + console.error(); + if (options) { + console.error("//-------------------------------------------------------------"); + console.error("// minified code"); + console.error(minified); + console.error(); + console.error(); + console.error("//-------------------------------------------------------------"); + console.error("minify(options):"); + console.error(JSON.stringify(o, null, 2)); + console.error(); + console.error(); + } + console.error("//-------------------------------------------------------------"); + console.error("// parse() error"); + console.error(input.error); + } + if (ok) ok = test(input, function(input) { + return input.ast.to_mozilla_ast(); + }, "AST_Node.to_mozilla_ast()"); + if (ok) ok = test(input, function(input) { + return acorn.parse(input.raw, { + ecmaVersion: "latest", + locations: true, + sourceType: "module", }); - - process.stdout.write("\n"); - throw new Error("Got different outputs, check out tmp/dump_*.{js,json} for codes and ASTs."); + }, "acorn.parse()", !ufuzz.verbose); + if (!ok && isFinite(num_iterations)) { + console.log(); + process.exit(1); } - } - - process.stdout.write(prefix + "Probability of error is less than " + (100 / options.iterations) + "%, stopping.\n"); -}; \ No newline at end of file + }); +} +console.log(); diff --git a/test/node.js b/test/node.js new file mode 100644 index 00000000000..b568c03c628 --- /dev/null +++ b/test/node.js @@ -0,0 +1,6 @@ +var fs = require("fs"); + +new Function("domprops", "exports", require("../tools/node").FILES.map(function(file) { + if (/exports\.js$/.test(file)) file = require.resolve("./exports"); + return fs.readFileSync(file, "utf8"); +}).join("\n\n"))(require("../tools/domprops.json"), exports); diff --git a/test/reduce.js b/test/reduce.js new file mode 100644 index 00000000000..10a0086bf50 --- /dev/null +++ b/test/reduce.js @@ -0,0 +1,827 @@ +var crypto = require("crypto"); +var U = require(".."); +var List = U.List; +var os = require("os"); +var sandbox = require("./sandbox"); + +// Reduce a test case by iteratively replacing AST nodes with various +// permutations. Each AST_Statement in the tree is also speculatively dropped +// to determine whether it is needed. If the altered tree and the last known +// good tree produce the same output after being run, then the permutation +// survives to the next generation and is the basis for subsequent iterations. +// The test case is reduced as a consequence of complex expressions being +// replaced with simpler ones. Note that a reduced test case will have +// different runtime output - it is not functionally equivalent to the +// original. The only criteria is that once the generated reduced test case is +// run without minification, it will produce different output from the code +// minified with `minify_options`. Returns a `minify` result object. + +Error.stackTraceLimit = Infinity; +module.exports = function reduce_test(testcase, minify_options, reduce_options) { + minify_options = minify_options || {}; + reduce_options = reduce_options || {}; + var parse_options = { + module: minify_options.module || minify_options.module === undefined, + } + var print_options = {}; + [ + "ie", + "v8", + "webkit", + ].forEach(function(name) { + var value = minify_options[name] || minify_options.output && minify_options.output[name]; + if (value) print_options[name] = value; + }); + if (testcase instanceof U.AST_Node) testcase = testcase.print_to_string(print_options); + var max_iterations = reduce_options.max_iterations || 1000; + var max_timeout = reduce_options.max_timeout || 10000; + var warnings = []; + var log = reduce_options.log || function(msg) { + warnings.push(msg); + }; + var verbose = reduce_options.verbose; + var minify_options_json = JSON.stringify(minify_options, null, 2); + var result_cache = Object.create(null); + var test_for_diff = compare_run_code; + // the initial timeout to assess the viability of the test case must be large + var differs = test_for_diff(testcase, minify_options, result_cache, max_timeout); + + if (verbose) { + log("// Node.js " + process.version + " on " + os.platform() + " " + os.arch()); + } + if (differs && differs.error && [ "DefaultsError", "SyntaxError" ].indexOf(differs.error.name) < 0) { + test_for_diff = test_minify; + differs = test_for_diff(testcase, minify_options, result_cache, max_timeout); + } + // same stdout result produced when minified + if (!differs) return { + code: [ + "// Can't reproduce test failure", + "// minify options: " + to_comment(minify_options_json) + ].join("\n"), + warnings: warnings, + }; + if (differs.timed_out) return { + code: [ + "// Can't reproduce test failure within " + max_timeout + "ms", + "// minify options: " + to_comment(minify_options_json) + ].join("\n"), + warnings: warnings, + }; + if (differs.error) { + differs.warnings = warnings; + return differs; + } + if (sandbox.is_error(differs.unminified_result) + && sandbox.is_error(differs.minified_result) + && differs.unminified_result.name == differs.minified_result.name) return { + code: [ + "// No differences except in error message", + "// minify options: " + to_comment(minify_options_json) + ].join("\n"), + warnings: warnings, + }; + max_timeout = Math.min(100 * differs.elapsed, max_timeout); + // Replace expressions with constants that will be parsed into + // AST_Nodes as required. Each AST_Node has its own permutation count, + // so these replacements can't be shared. + // Although simpler replacements are generally faster and better, + // feel free to experiment with a different replacement set. + var REPLACEMENTS = [ + // "null", "''", "false", "'foo'", "undefined", "9", + "1", "0", + ]; + + // There's a relationship between each node's _permute counter and + // REPLACEMENTS.length which is why fractional _permutes were needed. + // One could scale all _permute operations by a factor of `steps` + // to only deal with integer operations, but this works well enough. + var steps = 4; // must be a power of 2 + var step = 1 / steps; // 0.25 is exactly representable in floating point + + var tt = new U.TreeTransformer(function(node, descend, in_list) { + if (CHANGED) return; + + // quick ignores + if (node instanceof U.AST_Accessor) return; + if (node instanceof U.AST_Directive) return; + if (!in_list && node instanceof U.AST_EmptyStatement) return; + if (node instanceof U.AST_Label) return; + if (node instanceof U.AST_LabelRef) return; + if (node instanceof U.AST_Toplevel) return; + var parent = tt.parent(); + if (node instanceof U.AST_SymbolFunarg && parent instanceof U.AST_Accessor) return; + if (!in_list && parent.rest !== node && node instanceof U.AST_SymbolDeclaration) return; + + // ensure that the _permute prop is a number. + // can not use `node.start._permute |= 0;` as it will erase fractional part. + if (typeof node.start._permute === "undefined") node.start._permute = 0; + + // if node reached permutation limit - skip over it. + // no structural AST changes before this point. + if (node.start._permute >= REPLACEMENTS.length) return; + + // ignore lvalues + if (parent instanceof U.AST_Assign && parent.left === node) return; + if (parent instanceof U.AST_DefaultValue && parent.name === node) return; + if (parent instanceof U.AST_DestructuredKeyVal && parent.value === node) return; + if (parent instanceof U.AST_Unary && parent.expression === node) switch (parent.operator) { + case "++": + case "--": + case "delete": + return; + } + if (parent instanceof U.AST_VarDef && parent.name === node) return; + // preserve class methods + if (parent instanceof U.AST_ClassMethod && parent.value === node) return; + // preserve exports + if (parent instanceof U.AST_ExportDeclaration) return; + if (parent instanceof U.AST_ExportDefault) return; + if (parent instanceof U.AST_ExportForeign) return; + if (parent instanceof U.AST_ExportReferences) return; + // preserve sole definition of an export statement + if (node instanceof U.AST_VarDef + && parent.definitions.length == 1 + && tt.parent(1) instanceof U.AST_ExportDeclaration) { + return; + } + // preserve for (var xxx; ...) + if (parent instanceof U.AST_For && parent.init === node && node instanceof U.AST_Definitions) return node; + // preserve for (xxx in/of ...) + if (parent instanceof U.AST_ForEnumeration && parent.init === node) return node; + // preserve super(...) + if (node.TYPE == "Call" && node.expression instanceof U.AST_Super) return; + if (node instanceof U.AST_Super && parent.TYPE == "Call" && parent.expression === node) return node; + + // node specific permutations with no parent logic + + if (node instanceof U.AST_Array) { + var expr = node.elements[0]; + if (expr && !(expr instanceof U.AST_Hole)) { + node.start._permute++; + CHANGED = true; + return expr instanceof U.AST_Spread ? expr.expression : expr; + } + } + else if (node instanceof U.AST_Await) { + node.start._permute++; + CHANGED = true; + return node.expression; + } + else if (node instanceof U.AST_Binary) { + var permute = ((node.start._permute += step) * steps | 0) % 4; + var expr = [ + node.left, + node.right, + ][ permute & 1 ]; + if (expr instanceof U.AST_Destructured) expr = expr.transform(new U.TreeTransformer(function(node, descend) { + if (node instanceof U.AST_DefaultValue) return new U.AST_Assign({ + operator: "=", + left: node.name.transform(this), + right: node.value, + start: {}, + }); + if (node instanceof U.AST_DestructuredKeyVal) return new U.AST_ObjectKeyVal(node); + if (node instanceof U.AST_Destructured) { + node = new (node instanceof U.AST_DestructuredArray ? U.AST_Array : U.AST_Object)(node); + descend(node, this); + } + return node; + })); + CHANGED = true; + return permute < 2 ? expr : wrap_with_console_log(expr); + } + else if (node instanceof U.AST_BlockStatement) { + if (in_list && node.body.filter(function(node) { + return node instanceof U.AST_Const || node instanceof U.AST_Let; + }).length == 0) { + node.start._permute++; + CHANGED = true; + return List.splice(node.body); + } + } + else if (node instanceof U.AST_Call) { + var expr = [ + !(node.expression instanceof U.AST_Super) && node.expression, + node.args[0], + null, // intentional + ][ ((node.start._permute += step) * steps | 0) % 3 ]; + if (expr) { + CHANGED = true; + return expr instanceof U.AST_Spread ? expr.expression : expr; + } + if (node.expression instanceof U.AST_Arrow && node.expression.value) { + var seq = node.args.slice(); + seq.push(node.expression.value); + CHANGED = true; + return to_sequence(seq); + } + if (node.expression instanceof U.AST_Function) { + // hoist and return expressions from the IIFE function expression + var scope = tt.find_parent(U.AST_Scope), seq = []; + node.expression.body.forEach(function(node) { + var expr = node instanceof U.AST_Exit ? node.value : node.body; + if (expr instanceof U.AST_Node && !U.is_statement(expr) && can_hoist(expr, scope)) { + // collect expressions from each statement's body + seq.push(expr); + } + }); + CHANGED = true; + return to_sequence(seq); + } + } + else if (node instanceof U.AST_Catch) { + // drop catch block + node.start._permute++; + CHANGED = true; + return null; + } + else if (node instanceof U.AST_Conditional) { + CHANGED = true; + return [ + node.condition, + node.consequent, + node.alternative, + ][ ((node.start._permute += step) * steps | 0) % 3 ]; + } + else if (node instanceof U.AST_DefaultValue) { + node.start._permute++; + CHANGED = true; + return node.name; + } + else if (node instanceof U.AST_Defun) { + switch (((node.start._permute += step) * steps | 0) % 2) { + case 0: + CHANGED = true; + return List.skip; + default: + if (can_hoist(node, tt.find_parent(U.AST_Scope))) { + // hoist function declaration body + var body = node.body; + node.body = []; + // retain function with empty body to be dropped later + body.push(node); + CHANGED = true; + return List.splice(body); + } + } + } + else if (node instanceof U.AST_DestructuredArray) { + var expr = node.elements[0]; + if (expr && !(expr instanceof U.AST_Hole)) { + node.start._permute++; + CHANGED = true; + return expr; + } + } + else if (node instanceof U.AST_DestructuredObject) { + // first property's value + var expr = node.properties[0]; + if (expr) { + node.start._permute++; + CHANGED = true; + return expr.value; + } + } + else if (node instanceof U.AST_DWLoop) { + var expr = [ + node.condition, + node.body, + null, // intentional + ][ (node.start._permute * steps | 0) % 3 ]; + node.start._permute += step; + if (!expr) { + if (node.body[0] instanceof U.AST_Break) { + if (node instanceof U.AST_Do) { + CHANGED = true; + return List.skip; + } + expr = node.condition; // AST_While - fall through + } + } + if (expr && (expr !== node.body || !has_loopcontrol(expr, node, parent))) { + CHANGED = true; + return to_statement(expr); + } + } + else if (node instanceof U.AST_ExportDeclaration) { + node.start._permute++; + CHANGED = true; + return node.body; + } + else if (node instanceof U.AST_ExportDefault) { + node.start._permute++; + CHANGED = true; + return to_statement(node.body); + } + else if (node instanceof U.AST_Finally) { + // drop finally block + node.start._permute++; + CHANGED = true; + return null; + } + else if (node instanceof U.AST_For) { + var expr = [ + node.init, + node.condition, + node.step, + node.body, + ][ (node.start._permute * steps | 0) % 4 ]; + node.start._permute += step; + if (expr && (expr !== node.body || !has_loopcontrol(expr, node, parent))) { + CHANGED = true; + return to_statement_init(expr); + } + } + else if (node instanceof U.AST_ForEnumeration) { + var expr; + switch ((node.start._permute * steps | 0) % 4) { + case 0: + expr = node.object; + break; + case 1: + expr = wrap_with_console_log(node.object); + break; + case 2: + if (has_loopcontrol(node.body, node, parent)) break; + expr = node.body; + break; + case 3: + if (!(node.init instanceof U.AST_Var)) break; + if (node.init.definitions[0].name instanceof U.AST_Destructured) break; + expr = node.init; + break; + } + node.start._permute += step; + if (expr) { + CHANGED = true; + return to_statement_init(expr); + } + } + else if (node instanceof U.AST_If) { + var expr = [ + node.condition, + node.body, + node.alternative, + node, + ][ (node.start._permute * steps | 0) % 4 ]; + node.start._permute += step; + if (expr === node) { + if (node.alternative) { + expr = node.clone(); + expr.alternative = null; + CHANGED = true; + return expr; + } + } else if (expr) { + // replace if statement with its condition, then block or else block + CHANGED = true; + return to_statement(expr); + } + } + else if (node instanceof U.AST_LabeledStatement) { + if (node.body instanceof U.AST_Statement + && !has_loopcontrol(node.body, node.body, node)) { + // replace labelled statement with its non-labelled body + node.start._permute = REPLACEMENTS.length; + CHANGED = true; + return node.body; + } + } + else if (node instanceof U.AST_Object) { + // first property's value + var expr = node.properties[0]; + if (expr instanceof U.AST_ObjectKeyVal) { + expr = expr.value; + } else if (expr instanceof U.AST_Spread) { + expr = expr.expression; + } else if (expr && expr.key instanceof U.AST_Node) { + expr = expr.key; + } else { + expr = null; + } + if (expr) { + node.start._permute++; + CHANGED = true; + return expr; + } + } + else if (node instanceof U.AST_PropAccess) { + var expr = [ + !(node.expression instanceof U.AST_Super) && node.expression, + node.property instanceof U.AST_Node && !(parent instanceof U.AST_Destructured) && node.property, + ][ node.start._permute++ % 2 ]; + if (expr) { + CHANGED = true; + return expr; + } + } + else if (node instanceof U.AST_SimpleStatement) { + if (node.body instanceof U.AST_Call && node.body.expression instanceof U.AST_Function) { + // hoist simple statement IIFE function expression body + node.start._permute++; + if (can_hoist(node.body.expression, tt.find_parent(U.AST_Scope))) { + CHANGED = true; + return List.splice(node.body.expression.body); + } + } + } + else if (node instanceof U.AST_Switch) { + var expr = [ + node.expression, // switch expression + node.body[0] && node.body[0].expression, // first case expression or undefined + node.body[0], // first case body or undefined + ][ (node.start._permute * steps | 0) % 4 ]; + node.start._permute += step; + if (expr && (!(expr instanceof U.AST_Statement) || !has_loopcontrol(expr, node, parent))) { + CHANGED = true; + return expr instanceof U.AST_SwitchBranch ? new U.AST_BlockStatement({ + body: expr.body.slice(), + start: {}, + }) : to_statement(expr); + } + } + else if (node instanceof U.AST_Try) { + var body = [ + node.body, + node.bcatch && node.bcatch.body, + node.bfinally && node.bfinally.body, + null, // intentional + ][ (node.start._permute * steps | 0) % 4 ]; + node.start._permute += step; + if (body) { + // replace try statement with try block, catch block, or finally block + CHANGED = true; + return new U.AST_BlockStatement({ + body: body, + start: {}, + }); + } else { + // replace try with a break or return if first in try statement + if (node.body[0] instanceof U.AST_Break + || node.body[0] instanceof U.AST_Return) { + CHANGED = true; + return node.body[0]; + } + } + } + else if (node instanceof U.AST_Unary) { + node.start._permute++; + CHANGED = true; + return node.expression; + } + else if (node instanceof U.AST_Var) { + if (node.definitions.length == 1 && node.definitions[0].value) { + // first declaration value + node.start._permute++; + CHANGED = true; + return to_statement(node.definitions[0].value); + } + } + else if (node instanceof U.AST_VarDef) { + if (node.value && !(node.name instanceof U.AST_Destructured || parent instanceof U.AST_Const)) { + node.start._permute++; + CHANGED = true; + return new U.AST_VarDef({ + name: node.name, + start: {}, + }); + } + } + + if (in_list) { + // drop switch branches + if (parent instanceof U.AST_Switch && parent.expression != node) { + node.start._permute++; + CHANGED = true; + return List.skip; + } + + // replace or skip statement + if (node instanceof U.AST_Statement) { + node.start._permute++; + CHANGED = true; + return List.skip; + } + + // remove this node unless its the sole element of a (transient) sequence + if (!(parent instanceof U.AST_Sequence) || parent.expressions.length > 1) { + node.start._permute++; + CHANGED = true; + return List.skip; + } + } else if (parent.rest === node) { + node.start._permute++; + CHANGED = true; + return null; + } + + // replace this node + var newNode = U.is_statement(node) ? new U.AST_EmptyStatement({ + start: {}, + }) : U.parse(REPLACEMENTS[node.start._permute % REPLACEMENTS.length | 0], { + expression: true, + }); + newNode.start._permute = ++node.start._permute; + CHANGED = true; + return newNode; + }, function(node, in_list) { + if (node instanceof U.AST_Definitions) { + // remove empty var statement + if (node.definitions.length == 0) return in_list ? List.skip : new U.AST_EmptyStatement({ + start: {}, + }); + } else if (node instanceof U.AST_ObjectMethod) { + if (!/Function$/.test(node.value.TYPE)) return new U.AST_ObjectKeyVal({ + key: node.key, + value: node.value, + start: {}, + }); + } else if (node instanceof U.AST_Sequence) { + // expand single-element sequence + if (node.expressions.length == 1) return node.expressions[0]; + } else if (node instanceof U.AST_Try) { + // expand orphaned try block + if (!node.bcatch && !node.bfinally) return new U.AST_BlockStatement({ + body: node.body, + start: {}, + }); + } + }); + + var before_iterations, diff_error_message, passes = 3, testcase_ast; + for (var pass = 1; pass <= passes; pass++) { + if (before_iterations !== testcase) { + testcase_ast = U.parse(testcase, parse_options); + if (diff_error_message === testcase) { + // only difference detected is in error message, so expose that and try again + testcase_ast.transform(new U.TreeTransformer(function(node, descend) { + if (node.TYPE == "Call" && node.expression.print_to_string() == "console.log") { + return to_sequence(node.args); + } + if (node instanceof U.AST_Catch && node.argname instanceof U.AST_SymbolCatch) { + descend(node, this); + node.body.unshift(new U.AST_SimpleStatement({ + body: wrap_with_console_log(new U.AST_SymbolRef(node.argname)), + start: {}, + })); + return node; + } + })); + var code = testcase_ast.print_to_string(print_options); + var diff = test_for_diff(code, minify_options, result_cache, max_timeout); + if (diff && !diff.timed_out && !diff.error) { + testcase = code; + differs = diff; + } else { + testcase_ast = U.parse(testcase, parse_options); + } + } + diff_error_message = null; + testcase_ast.walk(new U.TreeWalker(function(node) { + // unshare start props to retain visit data between iterations + node.start = JSON.parse(JSON.stringify(node.start)); + node.start._permute = 0; + })); + before_iterations = testcase; + } + for (var c = 0; c < max_iterations; c++) { + if (verbose && c % (pass == 1 ? 25 : 100) == 0) { + log("// reduce test pass " + pass + ", iteration " + c + ": " + testcase.length + " bytes"); + } + var CHANGED = false; + var code_ast = testcase_ast.clone(true).transform(tt); + if (!CHANGED) break; + try { + var code = code_ast.print_to_string(print_options); + } catch (ex) { + // AST is not well formed. + // no harm done - just log the error, ignore latest change and continue iterating. + log("*** Error generating code from AST."); + log(ex.stack); + log("*** Discarding permutation and continuing."); + continue; + } + var diff = test_for_diff(code, minify_options, result_cache, max_timeout); + if (diff) { + if (diff.timed_out) { + // can't trust the validity of `code_ast` and `code` when timed out. + // no harm done - just ignore latest change and continue iterating. + } else if (diff.error) { + // something went wrong during minify() - could be malformed AST or genuine bug. + // no harm done - just log code & error, ignore latest change and continue iterating. + log("*** Error during minification."); + log(code); + log(diff.error.stack); + log("*** Discarding permutation and continuing."); + } else if (sandbox.is_error(diff.unminified_result) + && sandbox.is_error(diff.minified_result) + && diff.unminified_result.name == diff.minified_result.name) { + // ignore difference in error messages caused by minification + diff_error_message = testcase; + } else { + // latest permutation is valid, so use it as the basis of new changes + testcase_ast = code_ast; + testcase = code; + differs = diff; + } + } + } + if (before_iterations !== testcase) continue; + if (c < max_iterations) break; + passes++; + } + var beautified = U.minify(testcase, { + compress: false, + mangle: false, + module: minify_options.module, + output: function() { + var options = JSON.parse(JSON.stringify(print_options)); + options.beautify = true; + options.braces = true; + options.comments = true; + return options; + }(), + }); + testcase = { + code: testcase, + }; + if (!beautified.error) { + diff = test_for_diff(beautified.code, minify_options, result_cache, max_timeout); + if (diff && !diff.timed_out && !diff.error) { + testcase = beautified; + testcase.code = "// (beautified)\n" + testcase.code; + differs = diff; + } + } + var lines = [ "" ]; + if (isNaN(max_timeout)) { + lines.push("// minify error: " + to_comment(differs.minified_result.stack)); + } else { + var unminified_result = differs.unminified_result; + var minified_result = differs.minified_result; + if (trim_trailing_whitespace(unminified_result) == trim_trailing_whitespace(minified_result)) { + lines.push( + "// (stringified)", + "// output: " + JSON.stringify(unminified_result), + "// minify: " + JSON.stringify(minified_result) + ); + } else { + lines.push( + "// output: " + to_comment(unminified_result), + "// minify: " + to_comment(minified_result) + ); + } + } + lines.push("// options: " + to_comment(minify_options_json)); + testcase.code += lines.join("\n"); + testcase.warnings = warnings; + return testcase; +}; + +function to_comment(value) { + return ("" + value).replace(/\n/g, "\n// "); +} + +function trim_trailing_whitespace(value) { + return ("" + value).replace(/\s+$/, ""); +} + +function has_loopcontrol(body, loop, label) { + var found = false; + var tw = new U.TreeWalker(function(node) { + if (found) return true; + if (node instanceof U.AST_LoopControl && this.loopcontrol_target(node) === loop) { + return found = true; + } + }); + if (label instanceof U.AST_LabeledStatement) tw.push(label); + tw.push(loop); + body.walk(tw); + return found; +} + +function can_hoist(body, scope) { + var found = false; + var tw = new U.TreeWalker(function(node) { + if (found) return true; + if (node instanceof U.AST_Exit) return found = true; + if (node instanceof U.AST_NewTarget) return found = true; + if (node instanceof U.AST_Scope) { + if (node === body) return; + if (node instanceof U.AST_Arrow || node instanceof U.AST_AsyncArrow) node.argnames.forEach(function(sym) { + sym.walk(tw); + }); + // don't descend into nested functions + return true; + } + if (node instanceof U.AST_Super) return found = true; + if (node instanceof U.AST_SymbolDeclaration || node instanceof U.AST_SymbolRef) switch (node.name) { + case "await": + if (/^Async/.test(scope.TYPE)) return found = true; + return; + case "yield": + if (/Generator/.test(scope.TYPE)) return found = true; + return; + } + }); + body.walk(tw); + return !found; +} + +function is_timed_out(result) { + return sandbox.is_error(result) && /timed out/.test(result.message); +} + +function merge_sequence(array, node) { + if (node instanceof U.AST_Sequence) { + array.push.apply(array, node.expressions); + } else { + array.push(node); + } + return array; +} + +function to_sequence(expressions) { + if (expressions.length == 0) return new U.AST_Number({value: 0, start: {}}); + if (expressions.length == 1) return expressions[0]; + return new U.AST_Sequence({ + expressions: expressions.reduce(merge_sequence, []), + start: {}, + }); +} + +function to_statement(node) { + return U.is_statement(node) ? node : new U.AST_SimpleStatement({ + body: node, + start: {}, + }); +} + +function to_statement_init(node) { + return node instanceof U.AST_Const || node instanceof U.AST_Let ? new U.AST_BlockStatement({ + body: [ node ], + start: {}, + }) : to_statement(node); +} + +function wrap_with_console_log(node) { + // wrap with console.log() + return new U.AST_Call({ + expression: new U.AST_Dot({ + expression: new U.AST_SymbolRef({ + name: "console", + start: {}, + }), + property: "log", + start: {}, + }), + args: [ node ], + start: {}, + }); +} + +function run_code(code, toplevel, result_cache, timeout) { + var key = crypto.createHash("sha1").update(code).digest("base64"); + var value = result_cache[key]; + if (!value) { + var start = Date.now(); + result_cache[key] = value = { + result: sandbox.run_code(code, toplevel, timeout), + elapsed: Date.now() - start, + }; + } + return value; +} + +function compare_run_code(code, minify_options, result_cache, max_timeout) { + var minified = U.minify(code, minify_options); + if (minified.error) return minified; + + var toplevel = sandbox.has_toplevel(minify_options); + var unminified = run(code, max_timeout); + var timeout = Math.min(100 * unminified.elapsed, max_timeout); + var minified_result = run(minified.code, timeout).result; + + if (sandbox.same_stdout(unminified.result, minified_result)) { + return is_timed_out(unminified.result) && is_timed_out(minified_result) && { + timed_out: true, + }; + } + return { + unminified_result: unminified.result, + minified_result: minified_result, + elapsed: unminified.elapsed, + }; + + function run(code, timeout) { + return run_code(sandbox.patch_module_statements(code, minify_options.module), toplevel, result_cache, timeout); + } +} + +function test_minify(code, minify_options) { + var minified = U.minify(code, minify_options); + return minified.error && { + minified_result: minified.error, + }; +} diff --git a/test/release/acorn.sh b/test/release/acorn.sh new file mode 100755 index 00000000000..2cfe6f706cb --- /dev/null +++ b/test/release/acorn.sh @@ -0,0 +1,105 @@ +#!/bin/sh + +alias uglify-js=$PWD/bin/uglifyjs +UGLIFY_OPTIONS=$@ + +minify_in_situ() { + ARGS="$UGLIFY_OPTIONS --validate --in-situ" + DIRS="$1" + echo '> uglify-js' $DIRS $UGLIFY_OPTIONS + for i in `find $DIRS -type f -name '*.js'` + do + ARGS="$ARGS $i" + done + uglify-js $ARGS +} + +npm_install() { + PKG="$1" + while !(npm install $PKG); do + while !(npm cache clean --force); do echo "'npm cache clean' failed - retrying..."; done + done +} + +rm -rf tmp/acorn \ +&& git clone https://github.com/acornjs/acorn.git tmp/acorn \ +&& cd tmp/acorn \ +&& rm -rf .git/hooks \ +&& git checkout 74b59384320ced82e09da2e8fdbed16810f7379a \ +&& patch -l -p1 < uglify-js' $DIRS $UGLIFY_OPTIONS + for i in `find $DIRS -type f -name '*.js'` + do + ARGS="$ARGS $i" + done + uglify-js $ARGS +} + +npm_install() { + PKG="$1" + while !(npm install $PKG); do + while !(npm cache clean --force); do echo "'npm cache clean' failed - retrying..."; done + done +} + +rm -rf tmp/bootstrap \ +&& git clone --depth 1 --branch v5.0.0-beta2 https://github.com/twbs/bootstrap.git tmp/bootstrap \ +&& cd tmp/bootstrap \ +&& rm -rf .git/hooks \ +&& patch -p1 < uglify-js' $DIRS $UGLIFY_OPTIONS + for i in `find $DIRS -type f -name '*.js'` + do + ARGS="$ARGS $i" + done + uglify-js $ARGS +} + +npm_install() { + PKG="$1" + while !(npm install $PKG); do + while !(npm cache clean --force); do echo "'npm cache clean' failed - retrying..."; done + done +} + +rm -rf tmp/buble \ +&& git clone https://github.com/bublejs/buble.git tmp/buble \ +&& cd tmp/buble \ +&& rm -rf .git/hooks \ +&& git checkout dcc5ab02c9af6ddaad94e587c4911677340ec100 \ +&& patch -l -p1 < uglify-js' $DIRS $UGLIFY_OPTIONS + for i in `find $DIRS -type f -name '*.js'` + do + ARGS="$ARGS $i" + done + uglify-js $ARGS +} + +npm_install() { + PKG="$1" + while !(npm install $PKG); do + while !(npm cache clean --force); do echo "'npm cache clean' failed - retrying..."; done + done +} + +rm -rf tmp/butternut \ +&& git clone https://github.com/Rich-Harris/butternut.git tmp/butternut \ +&& cd tmp/butternut \ +&& rm -rf .git/hooks \ +&& patch -l -p1 < /dev/null; then NATIVE=1; fi +timeout() { + T=$1 + shift + shift + shift + expect < uglify-js' $DIRS $UGLIFY_OPTIONS + for i in `find $DIRS -type f -name '*.js'` + do + ARGS="$ARGS $i" + done + for i in `find $DIRS -type f -name '*.mjs'` + do + ARGS="$ARGS $i" + done + uglify-js $ARGS +} + +npm_install() { + PKG="$1" + while !(npm install $PKG); do + while !(npm cache clean --force); do echo "'npm cache clean' failed - retrying..."; done + done +} + +rm -rf tmp/mathjs \ +&& git clone --depth 1 --branch v9.2.0 https://github.com/josdejong/mathjs.git tmp/mathjs \ +&& cd tmp/mathjs \ +&& rm -rf .git/hooks \ +&& patch -l -p1 < uglify-js' $DIRS $UGLIFY_OPTIONS + for i in `find $DIRS -type f -name '*.js'` + do + ARGS="$ARGS $i" + done + uglify-js $ARGS +} + +npm_install() { + PKG="$1" + while !(npm install $PKG); do + while !(npm cache clean --force); do echo "'npm cache clean' failed - retrying..."; done + done +} + +rm -rf tmp/rollup \ +&& git clone https://github.com/rollup/rollup.git tmp/rollup \ +&& cd tmp/rollup \ +&& rm -rf .git/hooks \ +&& git checkout 3d80c06f895eab41e648ee99786fa68c72458b80 \ +&& patch -l -p1 < esbuild' $DIRS + for i in `find $DIRS -type f -name '*.ts' | grep -v '\.d\.ts'` + do + echo "$i" + CODE=`cat "$i"` + node_modules/.bin/esbuild --loader=ts --target=esnext > "$i" < uglify-js' $DIRS $UGLIFY_OPTIONS + for i in `find $DIRS -type f -name '*.js'` + do + ARGS="$ARGS $i" + done + uglify-js $ARGS +} + +npm_install() { + PKG="$1" + while !(npm install $PKG); do + while !(npm cache clean --force); do echo "'npm cache clean' failed - retrying..."; done + done +} + +rm -rf tmp/rollup \ +&& git clone --depth 1 --branch v2.39.1 https://github.com/rollup/rollup.git tmp/rollup \ +&& cd tmp/rollup \ +&& rm -rf .git/hooks \ +&& patch -l -p1 < " + args.join(" ") + "\u001B[39m"); + child_process.spawn(process.argv[0], args, { + stdio: [ "ignore", 1, 2 ] + }).on("exit", function(code) { + if (code) process.exit(code); + next(); + }); + })(); +}; diff --git a/test/release/sucrase.sh b/test/release/sucrase.sh new file mode 100755 index 00000000000..07b0aa8064a --- /dev/null +++ b/test/release/sucrase.sh @@ -0,0 +1,101 @@ +#!/bin/sh + +alias uglify-js=$PWD/bin/uglifyjs +UGLIFY_OPTIONS=$@ + +minify_in_situ() { + ARGS="$UGLIFY_OPTIONS --validate --in-situ" + DIRS="$1" + echo '> esbuild' $DIRS + for i in `find $DIRS -type f -name '*.ts' | grep -v '\.d\.ts'` + do + echo "$i" + CODE=`cat "$i"` + node_modules/.bin/esbuild --loader=ts --target=esnext > "$i" < uglify-js' $DIRS $UGLIFY_OPTIONS + for i in `find $DIRS -type f -name '*.js'` + do + ARGS="$ARGS $i" + done + for i in `find $DIRS -type f -name '*.mjs'` + do + ARGS="$ARGS $i" + done + uglify-js $ARGS +} + +npm_install() { + PKG="$1" + while !(npm install $PKG); do + while !(npm cache clean --force); do echo "'npm cache clean' failed - retrying..."; done + done +} + +rm -rf tmp/sucrase \ +&& git clone https://github.com/alangpierce/sucrase.git tmp/sucrase \ +&& cd tmp/sucrase \ +&& rm -rf .git/hooks \ +&& git checkout 38b66f3009feb76750a799deea211adcc83574f1 \ +&& patch -l -p1 < { +- () => buildBenchmark(), +@@ -18,5 +16,0 @@ async function main(): Promise { +- () => buildIntegration("./integrations/gulp-plugin"), +- () => buildIntegration("./integrations/jest-plugin"), +- () => buildIntegration("./integrations/webpack-loader"), +- () => buildIntegration("./integrations/webpack-object-rest-spread-plugin"), +- () => buildWebsite(), +@@ -66,3 +59,0 @@ async function buildSucrase(): Promise { +- // Also add in .d.ts files from tsc, which only need to be compiled once. +- await run(\`\${TSC} --project ./src --outDir ./dist-types\`); +- await mergeDirectoryContents("./dist-types/src", "./dist"); +@@ -70 +61 @@ async function buildSucrase(): Promise { +- await run("yarn link"); ++ await run("npm link"); +--- a/src/identifyShadowedGlobals.ts ++++ b/src/identifyShadowedGlobals.ts +@@ -23,0 +24 @@ export default function identifyShadowedGlobals( ++export { identifyShadowedGlobals as HACK }; +--- a/src/parser/tokenizer/state.ts ++++ b/src/parser/tokenizer/state.ts +@@ -100,0 +101 @@ export default class State { ++export { State as HACK }; +--- a/src/transformers/JSXTransformer.ts ++++ b/src/transformers/JSXTransformer.ts +@@ -253,0 +254 @@ export default class JSXTransformer extends Transformer { ++export { JSXTransformer as HACK }; +--- a/src/util/getClassInfo.ts ++++ b/src/util/getClassInfo.ts +@@ -164,0 +165 @@ export default function getClassInfo( ++export { getClassInfo as HACK }; +--- a/src/util/getDeclarationInfo.ts ++++ b/src/util/getDeclarationInfo.ts +@@ -40,0 +41 @@ export default function getDeclarationInfo(tokens: TokenProcessor): DeclarationI ++export { getDeclarationInfo as HACK }; +--- a/src/util/getJSXPragmaInfo.ts ++++ b/src/util/getJSXPragmaInfo.ts +@@ -14,0 +15 @@ export default function getJSXPragmaInfo(options: Options): JSXPragmaInfo { ++export { getJSXPragmaInfo as HACK }; +EOF +ERR=$?; if [ "$ERR" != "0" ]; then echo "Error: $ERR"; exit $ERR; fi +npm_install esbuild-wasm@0.8.56 \ +&& minify_in_situ "src" \ +&& rm -rf node_modules \ +&& npm_install \ +&& npm run clean \ +&& npm run build \ +&& minify_in_situ "dist" \ +&& minify_in_situ "dist-self-build" \ +&& npm run test-only diff --git a/test/release/web-tooling-benchmark.sh b/test/release/web-tooling-benchmark.sh new file mode 100755 index 00000000000..8b38abd4f4a --- /dev/null +++ b/test/release/web-tooling-benchmark.sh @@ -0,0 +1,65 @@ +#!/bin/sh + +alias uglify-js="node --max-old-space-size=8192 $PWD/bin/uglifyjs" +UGLIFY_OPTIONS=$@ + +minify_in_situ() { + ARGS="$UGLIFY_OPTIONS --validate --in-situ" + DIRS="$1" + echo '> uglify-js' $DIRS $UGLIFY_OPTIONS + for i in `find $DIRS -type f -name '*.js'` + do + ARGS="$ARGS $i" + done + uglify-js $ARGS +} + +npm_install() { + PKG="$1" + while !(npm install $PKG); do + while !(npm cache clean --force); do echo "'npm cache clean' failed - retrying..."; done + done +} + +rm -rf tmp/web-tooling-benchmark \ +&& git clone --depth 1 --branch v0.5.3 https://github.com/v8/web-tooling-benchmark.git tmp/web-tooling-benchmark \ +&& cd tmp/web-tooling-benchmark \ +&& rm -rf .git/hooks \ +&& patch -l -p1 < { ++ global.process.exitCode = 42; +EOF +ERR=$?; if [ "$ERR" != "0" ]; then echo "Error: $ERR"; exit $ERR; fi +minify_in_situ "src" \ +&& minify_in_situ "third_party" \ +&& rm -rf node_modules \ +&& npm_install --package-lock \ +&& rm -rf build/* \ +&& npm run build:terser-bundled \ +&& npm run build:uglify-js-bundled \ +&& minify_in_situ "build" \ +&& rm -rf dist \ +&& npm run build \ +&& minify_in_situ "dist" \ +&& node dist/cli.js diff --git a/test/run-tests.js b/test/run-tests.js deleted file mode 100755 index 93c79ce9bb2..00000000000 --- a/test/run-tests.js +++ /dev/null @@ -1,232 +0,0 @@ -#! /usr/bin/env node - -var U = require("../tools/node"); -var path = require("path"); -var fs = require("fs"); -var assert = require("assert"); -var sys = require("util"); - -var tests_dir = path.dirname(module.filename); -var failures = 0; -var failed_files = {}; - -run_compress_tests(); -if (failures) { - sys.error("\n!!! Failed " + failures + " test cases."); - sys.error("!!! " + Object.keys(failed_files).join(", ")); - process.exit(1); -} - -var run_ast_conversion_tests = require("./mozilla-ast"); - -run_ast_conversion_tests({ - iterations: 1000 -}); - -run_feature_extraction_tests(); - -/* -----[ utils ]----- */ - -function tmpl() { - return U.string_template.apply(this, arguments); -} - -function log() { - var txt = tmpl.apply(this, arguments); - sys.puts(txt); -} - -function log_directory(dir) { - log("*** Entering [{dir}]", { dir: dir }); -} - -function log_start_file(file) { - log("--- {file}", { file: file }); -} - -function log_test(name) { - log(" Running test [{name}]", { name: name }); -} - -function find_test_files(dir) { - var files = fs.readdirSync(dir).filter(function(name){ - return /\.js$/i.test(name); - }); - if (process.argv.length > 2) { - var x = process.argv.slice(2); - files = files.filter(function(f){ - return x.indexOf(f) >= 0; - }); - } - return files; -} - -function test_directory(dir) { - return path.resolve(tests_dir, dir); -} - -function as_toplevel(input) { - if (input instanceof U.AST_BlockStatement) input = input.body; - else if (input instanceof U.AST_Statement) input = [ input ]; - else throw new Error("Unsupported input syntax"); - var toplevel = new U.AST_Toplevel({ body: input }); - toplevel.figure_out_scope(); - return toplevel; -} - -function run_compress_tests() { - var dir = test_directory("compress"); - log_directory("compress"); - var files = find_test_files(dir); - function test_file(file) { - log_start_file(file); - function test_case(test) { - log_test(test.name); - var options = U.defaults(test.options, { - warnings: false - }); - var cmp = new U.Compressor(options, true); - var expect = make_code(as_toplevel(test.expect), false); - var input = as_toplevel(test.input); - var input_code = make_code(test.input); - var output = input.transform(cmp); - output.figure_out_scope(); - output = make_code(output, false); - if (expect != output) { - log("!!! failed\n---INPUT---\n{input}\n---OUTPUT---\n{output}\n---EXPECTED---\n{expected}\n\n", { - input: input_code, - output: output, - expected: expect - }); - failures++; - failed_files[file] = 1; - } - } - var tests = parse_test(path.resolve(dir, file)); - for (var i in tests) if (tests.hasOwnProperty(i)) { - test_case(tests[i]); - } - } - files.forEach(function(file){ - test_file(file); - }); -} - - -function removeWhitespace(input){ - return input.replace(/\s/g,""); -} - -function run_feature_extraction_tests() { - var dir = test_directory("feature_extraction"); - log_directory("feature_extraction"); - var files = find_test_files(dir); - function test_file(file) { - log_start_file(file); - function test_case(test) { - log_test(test.name); - - var features = "FNAMES,ASTREL"; - if (test.options.hasOwnProperty("features")) { - features = test.options.features; - }; - - var expect = test.expect.body.value; - - var input_code = make_code(test.input); - var output = U.extractFeatures(input_code, test.name, false, features); - - if (removeWhitespace(expect) != removeWhitespace(output)) { - log("!!! failed\n---INPUT---\n{input}\n---OUTPUT---\n{output}\n---EXPECTED---\n{expected}\n\n", { - input: input_code, - output: output, - expected: expect - }); - failures++; - failed_files[file] = 1; - } - } - var tests = parse_test(path.resolve(dir, file)); - for (var i in tests) if (tests.hasOwnProperty(i)) { - test_case(tests[i]); - } - } - files.forEach(function(file){ - test_file(file); - }); -} - - -function parse_test(file) { - var script = fs.readFileSync(file, "utf8"); - var ast = U.parse(script, { - filename: file - }); - var tests = {}; - var tw = new U.TreeWalker(function(node, descend){ - if (node instanceof U.AST_LabeledStatement - && tw.parent() instanceof U.AST_Toplevel) { - var name = node.label.name; - tests[name] = get_one_test(name, node.body); - return true; - } - if (!(node instanceof U.AST_Toplevel)) croak(node); - }); - ast.walk(tw); - return tests; - - function croak(node) { - throw new Error(tmpl("Can't understand test file {file} [{line},{col}]\n{code}", { - file: file, - line: node.start.line, - col: node.start.col, - code: make_code(node, false) - })); - } - - function get_one_test(name, block) { - var test = { name: name, options: {} }; - var tw = new U.TreeWalker(function(node, descend){ - if (node instanceof U.AST_Assign) { - if (!(node.left instanceof U.AST_SymbolRef)) { - croak(node); - } - var name = node.left.name; - test[name] = evaluate(node.right); - return true; - } - if (node instanceof U.AST_LabeledStatement) { - assert.ok( - node.label.name == "input" || node.label.name == "expect", - tmpl("Unsupported label {name} [{line},{col}]", { - name: node.label.name, - line: node.label.start.line, - col: node.label.start.col - }) - ); - var stat = node.body; - if (stat instanceof U.AST_BlockStatement) { - if (stat.body.length == 1) stat = stat.body[0]; - else if (stat.body.length == 0) stat = new U.AST_EmptyStatement(); - } - test[node.label.name] = stat; - return true; - } - }); - block.walk(tw); - return test; - }; -} - -function make_code(ast, beautify) { - if (arguments.length == 1) beautify = true; - var stream = U.OutputStream({ beautify: beautify }); - ast.print(stream); - return stream.get(); -} - -function evaluate(code) { - if (code instanceof U.AST_Node) - code = make_code(code); - return new Function("return(" + code + ")")(); -} diff --git a/test/sandbox.js b/test/sandbox.js new file mode 100644 index 00000000000..71d1e09fff3 --- /dev/null +++ b/test/sandbox.js @@ -0,0 +1,330 @@ +var readFileSync = require("fs").readFileSync; +var semver = require("semver"); +var spawnSync = require("child_process").spawnSync; +var vm = require("vm"); + +setup_log(); +var setup_code = "(" + setup + ")(" + [ + "this", + find_builtins(), + setup_log, + "function(process) {" + readFileSync(require.resolve("../tools/tty", "utf8")) + "}", +].join(",\n") + ");\n"; +exports.has_toplevel = function(options) { + return options.toplevel + || options.mangle && options.mangle.toplevel + || options.compress && options.compress.toplevel; +}; +exports.is_error = is_error; +exports.run_code = semver.satisfies(process.version, "0.8") ? function(code, toplevel, timeout) { + var stdout = run_code_vm(code, toplevel, timeout); + if (typeof stdout != "string" || !/arguments/.test(code)) return stdout; + do { + var prev = stdout; + stdout = run_code_vm(code, toplevel, timeout); + } while (prev !== stdout); + return stdout; +} : semver.satisfies(process.version, "<0.12") ? run_code_vm : function(code, toplevel, timeout) { + var stdout = ([ + /\b(async[ \t]+function|Promise|setImmediate|setInterval|setTimeout)\b/, + /\basync([ \t]+|[ \t]*#)[^\s()[\]{}#:;,.&|!~=*%/+-]+(\s*\(|[ \t]*=>)/, + /\basync[ \t]*\*[ \t]*[^\s()[\]{}#:;,.&|!~=*%/+-]+\s*\(/, + /\basync([ \t]*\*)?[ \t]*\[[\s\S]*?\]\s*\(/, + /\basync[ \t]*\([\s\S]*?\)[ \t]*=>/, + ].some(function(pattern) { + return pattern.test(code); + }) ? run_code_exec : run_code_vm)(code, toplevel, timeout); + var len = typeof stdout == "string" && stdout.length; + return len > 1000 ? stdout.slice(0, 1000) + "…《" + len + "》" : stdout; +}; +exports.same_stdout = semver.satisfies(process.version, "0.12") ? function(expected, actual) { + if (typeof expected != typeof actual) return false; + if (is_error(expected)) { + if (expected.name !== actual.name) return false; + if (typeof actual.message != "string") return false; + expected = expected.message.slice(expected.message.lastIndexOf("\n") + 1); + actual = actual.message.slice(actual.message.lastIndexOf("\n") + 1); + } + return strip_func_ids(expected) == strip_func_ids(actual); +} : function(expected, actual) { + return typeof expected == typeof actual && strip_func_ids(expected) == strip_func_ids(actual); +}; +exports.patch_module_statements = function(code, module) { + if (module || module === undefined && /\bawait\b/.test(code)) { + code = [ + "(async()=>{", + code, + '})().catch(e=>process.on("exit",()=>{throw e}));', + ]; + if (module) code.unshift('"use strict";'); + code = code.join("\n"); + } + var count = 0, has_default = "", imports = [], strict_mode = ""; + code = code.replace(/^\s*("|')use strict\1\s*;?/, function(match) { + strict_mode = match; + return ""; + }).replace(/\bexport(?:\s*\{[^{}]*}\s*?(?:$|\n|;)|\s+default\b(?:\s*(\(|\{|class\s*\{|class\s+(?=extends\b)|(?:async\s+)?function\s*(?:\*\s*)?\())?|\b)/g, function(match, header) { + if (/^export\s+default/.test(match)) has_default = "function _uglify_export_default_() {}"; + if (!header) return ""; + if (header.length == 1) return "0, " + header; + var name = "_uglify_export_default_"; + if (/^class\b/.test(header)) do { + name = "_uglify_export_default_" + ++count; + } while (code.indexOf(name) >= 0); + return header.slice(0, -1) + " " + name + header.slice(-1); + }).replace(/\bimport\.meta\b/g, function() { + return '({ url: "https://example.com/path/index.html" })'; + }).replace(/\bimport\b(?:\s*([^\s('"][^('"]*)\bfrom\b)?\s*(['"]).*?\2(?:$|\n|;)/g, function(match, symbols) { + if (symbols) { + if (!/^[{*]/.test(symbols)) symbols = "default:" + symbols; + symbols = symbols.replace(/[{}]/g, "").trim().replace(/\s*,\s*/g, ","); + symbols = symbols.replace(/\*/, '"*"').replace(/\bas\s+(?!$|,|as\s)/g, ":"); + imports.push([ + "const {", + symbols, + "} = new Proxy(Object.create(null), { get(_, value) { return { value }; } });", + ].join("")); + } + return ""; + }); + imports.push(""); + return strict_mode + has_default + imports.join("\n") + code; +}; + +function is_error(result) { + return result && typeof result.name == "string" && typeof result.message == "string"; +} + +function strip_color_codes(value) { + return value.replace(/\u001b\[\d+m/g, ""); +} + +function strip_func_ids(text) { + return ("" + text).replace(/F[0-9]{6}N/g, "N>"); +} + +function setup_log() { + var inspect = require("util").inspect; + if (inspect.defaultOptions) { + var log_options = { + breakLength: Infinity, + colors: false, + compact: true, + customInspect: false, + depth: Infinity, + maxArrayLength: Infinity, + maxStringLength: Infinity, + showHidden: false, + }; + for (var name in log_options) { + if (name in inspect.defaultOptions) inspect.defaultOptions[name] = log_options[name]; + } + } + return inspect; +} + +function find_builtins() { + setup_code = "console.log(Object.keys(this));"; + var builtins = run_code_vm(""); + if (semver.satisfies(process.version, ">=0.12")) builtins += ".concat(" + run_code_exec("") + ")"; + return builtins; +} + +function setup(global, builtins, setup_log, setup_tty) { + [ Array, Boolean, Error, Function, Number, Object, RegExp, String ].forEach(function(f) { + f.toString = Function.prototype.toString; + }); + Function.prototype.toString = function() { + var configurable = Object.getOwnPropertyDescriptor(Function.prototype, "name").configurable; + var id = 100000; + return function() { + var n = this.name; + if (!/^F[0-9]{6}N$/.test(n)) { + n = "F" + ++id + "N"; + if (configurable) Object.defineProperty(this, "name", { + get: function() { + return n; + } + }); + } + return "function(){}"; + }; + }(); + var process = global.process; + if (process) { + setup_tty(process); + var inspect = setup_log(); + process.on("uncaughtException", function(ex) { + var value = ex; + if (value instanceof Error) { + value = {}; + for (var name in ex) { + value[name] = ex[name]; + delete ex[name]; + } + } + var marker = "\n\n-----===== UNCAUGHT EXCEPTION =====-----\n\n"; + process.stderr.write(marker + inspect(value) + marker); + throw ex; + }).on("unhandledRejection", function() {}); + } + var log = console.log; + var safe_console = { + log: function(msg) { + if (arguments.length == 1 && typeof msg == "string") return log("%s", msg); + return log.apply(null, [].map.call(arguments, function(arg) { + return safe_log(arg, { + level: 5, + original: [], + replaced: [], + }); + })); + }, + }; + var props = { + // for Node.js v8 + console: { + get: function() { + return safe_console; + }, + }, + global: { get: self }, + self: { get: self }, + window: { get: self }, + }; + [ + // for Node.js v0.12 + "Buffer", + "clearImmediate", + "clearInterval", + "clearTimeout", + // for Node.js v0.12 + "DTRACE_NET_STREAM_END", + // for Node.js v8 + "process", + "setImmediate", + "setInterval", + "setTimeout", + ].forEach(function(name) { + var value = global[name]; + props[name] = { + get: function() { + return value; + }, + }; + }); + builtins.forEach(function(name) { + try { + delete global[name]; + } catch (e) {} + }); + Object.defineProperties(global, props); + // for Node.js v8+ + global.__proto__ = Object.defineProperty(Object.create(global.__proto__), "toString", { + value: function() { + return "[object global]"; + }, + }); + + function self() { + return this; + } + + function safe_log(arg, cache) { + if (arg) switch (typeof arg) { + case "function": + return arg.toString(); + case "object": + if (arg === global) return "[object global]"; + if (/Error$/.test(arg.name)) return arg.toString(); + if (typeof arg.then == "function") return "[object Promise]"; + if (arg.constructor) arg.constructor.toString(); + var index = cache.original.indexOf(arg); + if (index >= 0) return cache.replaced[index]; + if (--cache.level < 0) return "[object Object]"; + var value = {}; + cache.original.push(arg); + cache.replaced.push(value); + for (var key in arg) { + var desc = Object.getOwnPropertyDescriptor(arg, key); + if (desc && (desc.get || desc.set)) { + Object.defineProperty(value, key, desc); + } else { + value[key] = safe_log(arg[key], cache); + } + } + return value; + } + return arg; + } +} + +function run_code_vm(code, toplevel, timeout) { + timeout = timeout || 5000; + var stdout = ""; + var original_write = process.stdout.write; + process.stdout.write = function(chunk) { + stdout += chunk; + }; + try { + var ctx = vm.createContext({ console: console }); + // for Node.js v6 + vm.runInContext(setup_code, ctx); + vm.runInContext(toplevel ? "(function(){\n" + code + "\n})();" : code, ctx, { timeout: timeout }); + // for Node.js v4 + return strip_color_codes(stdout.replace(/\b(Array \[|Object {)/g, function(match) { + return match.slice(-1); + })); + } catch (ex) { + return ex; + } finally { + process.stdout.write = original_write; + } +} + +function run_code_exec(code, toplevel, timeout) { + if (toplevel) { + code = setup_code + "(function(){\n" + code + "\n})();"; + } else { + code = code.replace(/^((["'])[^"']*\2(;|$))?/, function(directive) { + return directive + setup_code; + }); + } + var result = spawnSync(process.argv[0], [ '--max-old-space-size=2048' ], { + encoding: "utf8", + input: code, + maxBuffer: 1073741824, + stdio: "pipe", + timeout: timeout || 5000, + }); + if (result.status === 0) return result.stdout; + var msg = ("" + result.stderr).replace(/\r\n/g, "\n"); + if (result.error && result.error.code == "ETIMEDOUT" || /FATAL ERROR:/.test(msg)) { + return new Error("Script execution timed out."); + } + if (result.error) return result.error; + var match = /\n([^:\s]*Error)(?:: ([\s\S]+?))?\n( at [\s\S]+)\n$/.exec(msg); + var marker = "\n\n-----===== UNCAUGHT EXCEPTION =====-----\n\n"; + var start = msg.indexOf(marker) + marker.length; + var end = msg.indexOf(marker, start); + var details; + if (end >= 0) { + details = msg.slice(start, end).replace(/<([1-9][0-9]*) empty items?>/g, function(match, count) { + return new Array(+count).join(); + }); + try { + details = vm.runInNewContext("(" + details + ")"); + } catch (e) {} + } else if (!match) { + return new Error("Script execution aborted."); + } + if (!match) return details; + var ex = new global[match[1]](match[2]); + ex.stack = ex.stack.slice(0, ex.stack.indexOf(" at ")) + match[3]; + if (typeof details == "object") { + for (var name in details) ex[name] = details[name]; + } else if (end >= 0) { + ex.details = details; + } + return ex; +} diff --git a/test/ufuzz/actions.js b/test/ufuzz/actions.js new file mode 100644 index 00000000000..30415069e32 --- /dev/null +++ b/test/ufuzz/actions.js @@ -0,0 +1,82 @@ +var get = require("https").get; +var parse = require("url").parse; + +var base, token, run_number; +var expires = Date.now() + (6 * 60 - 10) * 60 * 1000; +exports.init = function(url, auth, num) { + base = url; + token = auth; + run_number = num; +}; +exports.should_stop = function(callback) { + if (Date.now() > expires) return callback(); + read(base + "/actions/runs?per_page=100", function(reply) { + var runs = verify(reply, "workflow_runs").filter(function(workflow) { + return workflow.status != "completed"; + }).sort(function(a, b) { + return b.run_number - a.run_number; + }); + var found = false, remaining = 20; + (function next() { + var workflow; + do { + workflow = runs.pop(); + if (!workflow) return; + if (!is_cron(workflow)) break; + if (workflow.run_number == run_number) found = true; + } while (!found); + read(workflow.jobs_url, function(reply) { + verify(reply, "jobs").forEach(function(job) { + if (job.status != "completed") remaining--; + }); + if (remaining >= 0) { + next(); + } else { + callback(); + } + }); + })(); + }); +}; + +function is_cron(workflow) { + return /^(schedule|workflow_dispatch|workflow_run)$/.test(workflow.event); +} + +function read(url, callback) { + var done = function(reply) { + done = function() {}; + callback(reply); + }; + var options = parse(url); + options.headers = { + "Authorization": "Token " + token, + "User-Agent": "UglifyJS", + }; + get(options, function(response) { + var chunks = []; + response.setEncoding("utf8"); + response.on("data", function(chunk) { + chunks.push(chunk); + }).on("end", function() { + var reply; + try { + reply = JSON.parse(chunks.join("")); + } catch (e) {} + done(reply); + }).on("error", function() { + done(); + }); + }).on("error", function() { + done(); + }); +} + +function verify(reply, field) { + if (!reply) return []; + var values = reply[field]; + if (!Array.isArray(values)) return []; + return values.filter(function(value) { + return value; + }); +} diff --git a/test/ufuzz/index.js b/test/ufuzz/index.js new file mode 100644 index 00000000000..eb38de375cd --- /dev/null +++ b/test/ufuzz/index.js @@ -0,0 +1,2650 @@ +// derived from https://github.com/qfox/uglyfuzzer by Peter van der Zee +"use strict"; + +// check both CLI and file modes of nodejs (!). See #1695 for details. and the various settings of uglify. +// bin/uglifyjs s.js -c && bin/uglifyjs s.js -c passes=3 && bin/uglifyjs s.js -c passes=3 -m +// cat s.js | node && node s.js && bin/uglifyjs s.js -c | node && bin/uglifyjs s.js -c passes=3 | node && bin/uglifyjs s.js -c passes=3 -m | node + +require("../../tools/tty"); + +var UglifyJS = require("../.."); +var randomBytes = require("crypto").randomBytes; +var sandbox = require("../sandbox"); +var reduce_test = require("../reduce"); + +var MAX_GENERATED_TOPLEVELS_PER_RUN = 1; +var MAX_GENERATION_RECURSION_DEPTH = 12; +var INTERVAL_COUNT = 100; + +var STMT_ARG_TO_ID = Object.create(null); +var STMTS_TO_USE = []; +function STMT_(name) { + return STMT_ARG_TO_ID[name] = STMTS_TO_USE.push(STMTS_TO_USE.length) - 1; +} + +var STMT_BLOCK = STMT_("block"); +var STMT_IF_ELSE = STMT_("ifelse"); +var STMT_DO_WHILE = STMT_("dowhile"); +var STMT_WHILE = STMT_("while"); +var STMT_FOR_LOOP = STMT_("forloop"); +var STMT_FOR_ENUM = STMT_("forenum"); +var STMT_SEMI = STMT_("semi"); +var STMT_EXPR = STMT_("expr"); +var STMT_SWITCH = STMT_("switch"); +var STMT_VAR = STMT_("var"); +var STMT_RETURN_ETC = STMT_("stop"); +var STMT_FUNC_EXPR = STMT_("funcexpr"); +var STMT_TRY = STMT_("try"); +var STMT_C = STMT_("c"); + +var STMT_FIRST_LEVEL_OVERRIDE = -1; +var STMT_SECOND_LEVEL_OVERRIDE = -1; +var STMT_COUNT_FROM_GLOBAL = true; // count statement depth from nearest function scope or just global scope? + +var num_iterations = +process.argv[2] || 1/0; +var verbose = false; // log every generated test +var verbose_interval = false; // log every 100 generated tests +var use_strict = false; +var catch_redef = require.main === module; +var generate_directive = require.main === module; +for (var i = 2; i < process.argv.length; ++i) { + switch (process.argv[i]) { + case "-v": + verbose = true; + break; + case "-V": + verbose_interval = true; + break; + case "-t": + MAX_GENERATED_TOPLEVELS_PER_RUN = +process.argv[++i]; + if (!MAX_GENERATED_TOPLEVELS_PER_RUN) throw new Error("Must generate at least one toplevel per run"); + break; + case "-r": + MAX_GENERATION_RECURSION_DEPTH = +process.argv[++i]; + if (!MAX_GENERATION_RECURSION_DEPTH) throw new Error("Recursion depth must be at least 1"); + break; + case "-s1": + var name = process.argv[++i]; + STMT_FIRST_LEVEL_OVERRIDE = STMT_ARG_TO_ID[name]; + if (!(STMT_FIRST_LEVEL_OVERRIDE >= 0)) throw new Error("Unknown statement name; use -? to get a list"); + break; + case "-s2": + var name = process.argv[++i]; + STMT_SECOND_LEVEL_OVERRIDE = STMT_ARG_TO_ID[name]; + if (!(STMT_SECOND_LEVEL_OVERRIDE >= 0)) throw new Error("Unknown statement name; use -? to get a list"); + break; + case "--no-catch-redef": + catch_redef = false; + break; + case "--no-directive": + generate_directive = false; + break; + case "--use-strict": + use_strict = true; + break; + case "--stmt-depth-from-func": + STMT_COUNT_FROM_GLOBAL = false; + break; + case "--only-stmt": + STMTS_TO_USE = process.argv[++i].split(",").map(function(name) { + return STMT_ARG_TO_ID[name]; + }); + break; + case "--without-stmt": + // meh. it runs once it's fine. + process.argv[++i].split(",").forEach(function(name) { + var omit = STMT_ARG_TO_ID[name]; + STMTS_TO_USE = STMTS_TO_USE.filter(function(id) { + return id !== omit; + }); + }); + break; + case "--help": + case "-h": + case "-?": + println("** UglifyJS fuzzer help **"); + println("Valid options (optional):"); + println(": generate this many cases (if used must be first arg)"); + println("-v: print every generated test case"); + println("-V: print every 100th generated test case"); + println("-t : generate this many toplevels per run (more take longer)"); + println("-r : maximum recursion depth for generator (higher takes longer)"); + println("-s1 : force the first level statement to be this one (see list below)"); + println("-s2 : force the second level statement to be this one (see list below)"); + println("--no-catch-redef: do not redefine catch variables"); + println("--no-directive: do not generate directives"); + println('--use-strict: generate "use strict"'); + println("--stmt-depth-from-func: reset statement depth counter at each function, counts from global otherwise"); + println("--only-stmt : a comma delimited white list of statements that may be generated"); + println("--without-stmt : a comma delimited black list of statements never to generate"); + println("List of accepted statement names: " + Object.keys(STMT_ARG_TO_ID)); + println("** UglifyJS fuzzer exiting **"); + return 0; + default: + // first arg may be a number. + if (i > 2 || !parseInt(process.argv[i], 10)) throw new Error("Unknown argument[" + process.argv[i] + "]; see -h for help"); + } +} + +var SUPPORT = function(matrix) { + for (var name in matrix) { + matrix[name] = !sandbox.is_error(sandbox.run_code(matrix[name])); + } + return matrix; +}({ + arrow: "a => 0;", + async: "async function f(){}", + async_generator: "async function* f(){}", + bigint: "42n", + catch_omit_var: "try {} catch {}", + class: "class C { f() {} }", + class_field: "class C { p = 0; }", + class_private: "class C { #f() {} }", + class_static_init: "class C { static {} }", + computed_key: "({[0]: 0});", + const_block: "var a; { const a = 0; }", + default_value: "[ a = 0 ] = [];", + destructuring: "[] = [];", + exponentiation: "0 ** 0", + for_await_of: "async function f(a) { for await (a of []); }", + for_of: "for (var a of []);", + generator: "function* f(){}", + let: "let a;", + logical_assignment: "[].p ??= 0;", + new_target: "function f() { new.target; }", + nullish: "0 ?? 0", + optional_chaining: "0?.p", + rest: "var [...a] = [];", + rest_object: "var {...a} = {};", + spread: "[...[]];", + spread_object: "({...0});", + template: "``", + trailing_comma: "function f(a,) {}", +}); +if (SUPPORT.exponentiation && sandbox.run_code("console.log(10 ** 100 === Math.pow(10, 100));") !== "true\n") { + SUPPORT.exponentiation = false; +} + +var VALUES = [ + '"a"', + '"b"', + '"c"', + '""', + "true", + "false", + " /[a2][^e]+$/ ", + "(-1)", + "(-2)", + "(-3)", + "(-4)", + "(-5)", + "0", + "1", + "2", + "3", + "4", + "5", + "22", + "(-0)", // 0/-0 !== 0 + "23..toString()", + "24 .toString()", + "25. ", + "0x26.toString()", + "NaN", + "null", + "Infinity", + "undefined", + "[]", + "[,0][1]", // an array with elisions... but this is always false + "([,0].length === 2)", // an array with elisions... this is always true + "({})", // wrapped the object causes too many syntax errors in statements + '"foo"', + '"bar"', + '"undefined"', + '"object"', + '"number"', + '"function"', + "this", +]; +VALUES = VALUES.concat(VALUES); +VALUES = VALUES.concat(VALUES); +VALUES = VALUES.concat(VALUES); +if (SUPPORT.bigint) VALUES = VALUES.concat([ + "(!0o644n)", + "([3n][0] > 2)", + "(-42n).toString()", + "Number(0XDEADn << 16n | 0xbeefn)", +]); +VALUES = VALUES.concat(VALUES); +VALUES = VALUES.concat(VALUES); +VALUES = VALUES.concat(VALUES); +VALUES = VALUES.concat(VALUES); +VALUES.push("import.meta"); + +var BINARY_OPS = [ + " + ", // spaces needed to disambiguate with ++ cases (could otherwise cause syntax errors) + " - ", + "/", + "*", + "&", + "|", + "^", + "<", + "<=", + ">", + ">=", + "==", + "===", + "!=", + "!==", + "<<", + ">>", + ">>>", + "%", + "&&", + "||", + "^", + ",", +]; +BINARY_OPS = BINARY_OPS.concat(BINARY_OPS); +if (SUPPORT.nullish) BINARY_OPS.push("??"); +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.push(" in ", " instanceof "); + +var ASSIGNMENTS = [ "=" ]; +ASSIGNMENTS = ASSIGNMENTS.concat(ASSIGNMENTS); +ASSIGNMENTS.push("+="); +ASSIGNMENTS = ASSIGNMENTS.concat(ASSIGNMENTS); +ASSIGNMENTS = ASSIGNMENTS.concat(ASSIGNMENTS); +ASSIGNMENTS = ASSIGNMENTS.concat(ASSIGNMENTS); +ASSIGNMENTS = ASSIGNMENTS.concat([ + "-=", + "*=", + "/=", + "%=", + "&=", + "|=", + "^=", + "<<=", + ">>=", + ">>>=", +]); +ASSIGNMENTS = ASSIGNMENTS.concat(ASSIGNMENTS); +if (SUPPORT.exponentiation) ASSIGNMENTS.push("**="); +if (SUPPORT.logical_assignment) ASSIGNMENTS = ASSIGNMENTS.concat([ + "&&=", + "||=", + "??=", +]); + +var UNARY_SAFE = [ + "+", + "-", + "~", + "!", + "void ", + "delete ", +]; +var UNARY_POSTFIX = [ + "++", + "--", +]; +var UNARY_PREFIX = UNARY_POSTFIX.concat(UNARY_SAFE); + +var NO_COMMA = true; +var COMMA_OK = false; +var MAYBE = true; +var MANDATORY = false; +var CAN_THROW = true; +var CANNOT_THROW = false; +var CAN_BREAK = true; +var CANNOT_BREAK = false; +var CAN_CONTINUE = true; +var CANNOT_CONTINUE = false; +var CAN_RETURN = false; +var CANNOT_RETURN = true; +var NO_DEFUN = false; +var DEFUN_OK = true; +var DONT_STORE = true; +var NO_CONST = true; +var NO_DUPLICATE = true; +var NO_LAMBDA = true; +var NO_TEMPLATE = true; + +var VAR_NAMES = [ + "a", + "a", + "a", + "a", + "b", + "b", + "b", + "b", + "c", // prevent redeclaring this, avoid assigning to this + "foo", + "foo", + "bar", + "bar", + "undefined", + "NaN", + "Infinity", + "arguments", + "async", + "await", + "let", + "yield", +]; +var INITIAL_NAMES_LEN = VAR_NAMES.length; + +var TYPEOF_OUTCOMES = [ + "function", + "undefined", + "string", + "number", + "object", + "boolean", + "special", + "unknown", + "symbol", + "crap", +]; + +var avoid_vars = []; +var block_vars = [ "let" ]; +var lambda_vars = []; +var unique_vars = []; +var classes = []; +var allow_this = true; +var async = false; +var has_await = false; +var export_default = false; +var generator = false; +var loops = 0; +var funcs = 0; +var clazz = 0; +var imports = 0; +var in_class = 0; +var called = Object.create(null); +var labels = 10000; + +function rng(limit) { + var r = randomBytes(2).readUInt16LE(0) / 65536; + return Math.floor(limit * r); +} + +function get_num(max) { + if (rng(max + 1) == 0) return 0; + var i = 1; + while (i < max && rng(2)) i++; + return i; +} + +function strictMode() { + return use_strict && rng(4) == 0 ? '"use strict";' : ""; +} + +function appendExport(stmtDepth, allowDefault) { + if (SUPPORT.destructuring && stmtDepth == 1 && rng(20) == 0) { + if (allowDefault && !export_default && rng(5) == 0) { + export_default = true; + return "export default "; + } + return "export "; + } + return ""; +} + +function mayDefer(code) { + if (SUPPORT.arrow && rng(200) == 0) { + has_await = true; + return "void setImmediate(() => (" + code + "))"; + } + return code; +} + +function createTopLevelCode() { + VAR_NAMES.length = INITIAL_NAMES_LEN; // prune any previous names still in the list + block_vars.length = 1; + lambda_vars.length = 0; + unique_vars.length = 0; + classes.length = 0; + allow_this = true; + async = SUPPORT.async && rng(200) == 0; + has_await = false; + export_default = false; + generator = false; + loops = 0; + funcs = 0; + clazz = 0; + imports = 0; + in_class = async; + called = Object.create(null); + var s = [ + strictMode(), + appendExport(1) + "var _calls_ = 10, a = 100, b = 10, c = 0;", + ]; + createBlockVariables(MAX_GENERATION_RECURSION_DEPTH, 0, CANNOT_THROW, function(defns) { + s.push(defns()); + if (rng(2)) { + s.push(createStatements(3, MAX_GENERATION_RECURSION_DEPTH, CANNOT_THROW, CANNOT_BREAK, CANNOT_CONTINUE, CANNOT_RETURN, 0)); + } else { + s.push(createFunctions(MAX_GENERATED_TOPLEVELS_PER_RUN, MAX_GENERATION_RECURSION_DEPTH, DEFUN_OK, CANNOT_THROW, 0)); + } + }); + // preceding `null` makes for a cleaner output (empty string still shows up etc) + var log = "console.log(null, a, b, c, Infinity, NaN, undefined)"; + if (SUPPORT.arrow && has_await && rng(20) == 0) log = "setImmediate(() => " + log + ")"; + s.push(log + ";"); + return s.join("\n"); +} + +function createFunctions(n, recurmax, allowDefun, canThrow, stmtDepth) { + if (--recurmax < 0) { return ";"; } + var s = ""; + for (var i = get_num(n - 1) + 1; --i >= 0;) { + s += createFunction(recurmax, allowDefun, canThrow, stmtDepth) + "\n"; + } + return s; +} + +function addTrailingComma(list) { + return SUPPORT.trailing_comma && list && rng(20) == 0 ? list + "," : list; +} + +function createParams(was_async, was_generator, noDuplicate) { + var save_async = async; + if (!async) async = was_async; + var save_generator = generator; + if (!generator) generator = was_generator; + var len = unique_vars.length; + var params = []; + for (var i = get_num(3); --i >= 0;) { + var name = createVarName(MANDATORY); + if (noDuplicate || in_class) unique_vars.push(name); + params.push(name); + } + unique_vars.length = len; + generator = save_generator; + async = save_async; + return addTrailingComma(params.join(", ")); +} + +function createArgs(recurmax, stmtDepth, canThrow, noTemplate) { + recurmax--; + if (SUPPORT.template && !noTemplate && rng(20) == 0) return createTemplateLiteral(recurmax, stmtDepth, canThrow); + var args = []; + for (var i = get_num(3); --i >= 0;) switch (SUPPORT.spread ? rng(50) : 3) { + case 0: + case 1: + var name = getVarName(); + if (canThrow && rng(20) == 0) { + args.push("..." + name); + } else { + args.push('...("" + ' + name + ")"); + } + break; + case 2: + args.push("..." + createArrayLiteral(recurmax, stmtDepth, canThrow)); + break; + default: + args.push(rng(2) ? createValue() : createExpression(recurmax, NO_COMMA, stmtDepth, canThrow)); + break; + } + return "(" + addTrailingComma(args.join(", ")) + ")"; +} + +function createAssignmentPairs(recurmax, stmtDepth, canThrow, nameLenBefore, was_async, was_generator) { + var avoid = []; + var len = unique_vars.length; + var pairs = createPairs(recurmax, !nameLenBefore); + pairs.has_rest = nameLenBefore && convertToRest(pairs.names); + unique_vars.length = len; + return pairs; + + function mapShuffled(values, fn) { + var declare_only = []; + var side_effects = []; + values.forEach(function(value, index) { + value = fn(value, index); + if (/]:|=/.test(value) ? canThrow && rng(20) == 0 : rng(5)) { + declare_only.splice(rng(declare_only.length + 1), 0, value); + } else if (canThrow && rng(20) == 0) { + side_effects.splice(rng(side_effects.length + 1), 0, value); + } else { + side_effects.push(value); + } + }); + return declare_only.concat(side_effects); + } + + function convertToRest(names) { + var last = names.length - 1; + if (last >= 0 && SUPPORT.rest && rng(20) == 0) { + var name = names[last]; + if (name && name.indexOf("=") < 0) { + if (/^[[{]/.test(name)) name = "[ " + name + " ]"; + names[last] = "..." + name; + return true; + } + } + } + + function fill(nameFn, valueFn) { + var save_async = async; + if (was_async != null) { + async = false; + if (save_async || was_async) addAvoidVar("await"); + } + var save_generator = generator; + if (was_generator != null) { + generator = false; + if (save_generator || was_generator) addAvoidVar("yield"); + } + avoid.forEach(addAvoidVar); + var save_vars = nameLenBefore && VAR_NAMES.splice(nameLenBefore); + if (nameFn) nameFn(); + if (was_generator != null) { + generator = was_generator; + if (save_generator || was_generator) removeAvoidVar("yield"); + } + if (was_async != null) { + async = was_async; + if (save_async || was_async) removeAvoidVar("await"); + } + if (valueFn) valueFn(); + if (save_vars) [].push.apply(VAR_NAMES, save_vars); + avoid.forEach(removeAvoidVar); + generator = save_generator; + async = save_async; + } + + function createAssignmentValue(recurmax) { + return nameLenBefore && rng(2) ? createValue() : createExpression(recurmax, NO_COMMA, stmtDepth, canThrow); + } + + function createDefaultValue(recurmax, noDefault) { + return !noDefault && SUPPORT.default_value && rng(20) == 0 ? " = " + createAssignmentValue(recurmax) : ""; + } + + function createKey(recurmax, keys) { + var key; + do { + key = createObjectKey(recurmax, stmtDepth, canThrow); + } while (keys.indexOf(key) >= 0); + return key; + } + + function createName() { + unique_vars.push("a", "b", "c", "undefined", "NaN", "Infinity"); + var save_async = async; + if (!async) async = was_async; + var save_generator = generator; + if (!generator) generator = was_generator; + var name = createVarName(MANDATORY); + generator = save_generator; + async = save_async; + unique_vars.length -= 6; + avoid.push(name); + unique_vars.push(name); + return name; + } + + function createPairs(recurmax, noDefault) { + var names = [], values = []; + var m = rng(4), n = rng(4); + if (!nameLenBefore) m = Math.max(m, n, 1); + for (var i = Math.max(m, n); --i >= 0;) { + if (i < m && i < n) { + createDestructured(recurmax, noDefault, names, values); + } else if (i < m) { + var name = createName(); + fill(function() { + names.unshift(name + createDefaultValue(recurmax, noDefault)); + }); + } else { + fill(null, function() { + values.unshift(createAssignmentValue(recurmax)); + }); + } + } + return { + names: names, + values: values, + }; + } + + function createDestructured(recurmax, noDefault, names, values) { + switch (rng(20)) { + case 0: + if (--recurmax < 0) { + names.unshift("[]"); + values.unshift('""'); + } else { + var pairs = createPairs(recurmax); + var default_value; + fill(function() { + default_value = createDefaultValue(recurmax, noDefault); + }, function() { + while (!rng(10)) { + var index = rng(pairs.names.length + 1); + pairs.names.splice(index, 0, ""); + if (index < pairs.values.length) { + pairs.values.splice(index, 0, rng(2) ? createAssignmentValue(recurmax) : ""); + } else switch (rng(5)) { + case 0: + pairs.values[index] = createAssignmentValue(recurmax); + case 1: + pairs.values.length = index + 1; + } + } + convertToRest(pairs.names); + names.unshift("[ " + pairs.names.join(", ") + " ]" + default_value); + values.unshift("[ " + pairs.values.join(", ") + " ]"); + }); + } + break; + case 1: + if (--recurmax < 0) { + names.unshift("{}"); + values.unshift('""'); + } else { + var pairs = createPairs(recurmax); + var keys = []; + pairs.names.forEach(function(name, index) { + if (/^[[{]/.test(name)) { + var key; + do { + key = KEYS[rng(KEYS.length)]; + } while (keys.indexOf(key) >= 0); + keys[index] = key; + } + }); + fill(function() { + var last = pairs.names.length - 1, rest; + if (last >= 0 && !(last in keys) && SUPPORT.rest_object && rng(20) == 0) { + rest = pairs.names.pop(); + if (!/=/.test(rest)) rest = "..." + rest; + } + var s = mapShuffled(pairs.names, function(name, index) { + if (index in keys) return keys[index] + ": " + name; + return rng(10) == 0 ? name : createKey(recurmax, keys) + ": " + name; + }); + if (rest) { + s.push(rest); + s = s.join(", "); + } else { + s = addTrailingComma(s.join(", ")); + } + names.unshift("{ " + s + " }" + createDefaultValue(recurmax, noDefault)); + }, function() { + values.unshift("{ " + addTrailingComma(mapShuffled(pairs.values, function(value, index) { + var key = index in keys ? keys[index] : createKey(recurmax, keys); + return key + ": " + value; + }).join(", ")) + " }"); + }); + } + break; + default: + var name = createName(); + fill(function() { + names.unshift(name + createDefaultValue(recurmax, noDefault)); + }, function() { + values.unshift(createAssignmentValue(recurmax)); + }); + break; + } + } +} + +function filterDirective(s) { + if (!generate_directive && !s[1] && /\("/.test(s[2])) s[2] = ";" + s[2]; + return s; +} + +function createBlockVariables(recurmax, stmtDepth, canThrow, fn) { + ++stmtDepth; + var block_len = block_vars.length; + var class_len = classes.length; + var nameLenBefore = VAR_NAMES.length; + var consts = []; + var lets = []; + unique_vars.push("a", "b", "c", "undefined", "NaN", "Infinity"); + while (!rng(block_vars.length > block_len ? 10 : 100)) { + var name = createVarName(MANDATORY); + if (SUPPORT.let && rng(2)) { + lets.push(name); + } else { + consts.push(name); + } + block_vars.push(name); + } + unique_vars.length -= 6; + fn(function() { + consts.forEach(addAvoidVar); + lets.forEach(addAvoidVar); + var s = []; + if (SUPPORT.class) while (rng(200) == 0) { + var name = "C" + clazz++; + classes.push(name); + s.push(appendExport(stmtDepth, true) + createClassLiteral(recurmax, stmtDepth, canThrow, name)); + } + if (rng(2)) { + s.push(createDefinitions("const", consts), createDefinitions("let", lets)); + } else { + s.push(createDefinitions("let", lets), createDefinitions("const", consts)); + } + s.push(""); + return s.join("\n"); + }); + VAR_NAMES.length = nameLenBefore; + classes.length = class_len; + block_vars.length = block_len; + + function createDefinitions(type, names) { + if (!names.length) return ""; + var s = appendExport(stmtDepth) + type + " "; + switch (SUPPORT.destructuring ? rng(10) : 2) { + case 0: + while (!rng(10)) names.splice(rng(names.length + 1), 0, ""); + s += "[ " + names.join(", ") + " ] = [ " + names.map(function() { + return rng(10) ? createExpression(recurmax, NO_COMMA, stmtDepth, canThrow) : ""; + }).join(", ") + " ];"; + break; + case 1: + var keys = []; + s += "{ " + names.map(function(name, i) { + var key = createObjectKey(recurmax, stmtDepth, canThrow); + if (!/\[/.test(key)) keys[i] = key; + return key + ": " + name; + }).join(", ") + "} = { " + names.map(function(name, i) { + var key = i in keys ? keys[i] : createObjectKey(recurmax, stmtDepth, canThrow); + return key + ": " + createExpression(recurmax, NO_COMMA, stmtDepth, canThrow); + }).join(", ") + "};"; + break; + default: + s += names.map(function(name) { + if (type == "let" && !rng(10)) { + removeAvoidVar(name); + return name; + } + var value = createExpression(recurmax, NO_COMMA, stmtDepth, canThrow); + removeAvoidVar(name); + return name + " = " + value; + }).join(", ") + ";"; + names.length = 0; + break; + } + names.forEach(removeAvoidVar); + return s; + } +} + +function mayCreateBlockVariables(recurmax, stmtDepth, canThrow, fn) { + if (SUPPORT.const_block) { + createBlockVariables(recurmax, stmtDepth, canThrow, fn); + } else { + fn(function() { + return ""; + }); + } +} + +function makeFunction(name) { + lambda_vars.push(name); + if (generator) { + name = "function* " + name; + } else { + name = "function " + name; + } + if (async) name = "async " + name; + return name; +} + +function invokeGenerator(was_generator) { + if (generator && !was_generator) switch (rng(4)) { + case 0: + return ".next()"; + case 1: + return ".next().done"; + case 2: + return ".next().value"; + } + return ""; +} + +function createFunction(recurmax, allowDefun, canThrow, stmtDepth) { + if (--recurmax < 0) { return ";"; } + if (!STMT_COUNT_FROM_GLOBAL) stmtDepth = 0; + ++stmtDepth; + var s = []; + var name, args; + var nameLenBefore = VAR_NAMES.length; + var lambda_len = lambda_vars.length; + var save_async = async; + var save_generator = generator; + async = SUPPORT.async && rng(200) == 0; + generator = SUPPORT.generator && rng(50) == 0; + if (async && generator && !SUPPORT.async_generator) { + if (rng(2)) { + async = false; + } else { + generator = false; + } + } + createBlockVariables(recurmax, stmtDepth, canThrow, function(defns) { + if (allowDefun || rng(5) > 0) { + name = "f" + funcs++; + } else { + unique_vars.push("a", "b", "c"); + name = createVarName(MANDATORY, !allowDefun); + unique_vars.length -= 3; + } + var params; + if (SUPPORT.destructuring && (!allowDefun || !(name in called)) && rng(2)) { + called[name] = false; + var pairs = createAssignmentPairs(recurmax, stmtDepth, canThrow, nameLenBefore, save_async, save_generator); + params = pairs.names.join(", "); + if (!pairs.has_rest) params = addTrailingComma(params); + args = "(" + addTrailingComma(pairs.values.join(", ")) + ")"; + } else { + params = createParams(save_async, save_generator); + } + s.push(makeFunction(name) + "(" + params + "){", strictMode()); + s.push(defns()); + if (rng(5) === 0) { + // functions with functions. lower the recursion to prevent a mess. + s.push(createFunctions(5, Math.ceil(recurmax * 0.7), DEFUN_OK, canThrow, stmtDepth)); + } else { + // functions with statements + s.push(_createStatements(3, recurmax, canThrow, CANNOT_BREAK, CANNOT_CONTINUE, CAN_RETURN, stmtDepth)); + } + s.push("}", ""); + s = filterDirective(s).join("\n"); + }); + var call_next = invokeGenerator(save_generator); + generator = save_generator; + async = save_async; + lambda_vars.length = lambda_len; + VAR_NAMES.length = nameLenBefore; + + if (allowDefun) s = appendExport(stmtDepth, true) + s; + if (!allowDefun) { + // avoid "function statements" (decl inside statements) + s = appendExport(stmtDepth) + "var " + createVarName(MANDATORY) + " = " + s; + s += args || createArgs(recurmax, stmtDepth, canThrow); + s += call_next; + } else if (!(name in called) || args || rng(3)) { + s += appendExport(stmtDepth) + "var " + createVarName(MANDATORY) + " = " + name; + s += args || createArgs(recurmax, stmtDepth, canThrow); + s += call_next; + } + + return s + ";"; +} + +function _createStatements(n, recurmax, canThrow, canBreak, canContinue, cannotReturn, stmtDepth) { + if (--recurmax < 0) return ";"; + var s = ""; + for (var i = get_num(n); --i >= 0;) { + s += createStatement(recurmax, canThrow, canBreak, canContinue, cannotReturn, stmtDepth) + "\n"; + } + return s; +} + +function createStatements(n, recurmax, canThrow, canBreak, canContinue, cannotReturn, stmtDepth) { + var s = ""; + mayCreateBlockVariables(recurmax, stmtDepth, canThrow, function(defns) { + s += defns() + "\n"; + s += _createStatements(n, recurmax, canThrow, canBreak, canContinue, cannotReturn, stmtDepth); + }); + return s; +} + +function enableLoopControl(flag, defaultValue) { + return Array.isArray(flag) && flag.indexOf("") < 0 ? flag.concat("") : flag || defaultValue; +} + +function createLabel(canBreak, canContinue) { + var label; + if (rng(10) < 3) { + label = ++labels; + if (Array.isArray(canBreak)) { + canBreak = canBreak.slice(); + } else { + canBreak = canBreak ? [ "" ] : []; + } + canBreak.push(label); + if (Array.isArray(canContinue)) { + canContinue = canContinue.slice(); + } else { + canContinue = canContinue ? [ "" ] : []; + } + canContinue.push(label); + } + return { + break: canBreak, + continue: canContinue, + target: label ? "L" + label + ": " : "" + }; +} + +function getLabel(label) { + if (!Array.isArray(label)) return ""; + label = label[rng(label.length)]; + return label && " L" + label; +} + +function declareVarName(name, no_var) { + if (!SUPPORT.let || !no_var && rng(10)) return "var "; + block_vars.push(name); + return rng(2) ? "let " : "const "; +} + +function createImportAlias() { + if (rng(10)) return "alias" + imports++; + unique_vars.push("a", "b", "c", "undefined", "NaN", "Infinity"); + var name = createVarName(MANDATORY); + block_vars.push(name); + unique_vars.length -= 6; + return name; +} + +function createStatement(recurmax, canThrow, canBreak, canContinue, cannotReturn, stmtDepth, target) { + ++stmtDepth; + var loop = ++loops; + if (--recurmax < 0) { + return createExpression(recurmax, COMMA_OK, stmtDepth, canThrow) + ";"; + } + + // allow to forcefully generate certain structures at first or second recursion level + if (target === undefined) { + if (stmtDepth === 1 && STMT_FIRST_LEVEL_OVERRIDE >= 0) target = STMT_FIRST_LEVEL_OVERRIDE; + else if (stmtDepth === 2 && STMT_SECOND_LEVEL_OVERRIDE >= 0) target = STMT_SECOND_LEVEL_OVERRIDE; + else target = STMTS_TO_USE[rng(STMTS_TO_USE.length)]; + } + + switch (target) { + case STMT_BLOCK: + var label = createLabel(canBreak); + return label.target + "{" + createStatements(5, recurmax, canThrow, label.break, canContinue, cannotReturn, stmtDepth) + "}"; + case STMT_IF_ELSE: + return "if (" + createExpression(recurmax, COMMA_OK, stmtDepth, canThrow) + ")" + createStatement(recurmax, canThrow, canBreak, canContinue, cannotReturn, stmtDepth) + (rng(2) ? " else " + createStatement(recurmax, canThrow, canBreak, canContinue, cannotReturn, stmtDepth) : ""); + case STMT_DO_WHILE: + var label = createLabel(canBreak, canContinue); + canBreak = label.break || enableLoopControl(canBreak, CAN_BREAK); + canContinue = label.continue || enableLoopControl(canContinue, CAN_CONTINUE); + return "{var brake" + loop + " = 5; " + label.target + "do {" + createStatement(recurmax, canThrow, canBreak, canContinue, cannotReturn, stmtDepth) + "} while (" + createExpression(recurmax, COMMA_OK, stmtDepth, canThrow) + " && --brake" + loop + " > 0);}"; + case STMT_WHILE: + var label = createLabel(canBreak, canContinue); + canBreak = label.break || enableLoopControl(canBreak, CAN_BREAK); + canContinue = label.continue || enableLoopControl(canContinue, CAN_CONTINUE); + return "{var brake" + loop + " = 5; " + label.target + "while (" + createExpression(recurmax, COMMA_OK, stmtDepth, canThrow) + " && --brake" + loop + " > 0)" + createStatement(recurmax, canThrow, canBreak, canContinue, cannotReturn, stmtDepth) + "}"; + case STMT_FOR_LOOP: + var label = createLabel(canBreak, canContinue); + canBreak = label.break || enableLoopControl(canBreak, CAN_BREAK); + canContinue = label.continue || enableLoopControl(canContinue, CAN_CONTINUE); + return label.target + "for (var brake" + loop + " = 5; " + createExpression(recurmax, COMMA_OK, stmtDepth, canThrow) + " && brake" + loop + " > 0; --brake" + loop + ")" + createStatement(recurmax, canThrow, canBreak, canContinue, cannotReturn, stmtDepth); + case STMT_FOR_ENUM: + var block_len = block_vars.length; + var nameLenBefore = VAR_NAMES.length; + var label = createLabel(canBreak, canContinue); + canBreak = label.break || enableLoopControl(canBreak, CAN_BREAK); + canContinue = label.continue || enableLoopControl(canContinue, CAN_CONTINUE); + var of = SUPPORT.for_of && rng(20) == 0; + var key; + if (rng(10)) { + key = "key" + loop; + } else if (bug_for_of_async && of) { + addAvoidVar("async"); + key = getVarName(NO_CONST); + removeAvoidVar("async"); + } else { + key = getVarName(NO_CONST); + } + var init = ""; + if (!/^key/.test(key)) { + if (!(of && bug_for_of_var) && rng(10) == 0) init = "var "; + } else { + init = declareVarName(key, of && bug_for_of_var); + } + if (!SUPPORT.destructuring || of && !(canThrow && rng(20) == 0) || rng(10)) { + init += key; + } else if (rng(5)) { + init += "[ " + key + " ]"; + } else { + init += "{ length: " + key + " }"; + } + var s = "var expr" + loop + " = "; + if (of) { + var await = SUPPORT.for_await_of && async && rng(20) == 0; + if (SUPPORT.generator && rng(20) == 0) { + var gen = getVarName(); + if (canThrow && rng(20) == 0) { + s += gen + "; "; + } else { + s += gen + " && typeof " + gen + "[Symbol."; + s += await ? "asyncIterator" : "iterator"; + s += '] == "function" ? ' + gen + " : " + createArrayLiteral(recurmax, stmtDepth, canThrow) + "; "; + } + } else if (rng(5)) { + s += createArrayLiteral(recurmax, stmtDepth, canThrow) + "; "; + } else if (canThrow && rng(20) == 0) { + s += createExpression(recurmax, COMMA_OK, stmtDepth, canThrow) + "; "; + } else { + s += '"" + (' + createExpression(recurmax, COMMA_OK, stmtDepth, canThrow) + "); "; + } + s += label.target + " for "; + if (await) { + s += "await "; + has_await = true; + } + s += "(" + init + " of expr" + loop + ") {"; + } else { + s += createExpression(recurmax, COMMA_OK, stmtDepth, canThrow) + "; "; + s += label.target + " for (" + init + " in expr" + loop + ") {"; + } + if (/^key/.test(key)) VAR_NAMES.push(key); + if (rng(3)) { + s += "c = 1 + c; "; + unique_vars.push("a", "b", "c", "undefined", "NaN", "Infinity"); + var name; + do { + name = createVarName(MANDATORY); + } while (name == key); + unique_vars.length -= 6; + s += declareVarName(name) + name + " = expr" + loop + "[" + key + "]; "; + } + s += createStatement(recurmax, canThrow, canBreak, canContinue, cannotReturn, stmtDepth) + "}"; + VAR_NAMES.length = nameLenBefore; + block_vars.length = block_len; + return "{" + s + "}"; + case STMT_SEMI: + return use_strict && rng(20) === 0 ? '"use strict";' : ";"; + case STMT_EXPR: + if (SUPPORT.destructuring && stmtDepth == 1 && !export_default && rng(20) == 0) { + export_default = true; + return "export default " + createExpression(recurmax, COMMA_OK, stmtDepth, canThrow) + ";"; + } + return createExpression(recurmax, COMMA_OK, stmtDepth, canThrow) + ";"; + case STMT_SWITCH: + // note: case args are actual expressions + // note: default does not _need_ to be last + return "switch (" + createExpression(recurmax, COMMA_OK, stmtDepth, canThrow) + ") { " + createSwitchParts(recurmax, 4, canThrow, canBreak, canContinue, cannotReturn, stmtDepth) + "}"; + case STMT_VAR: + if (SUPPORT.destructuring && stmtDepth == 1 && rng(5) == 0) { + var s = rng(2) ? " " + createImportAlias() : ""; + if (rng(10)) { + if (s) s += ","; + if (rng(2)) { + s += " * as " + createImportAlias(); + } else { + var names = []; + for (var i = get_num(3); --i >= 0;) { + var name = createImportAlias(); + names.push(rng(2) ? getDotKey() + " as " + name : name); + } + s += " { " + names.join(", ") + " }"; + } + } + if (s) s += " from"; + return "import" + s + ' "path/to/module.js";'; + } else if (SUPPORT.destructuring && rng(20) == 0) { + var pairs = createAssignmentPairs(recurmax, stmtDepth, canThrow); + return appendExport(stmtDepth) + "var " + pairs.names.map(function(name, index) { + return index in pairs.values ? name + " = " + pairs.values[index] : name; + }).join(", ") + ";"; + } else switch (rng(3)) { + case 0: + unique_vars.push("c"); + var name = createVarName(MANDATORY); + unique_vars.pop(); + return appendExport(stmtDepth) + "var " + name + ";"; + case 1: + // initializer can only have one expression + unique_vars.push("c"); + var name = createVarName(MANDATORY); + unique_vars.pop(); + return appendExport(stmtDepth) + "var " + name + " = " + createExpression(recurmax, NO_COMMA, stmtDepth, canThrow) + ";"; + default: + // initializer can only have one expression + unique_vars.push("c"); + var n1 = createVarName(MANDATORY); + var n2 = createVarName(MANDATORY); + unique_vars.pop(); + return appendExport(stmtDepth) + "var " + n1 + " = " + createExpression(recurmax, NO_COMMA, stmtDepth, canThrow) + ", " + n2 + " = " + createExpression(recurmax, NO_COMMA, stmtDepth, canThrow) + ";"; + } + case STMT_RETURN_ETC: + switch (rng(8)) { + case 0: + case 1: + case 2: + case 3: + if (canBreak && rng(5) === 0) return "break" + getLabel(canBreak) + ";"; + if (canContinue && rng(5) === 0) return "continue" + getLabel(canContinue) + ";"; + if (cannotReturn) return createExpression(recurmax, NO_COMMA, stmtDepth, canThrow) + ";"; + if (rng(3) == 0) return "/*3*/return;"; + return "return " + createExpression(recurmax, NO_COMMA, stmtDepth, canThrow) + ";"; + case 4: + // this is actually more like a parser test, but perhaps it hits some dead code elimination traps + // must wrap in curlies to prevent orphaned `else` statement + // note: you can't `throw` without an expression so don't put a `throw` option in this case + if (cannotReturn) return createExpression(recurmax, NO_COMMA, stmtDepth, canThrow) + ";"; + return "{ /*2*/ return\n" + createExpression(recurmax, NO_COMMA, stmtDepth, canThrow) + "}"; + default: + // must wrap in curlies to prevent orphaned `else` statement + if (canThrow && rng(5) === 0) return "{ throw " + createExpression(recurmax, NO_COMMA, stmtDepth, canThrow) + "}"; + if (cannotReturn) return createExpression(recurmax, NO_COMMA, stmtDepth, canThrow) + ";"; + return "{ return " + createExpression(recurmax, NO_COMMA, stmtDepth, canThrow) + "}"; + } + case STMT_FUNC_EXPR: + // "In non-strict mode code, functions can only be declared at top level, inside a block, or ..." + // (don't make func decls in `if`; it's only a parser thing because you can't call them without a block) + return "{" + createFunction(recurmax, NO_DEFUN, canThrow, stmtDepth) + "}"; + case STMT_TRY: + // catch var could cause some problems + // note: the "blocks" are syntactically mandatory for try/catch/finally + var n = rng(3); // 0=only catch, 1=only finally, 2=catch+finally + var s = "try {" + createStatement(recurmax, n === 1 ? CANNOT_THROW : CAN_THROW, canBreak, canContinue, cannotReturn, stmtDepth) + " }"; + if (n !== 1) { + // the catch var should only be accessible in the catch clause... + // we have to do go through some trouble here to prevent leaking it + mayCreateBlockVariables(recurmax, stmtDepth, canThrow, function(defns) { + var block_len = block_vars.length; + var nameLenBefore = VAR_NAMES.length; + var unique_len = unique_vars.length; + if (SUPPORT.catch_omit_var && !rng(20)) { + s += " catch { "; + } else if (canThrow && SUPPORT.destructuring && !rng(20)) { + unique_vars.push("a", "b", "c", "undefined", "NaN", "Infinity"); + var name = createVarName(MANDATORY); + block_vars.push(name); + var message = createVarName(MANDATORY); + block_vars.push(message); + unique_vars.length -= 6; + if (SUPPORT.computed_key && rng(10) == 0) { + s += " catch ({ message: " + message + ", "; + addAvoidVar(name); + s += "[" + createExpression(recurmax, NO_COMMA, stmtDepth, canThrow) + "]: " + name; + removeAvoidVar(name); + s += " }) { "; + } else { + s += " catch ({ name: " + name + ", message: " + message + " }) { "; + } + } else { + var name = createVarName(MANDATORY); + if (!catch_redef) unique_vars.push(name); + s += " catch (" + name + ") { "; + } + var catches = VAR_NAMES.length - nameLenBefore; + s += defns() + "\n"; + s += _createStatements(3, recurmax, canThrow, canBreak, canContinue, cannotReturn, stmtDepth); + s += " }"; + // remove catch variables + block_vars.length = block_len; + if (catches > 0) VAR_NAMES.splice(nameLenBefore, catches); + unique_vars.length = unique_len; + }); + } + if (n !== 0) s += [ + " finally { ", + createStatements(5, recurmax, canThrow, canBreak, canContinue, cannotReturn, stmtDepth), + " }", + ].join(""); + return s; + case STMT_C: + return "c = c + 1;"; + default: + throw "no"; + } +} + +function createSwitchParts(recurmax, n, canThrow, canBreak, canContinue, cannotReturn, stmtDepth) { + var hadDefault = false; + var s = [ "" ]; + canBreak = enableLoopControl(canBreak, CAN_BREAK); + while (n-- > 0) { + //hadDefault = n > 0; // disables weird `default` clause positioning (use when handling destabilizes) + if (hadDefault || rng(5) > 0) { + s.push( + "case " + createExpression(recurmax, NO_COMMA, stmtDepth, canThrow) + ":", + _createStatements(3, recurmax, canThrow, canBreak, canContinue, cannotReturn, stmtDepth), + rng(10) > 0 ? " break;" : "/* fall-through */", + "" + ); + } else { + hadDefault = true; + s.push( + "default:", + _createStatements(3, recurmax, canThrow, canBreak, canContinue, cannotReturn, stmtDepth), + "" + ); + } + } + return s.join("\n"); +} + +function createExpression(recurmax, noComma, stmtDepth, canThrow) { + if (--recurmax < 0) { + return "(c = 1 + c, " + createNestedBinaryExpr(recurmax, noComma, stmtDepth, canThrow) + ")"; // note: should return a simple non-recursing expression value! + } + // since `a` and `b` are our canaries we want them more frequently than other expressions (1/3rd chance of a canary) + switch (rng(6)) { + case 0: + return "(a++ + (" + _createExpression(recurmax, noComma, stmtDepth, canThrow) + "))"; + case 1: + return "((--b) + (" + _createExpression(recurmax, noComma, stmtDepth, canThrow) + "))"; + case 2: + return "((c = c + 1) + (" + _createExpression(recurmax, noComma, stmtDepth, canThrow) + "))"; // c only gets incremented + default: + var expr = "(" + _createExpression(recurmax, noComma, stmtDepth, canThrow) + ")"; + if (async && rng(50) == 0) { + has_await = true; + return "(await" + expr + ")"; + } + if (generator && rng(50) == 0) return "(yield" + (canThrow && rng(20) == 0 ? "*" : "") + expr + ")"; + return expr; + } +} + +function _createExpression(recurmax, noComma, stmtDepth, canThrow) { + var p = 0; + switch (rng(_createExpression.N)) { + case p++: + if (generator && rng(50) == 0) return "yield"; + case p++: + return createUnaryPrefix() + (rng(2) ? "a" : "b"); + case p++: + case p++: + return (rng(2) ? "a" : "b") + createUnaryPostfix(); + case p++: + case p++: + // parens needed because assignments aren't valid unless they're the left-most op(s) in an expression + return "b " + createAssignment() + " a"; + case p++: + case p++: + return rng(2) + " === 1 ? a : b"; + case p++: + if (SUPPORT.template && rng(20) == 0) { + var tmpl = createTemplateLiteral(recurmax, stmtDepth, canThrow); + if (rng(10) == 0) tmpl = "String.raw" + tmpl; + return tmpl; + } + case p++: + return createValue(); + case p++: + if (SUPPORT.destructuring && rng(20) == 0) { + var name = "alias" + rng(imports + 2); + return canThrow && rng(20) == 0 ? name : "typeof " + name + ' != "undefined" && ' + name; + } + case p++: + return getVarName(); + case p++: + switch (SUPPORT.destructuring ? rng(20) : 2) { + case 0: + return [ + "[ ", + new Array(rng(3)).join(), + getVarName(NO_CONST, NO_LAMBDA), + new Array(rng(3)).join(), + " ] = ", + createArrayLiteral(recurmax, stmtDepth, canThrow), + ].join(""); + case 1: + return [ + "{ ", + rng(2) ? "" : createObjectKey(recurmax, stmtDepth, canThrow) + ": ", + getVarName(NO_CONST, NO_LAMBDA), + " } = ", + createExpression(recurmax, COMMA_OK, stmtDepth, canThrow), + " || {}", + ].join(""); + default: + return getVarName(NO_CONST, NO_LAMBDA) + createAssignment() + createExpression(recurmax, COMMA_OK, stmtDepth, canThrow); + } + case p++: + return createExpression(recurmax, COMMA_OK, stmtDepth, canThrow); + case p++: + return createExpression(recurmax, noComma, stmtDepth, canThrow) + " ? " + createExpression(recurmax, NO_COMMA, stmtDepth, canThrow) + " : " + createExpression(recurmax, noComma, stmtDepth, canThrow); + case p++: + case p++: + var nameLenBefore = VAR_NAMES.length; + var lambda_len = lambda_vars.length; + var save_async = async; + var save_generator = generator; + async = SUPPORT.async && rng(200) == 0; + generator = SUPPORT.generator && rng(50) == 0; + if (async && generator && !SUPPORT.async_generator) { + if (rng(2)) { + async = false; + } else { + generator = false; + } + } + unique_vars.push("a", "b", "c"); + var name = createVarName(MAYBE); // note: this name is only accessible from _within_ the function. and immutable at that. + unique_vars.length -= 3; + var s = []; + switch (rng(5)) { + case 0: + if (SUPPORT.arrow && !name && !generator && rng(2)) { + var args, suffix; + (rng(2) ? createBlockVariables : function() { + arguments[3](); + })(recurmax, stmtDepth, canThrow, function(defns) { + var params; + if (SUPPORT.destructuring && rng(2)) { + var pairs = createAssignmentPairs(recurmax, stmtDepth, canThrow, nameLenBefore, save_async, save_generator); + params = pairs.names.join(", "); + if (!pairs.has_rest) params = addTrailingComma(params); + args = "(" + addTrailingComma(pairs.values.join(", ")) + ")"; + } else { + params = createParams(save_async, save_generator, NO_DUPLICATE); + } + params = (async ? "async (" : "(") + params + ") => "; + if (defns) { + s.push( + "(" + params + "{", + strictMode(), + defns(), + _createStatements(5, recurmax, canThrow, CANNOT_BREAK, CANNOT_CONTINUE, CAN_RETURN, stmtDepth) + ); + suffix = "})"; + } else { + s.push("(" + params); + switch (rng(10)) { + case 0: + if (!in_class) { + s.push('(typeof arguments != "undefined" && arguments && arguments[' + rng(3) + "])"); + break; + } + case 1: + s.push("(this && this." + getDotKey() + ")"); + break; + default: + s.push(createExpression(recurmax, NO_COMMA, stmtDepth, canThrow)); + break; + } + suffix = ")"; + } + }); + generator = save_generator; + async = save_async; + lambda_vars.length = lambda_len; + VAR_NAMES.length = nameLenBefore; + if (!args && rng(2)) args = createArgs(recurmax, stmtDepth, canThrow); + if (args) suffix += args; + s.push(suffix); + } else { + s.push( + "(" + makeFunction(name) + "(){", + strictMode(), + createStatements(5, recurmax, canThrow, CANNOT_BREAK, CANNOT_CONTINUE, CAN_RETURN, stmtDepth), + rng(2) ? "})" : "})()" + invokeGenerator(save_generator) + ); + } + break; + case 1: + s.push( + "+" + makeFunction(name) + "(){", + strictMode(), + createStatements(5, recurmax, canThrow, CANNOT_BREAK, CANNOT_CONTINUE, CAN_RETURN, stmtDepth), + "}()" + invokeGenerator(save_generator) + ); + break; + case 2: + s.push( + "!" + makeFunction(name) + "(){", + strictMode(), + createStatements(5, recurmax, canThrow, CANNOT_BREAK, CANNOT_CONTINUE, CAN_RETURN, stmtDepth), + "}()" + invokeGenerator(save_generator) + ); + break; + case 3: + s.push( + "void " + makeFunction(name) + "(){", + strictMode(), + createStatements(5, recurmax, canThrow, CANNOT_BREAK, CANNOT_CONTINUE, CAN_RETURN, stmtDepth), + "}()" + invokeGenerator(save_generator) + ); + break; + default: + async = false; + generator = false; + var instantiate = rng(4) ? "new " : ""; + createBlockVariables(recurmax, stmtDepth, canThrow, function(defns) { + s.push( + instantiate + makeFunction(name) + "(" + createParams(save_async, save_generator) + "){", + 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 = get_num(3); --i >= 0;) { + s.push((in_class ? "if (this) " : "") + createThisAssignment(recurmax, stmtDepth, canThrow)); + } + s.push(_createStatements(5, 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; + lambda_vars.length = lambda_len; + VAR_NAMES.length = nameLenBefore; + s.push(rng(2) ? "}" : "}" + createArgs(recurmax, stmtDepth, canThrow, instantiate)); + break; + } + generator = save_generator; + async = save_async; + lambda_vars.length = lambda_len; + VAR_NAMES.length = nameLenBefore; + return filterDirective(s).join("\n"); + case p++: + case p++: + return createTypeofExpr(recurmax, stmtDepth, canThrow); + case p++: + case p++: + // more like a parser test but perhaps comment nodes mess up the analysis? + // note: parens not needed for post-fix (since that's the default when ambiguous) + // for prefix ops we need parens to prevent accidental syntax errors. + switch (rng(6)) { + case 0: + return "a/* ignore */++"; + case 1: + return "b/* ignore */--"; + case 2: + return "++/* ignore */a"; + case 3: + return "--/* ignore */b"; + case 4: + // only groups that wrap a single variable return a "Reference", so this is still valid. + // may just be a parser edge case that is invisible to uglify... + return "--(b)"; + case 5: + // classic 0.3-0.1 case; 1-0.1-0.1-0.1 is not 0.7 :) + return "b + 1 - 0.1 - 0.1 - 0.1"; + default: + return "--/* ignore */b"; + } + case p++: + case p++: + return createNestedBinaryExpr(recurmax, noComma, stmtDepth, canThrow); + case p++: + case p++: + return createUnarySafePrefix() + "(" + createNestedBinaryExpr(recurmax, noComma, stmtDepth, canThrow) + ")"; + case p++: + return " (" + createExpression(recurmax, COMMA_OK, stmtDepth, canThrow) + " || a || 3).toString() "; + case p++: + return " /[abc4]/.test((" + createExpression(recurmax, COMMA_OK, stmtDepth, canThrow) + " || b || 5).toString()) "; + case p++: + return " /[abc4]/g.exec((" + createExpression(recurmax, COMMA_OK, stmtDepth, canThrow) + " || b || 5).toString()) "; + case p++: + return " (" + createExpression(recurmax, COMMA_OK, stmtDepth, canThrow) + " || " + rng(10) + ").toString()[" + + createExpression(recurmax, COMMA_OK, stmtDepth, canThrow) + "] "; + case p++: + return createArrayLiteral(recurmax, stmtDepth, canThrow); + case p++: + return createObjectLiteral(recurmax, stmtDepth, canThrow); + case p++: + return createArrayLiteral(recurmax, stmtDepth, canThrow) + "[" + + createExpression(recurmax, COMMA_OK, stmtDepth, canThrow) + "]"; + case p++: + return createObjectLiteral(recurmax, stmtDepth, canThrow) + "[" + + createExpression(recurmax, COMMA_OK, stmtDepth, canThrow) + "]"; + case p++: + return createArrayLiteral(recurmax, stmtDepth, canThrow) + "." + getDotKey(); + case p++: + return createObjectLiteral(recurmax, stmtDepth, canThrow) + "." + getDotKey(); + case p++: + return createValue() + " in " + createArrayLiteral(recurmax, stmtDepth, canThrow); + case p++: + return createValue() + " in " + createObjectLiteral(recurmax, stmtDepth, canThrow); + case p++: + var name = getVarName(); + var prop = "[" + createExpression(recurmax, COMMA_OK, stmtDepth, canThrow) + "]"; + if (SUPPORT.optional_chaining && rng(50) == 0) return name + "?." + prop; + if (canThrow && rng(20) == 0) return name + prop; + return name + " && " + name + prop; + case p++: + var name = getVarName(); + var prop = getDotKey(); + if (SUPPORT.optional_chaining && rng(50) == 0) return name + "?." + prop; + if (canThrow && rng(20) == 0) return name + "." + prop; + return name + " && " + name + "." + prop; + case p++: + case p++: + var name = getVarName(); + var fn = name + "." + getDotKey(); + var s = "typeof " + fn + ' == "function" && --_calls_ >= 0 && '; + s += rng(5) ? fn : "(" + createExpression(recurmax, NO_COMMA, stmtDepth, canThrow) + ", " + fn + ")"; + s += createArgs(recurmax, stmtDepth, canThrow); + return mayDefer(canThrow && rng(20) == 0 ? s : name + " && " + s); + case p++: + if (SUPPORT.class) { + if (classes.length && rng(20) == 0) { + return "--_calls_ >= 0 && new " + classes[rng(classes.length)] + createArgs(recurmax, stmtDepth, canThrow, NO_TEMPLATE); + } + if (rng(200) == 0) { + var s = "--_calls_ >= 0 && new "; + var nameLenBefore = VAR_NAMES.length; + var class_len = classes.length; + var name; + if (canThrow && rng(20) == 0) { + in_class++; + name = createVarName(MAYBE); + in_class--; + } else if (rng(2)) { + name = "C" + clazz++; + classes.push(name); + } + s += createClassLiteral(recurmax, stmtDepth, canThrow, name); + classes.length = class_len; + VAR_NAMES.length = nameLenBefore; + s += createArgs(recurmax, stmtDepth, canThrow, NO_TEMPLATE); + return s; + } + } + case p++: + case p++: + case p++: + var name; + do { + name = rng(3) == 0 ? getVarName() : "f" + rng(funcs + 2); + } while (name in called && !called[name]); + called[name] = true; + var args = createArgs(recurmax, stmtDepth, canThrow); + var call = "typeof " + name + ' == "function" && --_calls_ >= 0 && ' + name + args; + if (canThrow) { + if (SUPPORT.optional_chaining && args[0] != "`" && rng(50) == 0) { + call = "--_calls_ >= 0 && " + name + "?." + args; + } else if (rng(20) == 0) { + call = "--_calls_ >= 0 && " + name + args; + } + } + return mayDefer(call); + } + _createExpression.N = p; + return _createExpression(recurmax, noComma, stmtDepth, canThrow); +} + +function createArrayLiteral(recurmax, stmtDepth, canThrow) { + recurmax--; + var arr = []; + for (var i = get_num(5); --i >= 0;) switch (SUPPORT.spread ? rng(50) : 3 + rng(47)) { + case 0: + case 1: + var name = getVarName(); + if (canThrow && rng(20) == 0) { + arr.push("..." + name); + } else { + arr.push('...("" + ' + name + ")"); + } + break; + case 2: + arr.push("..." + createArrayLiteral(recurmax, stmtDepth, canThrow)); + break; + case 3: + case 4: + // in rare cases produce an array hole element + arr.push(""); + break; + default: + arr.push(createExpression(recurmax, COMMA_OK, stmtDepth, canThrow)); + break; + } + return "[" + arr.join(", ") + "]"; +} + +function createTemplateLiteral(recurmax, stmtDepth, canThrow) { + recurmax--; + var s = []; + addText(); + for (var i = get_num(5); --i >= 0;) { + s.push("${", createExpression(recurmax, COMMA_OK, stmtDepth, canThrow), "}"); + addText(); + } + return "`" + s.join(rng(5) ? "" : "\n") + "`"; + + function addText() { + while (rng(5) == 0) s.push([ + " ", + "$", + "}", + "\\`", + "\\\\", + "tmpl", + ][rng(6)]); + } +} + +var SAFE_KEYS = [ + "a", + "b", + "c", + "foo", + "NaN", + "null", + "Infinity", + "undefined", + "async", + "done", + "get", + "in", + "length", + "next", + "set", + "static", + "then", + "value", + "var", +]; +var KEYS = [ + "''", + '"\t"', + '"-2"', + "0", + "1.5", + "3", +].concat(SAFE_KEYS); +SAFE_KEYS = SAFE_KEYS.concat(SAFE_KEYS); +SAFE_KEYS = SAFE_KEYS.concat(SAFE_KEYS); +SAFE_KEYS = SAFE_KEYS.concat(SAFE_KEYS); +SAFE_KEYS.push("__proto__"); + +function getDotKey(assign) { + var key; + do { + key = SAFE_KEYS[rng(SAFE_KEYS.length)]; + } while (assign && key == "length"); + return key; +} + +function createObjectKey(recurmax, stmtDepth, canThrow) { + if (SUPPORT.computed_key && rng(10) == 0) { + return "[" + createExpression(recurmax, NO_COMMA, stmtDepth, canThrow) + "]"; + } + return KEYS[rng(KEYS.length)]; +} + +function createSuperAssignment(recurmax, stmtDepth, canThrow) { + var s = rng(2) ? "super." + getDotKey() : "super[" + createExpression(recurmax, COMMA_OK, stmtDepth, canThrow) + "]"; + return getVarName(NO_CONST, NO_LAMBDA) + createAssignment() + s + ";"; +} + +function createThisAssignment(recurmax, stmtDepth, canThrow) { + var s = rng(2) ? "this." + getDotKey(true) : "this[" + createExpression(recurmax, COMMA_OK, stmtDepth, canThrow) + "]"; + return s + createAssignment() + _createBinaryExpr(recurmax, COMMA_OK, stmtDepth, canThrow) + ";"; +} + +function createObjectFunction(recurmax, stmtDepth, canThrow, internal, isClazz) { + var nameLenBefore = VAR_NAMES.length; + var save_async = async; + var save_generator = generator; + var s; + var name; + if (internal) { + name = internal; + } else if (isClazz) { + var clazzName = classes.pop(); + name = createObjectKey(recurmax, stmtDepth, canThrow); + classes.push(clazzName); + } else { + name = createObjectKey(recurmax, stmtDepth, canThrow); + } + var fn; + switch (internal ? 2 : rng(SUPPORT.computed_key ? 3 : 2)) { + case 0: + async = false; + generator = false; + fn = function(defns) { + s = [ + "get " + name + "(){", + strictMode(), + defns(), + _createStatements(2, recurmax, canThrow, CANNOT_BREAK, CANNOT_CONTINUE, CAN_RETURN, stmtDepth), + createStatement(recurmax, canThrow, CANNOT_BREAK, CANNOT_CONTINUE, CAN_RETURN, stmtDepth, STMT_RETURN_ETC), + "}", + ]; + }; + break; + case 1: + var prop; + do { + prop = getDotKey(); + } while (name == prop); + async = false; + generator = false; + fn = function(defns) { + s = [ + "set " + name + "(" + createVarName(MANDATORY) + "){", + strictMode(), + defns(), + _createStatements(2, recurmax, canThrow, CANNOT_BREAK, CANNOT_CONTINUE, CAN_RETURN, stmtDepth), + "this." + prop + createAssignment() + _createBinaryExpr(recurmax, COMMA_OK, stmtDepth, canThrow) + ";", + "}", + ]; + }; + break; + default: + if (/^(constructor|super)$/.test(internal)) { + async = false; + generator = false; + name = "constructor"; + } else { + async = SUPPORT.async && rng(200) == 0; + generator = SUPPORT.generator && rng(50) == 0; + if (async && generator && !SUPPORT.async_generator) { + if (rng(2)) { + async = false; + } else { + generator = false; + } + } + } + fn = function(defns) { + if (generator) name = "*" + name; + if (async) name = "async "+ name; + var save_allow = allow_this; + if (internal == "super") allow_this = false; + s = [ + name + "(" + createParams(save_async, save_generator, NO_DUPLICATE) + "){", + strictMode(), + defns(), + ]; + s.push(_createStatements(2, recurmax, canThrow, CANNOT_BREAK, CANNOT_CONTINUE, CANNOT_RETURN, stmtDepth)); + if (internal == "super") s.push("super" + createArgs(recurmax, stmtDepth, canThrow, NO_TEMPLATE) + ";"); + allow_this = save_allow; + if (/^(constructor|super)$/.test(internal) || rng(10) == 0) for (var i = get_num(3); --i >= 0;) { + s.push(rng(2) ? createSuperAssignment(recurmax, stmtDepth, canThrow) : createThisAssignment(recurmax, stmtDepth, canThrow)); + } + s.push(_createStatements(2, recurmax, canThrow, CANNOT_BREAK, CANNOT_CONTINUE, CAN_RETURN, stmtDepth), "}"); + }; + break; + } + createBlockVariables(recurmax, stmtDepth, canThrow, fn); + generator = save_generator; + async = save_async; + VAR_NAMES.length = nameLenBefore; + return filterDirective(s).join("\n"); +} + +function createObjectLiteral(recurmax, stmtDepth, canThrow) { + recurmax--; + var obj = [ "({" ]; + var offset = SUPPORT.spread_object ? 0 : SUPPORT.computed_key ? 2 : 4; + var has_proto = false; + for (var i = get_num(5); --i >= 0;) switch (offset + rng(50 - offset)) { + case 0: + obj.push("..." + getVarName() + ","); + break; + case 1: + obj.push("..." + createExpression(recurmax, COMMA_OK, stmtDepth, canThrow) + ","); + break; + case 2: + case 3: + obj.push(getVarName() + ","); + break; + case 4: + obj.push(createObjectFunction(recurmax, stmtDepth, canThrow) + ","); + break; + default: + if (has_proto || rng(200)) { + obj.push(createObjectKey(recurmax, stmtDepth, canThrow) + ": " + createExpression(recurmax, COMMA_OK, stmtDepth, canThrow) + ","); + } else { + obj.push("__proto__: " + createExpression(recurmax, COMMA_OK, stmtDepth, canThrow) + " || {},"); + has_proto = true; + } + break; + } + obj.push("})"); + return obj.join("\n"); +} + +function createClassLiteral(recurmax, stmtDepth, canThrow, name) { + recurmax--; + var save_async = async; + var save_generator = generator; + in_class++; + var s = "class", constructor = "constructor"; + var isClazz = /^C/.test(name); + if (name) s += " " + name; + if (rng(10) == 0) { + constructor = "super"; + s += " extends "; + var p = getVarName(); + if (canThrow && rng(20) == 0) { + s += p; + } else { + s += "(typeof " + p + ' == "function" && typeof ' + p + '.prototype == "object" && ' + p + ".constructor === Function ? " + p + " : function() {})"; + } + } + s += " {\n"; + var declared = []; + for (var i = get_num(5); --i >= 0;) { + var fixed = false; + if (rng(5) == 0) { + fixed = true; + s += "static "; + } + var internal = null; + if (SUPPORT.class_private && rng(10) == 0) { + do { + internal = "#" + getDotKey(); + } while (declared.indexOf(internal) >= 0); + declared.push(internal); + } + if (SUPPORT.class_field && rng(2)) { + if (internal) { + s += internal; + } else if (fixed && bug_class_static_nontrivial) { + s += getDotKey(); + } else { + s += createObjectKey(recurmax, stmtDepth, canThrow); + } + if (rng(5)) { + async = false; + generator = false; + s += " = " + createExpression(recurmax, NO_COMMA, stmtDepth, fixed ? canThrow : CANNOT_THROW); + generator = save_generator; + async = save_async; + } + s += ";\n"; + } else if (SUPPORT.class_static_init && fixed && !internal && rng(10) == 0) { + async = false; + generator = false; + s += [ + "{ ", + createStatements(5, recurmax, canThrow, CANNOT_BREAK, CANNOT_CONTINUE, CANNOT_RETURN, stmtDepth), + " }\n", + ].join(""); + generator = save_generator; + async = save_async; + } else { + if (!fixed && !internal && constructor && rng(10) == 0) { + internal = constructor; + constructor = null; + } + s += createObjectFunction(recurmax, stmtDepth, canThrow, internal, isClazz) + "\n"; + } + } + in_class--; + generator = save_generator; + async = save_async; + return s + "}"; +} + +function createNestedBinaryExpr(recurmax, noComma, stmtDepth, canThrow) { + recurmax = 3; // note that this generates 2^recurmax expression parts... make sure to cap it + return _createSimpleBinaryExpr(recurmax, noComma, stmtDepth, canThrow); +} +function _createBinaryExpr(recurmax, noComma, stmtDepth, canThrow) { + return "(" + _createSimpleBinaryExpr(recurmax, noComma, stmtDepth, canThrow) + + createBinaryOp(noComma, canThrow) + _createSimpleBinaryExpr(recurmax, noComma, stmtDepth, canThrow) + ")"; +} +function _createSimpleBinaryExpr(recurmax, noComma, stmtDepth, canThrow) { + // intentionally generate more hardcore ops + if (--recurmax < 0) return createValue(); + var assignee, expr; + switch (rng(30)) { + case 0: + return "(c = c + 1, " + _createSimpleBinaryExpr(recurmax, noComma, stmtDepth, canThrow) + ")"; + case 1: + return "(" + createUnarySafePrefix() + "(" + _createSimpleBinaryExpr(recurmax, noComma, stmtDepth, canThrow) + "))"; + case 2: + assignee = getVarName(NO_CONST, NO_LAMBDA); + return "(" + assignee + createAssignment() + _createBinaryExpr(recurmax, noComma, stmtDepth, canThrow) + ")"; + case 3: + assignee = getVarName(); + switch (SUPPORT.destructuring ? rng(20) : 2) { + case 0: + expr = [ + "([ ", + assignee, + "[", createExpression(recurmax, COMMA_OK, stmtDepth, canThrow), "]", + " ] = [ ", + _createBinaryExpr(recurmax, noComma, stmtDepth, canThrow), + " ])", + ].join(""); + break; + case 1: + var key1 = createObjectKey(recurmax, stmtDepth, canThrow); + var key2 = /^\[/.test(key1) ? createObjectKey(recurmax, stmtDepth, canThrow) : key1; + expr = [ + "({ ", + key1, ": ", assignee, + "[", createExpression(recurmax, COMMA_OK, stmtDepth, canThrow), "]", + " } = { ", + key2, ": ", _createBinaryExpr(recurmax, noComma, stmtDepth, canThrow), + " })", + ].join(""); + break; + default: + expr = [ + "(", + assignee, + "[", createExpression(recurmax, COMMA_OK, stmtDepth, canThrow), "]", + createAssignment(), + _createBinaryExpr(recurmax, noComma, stmtDepth, canThrow), + ")", + ].join(""); + break; + } + if (in_class) return "(Object.isExtensible(" + assignee + ") && " + expr + ")"; + return canThrow && rng(20) == 0 ? expr : "(" + assignee + " && " + expr + ")"; + case 4: + assignee = getVarName(); + switch (SUPPORT.destructuring ? rng(20) : 2) { + case 0: + expr = [ + "([ ", + assignee, + ".", getDotKey(true), + " ] = [ ", + _createBinaryExpr(recurmax, noComma, stmtDepth, canThrow), + " ])", + ].join(""); + break; + case 1: + var key1 = createObjectKey(recurmax, stmtDepth, canThrow); + var key2 = /^\[/.test(key1) ? createObjectKey(recurmax, stmtDepth, canThrow) : key1; + expr = [ + "({ ", + key1, ": ", assignee, + ".", getDotKey(true), + " } = { ", + key2, ": ", _createBinaryExpr(recurmax, noComma, stmtDepth, canThrow), + " })", + ].join(""); + break; + default: + expr = [ + "(", + assignee, + ".", getDotKey(true), + createAssignment(), + _createBinaryExpr(recurmax, noComma, stmtDepth, canThrow), + ")", + ].join(""); + break; + } + if (in_class) return "(Object.isExtensible(" + assignee + ") && " + expr + ")"; + return canThrow && rng(20) == 0 ? expr : "(" + assignee + " && " + expr + ")"; + default: + return _createBinaryExpr(recurmax, noComma, stmtDepth, canThrow); + } +} + +function createTypeofExpr(recurmax, stmtDepth, canThrow) { + switch (rng(8)) { + case 0: + return "(typeof " + createVar() + ' === "' + TYPEOF_OUTCOMES[rng(TYPEOF_OUTCOMES.length)] + '")'; + case 1: + return "(typeof " + createVar() + ' !== "' + TYPEOF_OUTCOMES[rng(TYPEOF_OUTCOMES.length)] + '")'; + case 2: + return "(typeof " + createVar() + ' == "' + TYPEOF_OUTCOMES[rng(TYPEOF_OUTCOMES.length)] + '")'; + case 3: + return "(typeof " + createVar() + ' != "' + TYPEOF_OUTCOMES[rng(TYPEOF_OUTCOMES.length)] + '")'; + case 4: + return "(typeof " + createVar() + ")"; + default: + return "(typeof " + createExpression(recurmax, COMMA_OK, stmtDepth, canThrow) + ")"; + } + + function createVar() { + var save_async = async; + var save_generator = generator; + if (!async && avoid_vars.indexOf("await") >= 0) async = true; + if (!generator && avoid_vars.indexOf("yield") >= 0) generator = true; + var name = createVarName(MANDATORY, DONT_STORE); + generator = save_generator; + async = save_async; + return name; + } +} + +function createValue() { + var v; + do { + v = VALUES[rng(VALUES.length)]; + } while (v == "new.target" && rng(200) || !allow_this && v == "this"); + return v; +} + +function createBinaryOp(noComma, canThrow) { + var op; + do { + op = BINARY_OPS[rng(BINARY_OPS.length)]; + } while (noComma && op == "," || !canThrow && /^ in/.test(op)); + return op; +} + +function createAssignment() { + return ASSIGNMENTS[rng(ASSIGNMENTS.length)]; +} + +function createUnarySafePrefix() { + var prefix; + do { + prefix = UNARY_SAFE[rng(UNARY_SAFE.length)]; + } while (prefix == "delete " && in_class); + return prefix; +} + +function createUnaryPrefix() { + var prefix; + do { + prefix = UNARY_PREFIX[rng(UNARY_PREFIX.length)]; + } while (prefix == "delete " && in_class); + return prefix; +} + +function createUnaryPostfix() { + return UNARY_POSTFIX[rng(UNARY_POSTFIX.length)]; +} + +function addAvoidVar(name) { + avoid_vars.push(name); +} + +function removeAvoidVar(name) { + var index = avoid_vars.lastIndexOf(name); + if (index >= 0) avoid_vars.splice(index, 1); +} + +function isBannedKeyword(name) { + switch (name) { + case "arguments": + case "let": + return in_class; + case "await": + return async || in_class && bug_class_static_await; + case "yield": + return generator || in_class; + } +} + +function getVarName(noConst, noLambda) { + // try to get a generated name reachable from current scope. default to just `a` + var name, tries = 10; + do { + if (--tries < 0) return "a"; + name = VAR_NAMES[INITIAL_NAMES_LEN + rng(VAR_NAMES.length - INITIAL_NAMES_LEN)]; + } while (!name + || avoid_vars.indexOf(name) >= 0 + || noConst && (block_vars.indexOf(name) >= 0 + || in_class && [ + "Infinity", + "NaN", + "undefined", + ].indexOf(name) >= 0) + || noLambda && lambda_vars.indexOf(name) >= 0 + || isBannedKeyword(name)); + return name; +} + +function createVarName(maybe, dontStore) { + if (!maybe || rng(2)) { + var suffix = rng(3); + var name, tries = 10; + do { + name = VAR_NAMES[rng(VAR_NAMES.length)]; + if (--tries < 0) suffix++; + if (suffix) name += "_" + suffix; + } while (unique_vars.indexOf(name) >= 0 + || block_vars.indexOf(name) >= 0 + || isBannedKeyword(name)); + if (!dontStore) VAR_NAMES.push(name); + return name; + } + return ""; +} + +if (require.main !== module) { + exports.createTopLevelCode = function() { + var code = createTopLevelCode(); + exports.module = async && has_await; + return code; + }; + exports.num_iterations = num_iterations; + exports.verbose = verbose; + return; +} + +function run_code(code, toplevel, timeout) { + return sandbox.run_code(sandbox.patch_module_statements(code, async && has_await), toplevel, timeout); +} + +function writeln(stream, msg) { + if (typeof msg != "undefined") { + stream.write(typeof msg == "string" ? msg : msg.stack || "" + msg); + } + stream.write("\n"); +} + +function println(msg) { + writeln(process.stdout, msg); +} + +function errorln(msg) { + writeln(process.stderr, msg); +} + +function try_beautify(code, toplevel, result, printfn, options) { + var o = JSON.parse(beautify_options); + o.module = !!(async && has_await); + var beautified = UglifyJS.minify(code, o); + if (beautified.error) { + printfn("// !!! beautify failed !!!"); + printfn(beautified.error); + beautified = null; + } else if (!sandbox.same_stdout(run_code(beautified.code, toplevel), result)) { + beautified = null; + } else if (options) { + var uglified = UglifyJS.minify(beautified.code, JSON.parse(options)); + var expected, actual; + if (sandbox.is_error(uglify_code) || uglified.error) { + expected = uglify_code; + actual = uglified.error; + } else { + expected = uglify_result; + actual = run_code(uglified.code, toplevel); + } + if (!sandbox.same_stdout(expected, actual)) { + beautified = null; + } + } + if (beautified) { + printfn("// (beautified)"); + printfn(beautified.code); + } else { + printfn("//"); + printfn(code); + } +} + +var default_options = UglifyJS.default_options(); + +function log_suspects(minify_options, component) { + var options = component in minify_options ? minify_options[component] : true; + if (!options) return; + if (typeof options != "object") options = {}; + var defs = default_options[component]; + var toplevel = sandbox.has_toplevel(minify_options); + var suspects = Object.keys(defs).filter(function(name) { + var flip = component == "compress" && name == "keep_fargs"; + if (flip !== (name in options ? options : defs)[name]) { + var m = JSON.parse(JSON.stringify(minify_options)); + var o = JSON.parse(JSON.stringify(options)); + o[name] = flip; + m[component] = o; + m.validate = true; + var result = UglifyJS.minify(original_code, m); + if (sandbox.is_error(uglify_code)) { + return !sandbox.same_stdout(uglify_code, result.error); + } else if (result.error) { + errorln("Error testing options." + component + "." + name); + errorln(result.error); + } else { + var r = run_code(result.code, toplevel); + return !sandbox.same_stdout(uglify_result, r); + } + } + }); + if (suspects.length > 0) { + errorln("Suspicious " + component + " options:"); + suspects.forEach(function(name) { + errorln(" " + name); + }); + errorln(); + } +} + +function log_suspects_global(options, toplevel) { + var suspects = Object.keys(default_options).filter(function(component) { + return typeof default_options[component] != "object"; + }).filter(function(component) { + var m = JSON.parse(options); + m[component] = false; + m.validate = true; + var result = UglifyJS.minify(original_code, m); + if (sandbox.is_error(uglify_code)) { + return !sandbox.same_stdout(uglify_code, result.error); + } else if (result.error) { + errorln("Error testing options." + component); + errorln(result.error); + } else { + var r = run_code(result.code, toplevel); + return !sandbox.same_stdout(uglify_result, r); + } + }); + if (suspects.length > 0) { + errorln("Suspicious options:"); + suspects.forEach(function(name) { + errorln(" " + name); + }); + errorln(); + } +} + +function log(options) { + var toplevel = sandbox.has_toplevel(JSON.parse(options)); + if (!ok) errorln("\n\n\n\n\n\n!!!!!!!!!!\n\n\n"); + errorln("//============================================================="); + if (!ok) errorln("// !!!!!! Failed... round " + round); + errorln("// original code"); + try_beautify(original_code, toplevel, original_result, errorln, options); + errorln(); + errorln(); + errorln("//-------------------------------------------------------------"); + if (sandbox.is_error(uglify_code)) { + errorln("// !!! uglify failed !!!"); + errorln(uglify_code); + if (original_erred) { + errorln(); + errorln(); + errorln("original stacktrace:"); + errorln(original_result); + } + } else { + errorln("// uglified code"); + try_beautify(uglify_code, toplevel, uglify_result, errorln); + errorln(); + errorln(); + errorln("original result:"); + errorln(original_result); + errorln("uglified result:"); + errorln(uglify_result); + } + errorln("//-------------------------------------------------------------"); + if (!ok) { + var reduce_options = JSON.parse(options); + reduce_options.validate = true; + var reduced = reduce_test(original_code, reduce_options, { + max_timeout: max_timeout, + verbose: false, + }).code; + if (reduced) { + errorln(); + errorln("// reduced test case (output will differ)"); + errorln(); + errorln(reduced); + errorln(); + errorln("//-------------------------------------------------------------"); + } + } + errorln("minify(options):"); + errorln(JSON.stringify(JSON.parse(options), null, 2)); + errorln(); + if (!ok) { + Object.keys(default_options).filter(function(component) { + var defs = default_options[component]; + return defs && typeof defs == "object"; + }).forEach(log_suspects.bind(null, JSON.parse(options))); + log_suspects_global(options, toplevel); + errorln("!!!!!! Failed... round " + round); + } +} + +function sort_globals(code) { + var injected = "throw Object.keys(this).sort(" + function(global) { + return function(m, n) { + return (typeof global[n] == "function") - (typeof global[m] == "function") + || (m < n ? -1 : m > n ? 1 : 0); + }; + } + "(this));"; + var save_async = async; + if (async && has_await) { + async = false; + injected = [ + '"use strict";', + injected, + "(async function(){", + code, + "})();" + ].join("\n"); + } else { + injected += "\n" + code; + } + var globals = run_code(injected); + async = save_async; + if (!Array.isArray(globals)) { + errorln(); + errorln(); + errorln("//-------------------------------------------------------------"); + errorln("// !!! sort_globals() failed !!!"); + errorln("// expected Array, got:"); + if (!sandbox.is_error(globals)) try { + globals = JSON.stringify(globals); + } catch (e) {} + errorln(globals); + errorln("//"); + errorln(code); + errorln(); + return code; + } + return globals.length ? "var " + globals.map(function(name) { + return name + "=" + name; + }).join() + ";" + code : code; +} + +function fuzzy_match(original, uglified) { + var m = [], n = []; + if (collect(original, m) !== collect(uglified, n)) return false; + for (var i = 0; i < m.length; i++) { + var a = m[i]; + var b = n[i]; + if (Math.abs((b - a) / a) > 1e-10) return false; + } + return true; + + function collect(input, nums) { + return input.replace(/-?([1-9][0-9]*(\.[0-9]+)?|0\.[0-9]+)(e-?[1-9][0-9]*)?/gi, function(num) { + return "<|" + nums.push(+num) + "|>"; + }); + } +} + +function patch_proto() { + [ Array, Boolean, Error, Function, Number, Object, RegExp, String ].forEach(function(type) { + [ "toString", "valueOf" ].forEach(function(prop) { + type.prototype[prop] = function(fn) { + return function() { + try { + return fn.apply(this, arguments); + } catch (e) {} + }; + }(type.prototype[prop]); + }); + }); +} + +function is_error_timeout(ex) { + return /timed out/.test(ex.message); +} + +function is_error_in(ex) { + return ex.name == "TypeError" && /'in'/.test(ex.message); +} + +function is_error_tdz(ex) { + return ex.name == "ReferenceError"; +} + +function is_error_spread(ex) { + return ex.name == "TypeError" && /Found non-callable @@iterator| is not iterable| not a function|Symbol\(Symbol\.iterator\)/.test(ex.message); +} + +function is_error_recursion(ex) { + return ex.name == "RangeError" && /Invalid string length|Maximum call stack size exceeded/.test(ex.message); +} + +function is_error_set_property(ex) { + return ex.name == "TypeError" && /^Cannot set propert[\s\S]+? of (null|undefined)/.test(ex.message); +} + +function is_error_redeclaration(ex) { + return ex.name == "SyntaxError" && /already been declared|redeclaration/.test(ex.message); +} + +function is_error_destructuring(ex) { + return ex.name == "TypeError" && /^Cannot (destructure|read propert)/.test(ex.message); +} + +function is_error_class_constructor(ex) { + return ex.name == "TypeError" && /\bconstructors?\b/.test(ex.message) && /\bnew\b/.test(ex.message); +} + +function is_error_getter_only_property(ex) { + return ex.name == "TypeError" && [ "getter", "only", "property" ].every(function(keyword) { + return ex.message.indexOf(keyword) >= 0; + }); +} + +function patch_try_catch(orig, toplevel) { + var patched = Object.create(null); + var patches = []; + var stack = [ { + code: orig, + index: 0, + offset: 0, + tries: [], + } ]; + var tail_throw = '\nif (typeof UFUZZ_ERROR == "object") throw UFUZZ_ERROR;\n'; + var re = /(?:(?:^|[\s{}):;])try|}\s*catch\s*\(([^()[{]+)\)|}\s*finally)\s*(?={)/g; + while (stack.length) { + var code = stack[0].code; + var offset = stack[0].offset; + var tries = stack[0].tries; + var match; + re.lastIndex = stack.shift().index; + while (match = re.exec(code)) { + var index = match.index + match[0].length + 1; + if (/(?:^|[\s{}):;])try\s*$/.test(match[0])) { + tries.unshift({ try: index - offset }); + continue; + } + var insert; + if (/}\s*finally\s*$/.test(match[0])) { + tries.shift(); + insert = tail_throw; + } else { + while (tries.length && tries[0].catch) tries.shift(); + tries[0].catch = index - offset; + insert = [ + "if (!" + match[1] + ".ufuzz_var) {", + match[1] + '.ufuzz_var = "' + match[1] + '";', + match[1] + ".ufuzz_try = " + tries[0].try + ";", + match[1] + ".ufuzz_catch = " + tries[0].catch + ";", + "UFUZZ_ERROR = " + match[1] + ";", + "}", + "throw " + match[1] + ";", + ].join("\n"); + } + var new_code = code.slice(0, index) + insert + code.slice(index) + tail_throw + "var UFUZZ_ERROR;"; + var result = run_code(new_code, toplevel); + if (!sandbox.is_error(result)) { + if (!stack.filled && match[1]) stack.push({ + code: code, + index: index && index - 1, + offset: offset, + tries: JSON.parse(JSON.stringify(tries)), + }); + offset += insert.length; + code = new_code; + } else if (is_error_in(result)) { + patch(result.ufuzz_catch, result.ufuzz_var + ' = new Error("invalid `in`");'); + } else if (is_error_tdz(result)) { + patch(result.ufuzz_catch, result.ufuzz_var + ' = new Error("TDZ");'); + } else if (is_error_spread(result)) { + patch(result.ufuzz_catch, result.ufuzz_var + ' = new Error("spread not iterable");'); + } else if (is_error_recursion(result)) { + patch(result.ufuzz_try, 'throw new Error("skipping infinite recursion");'); + } else if (is_error_set_property(result)) { + patch(result.ufuzz_catch, result.ufuzz_var + ' = new Error("cannot set property");'); + } else if (is_error_destructuring(result)) { + patch(result.ufuzz_catch, result.ufuzz_var + ' = new Error("cannot destructure");'); + } else if (is_error_class_constructor(result)) { + patch(result.ufuzz_catch, result.ufuzz_var + ' = new Error("missing new for class");'); + } else if (is_error_getter_only_property(result)) { + patch(result.ufuzz_catch, result.ufuzz_var + ' = new Error("setting getter-only property");'); + } + } + stack.filled = true; + } + if (patches.length) return patches.reduce(function(code, patch) { + var index = patch[0]; + return code.slice(0, index) + patch[1] + code.slice(index); + }, orig); + + function patch(index, code) { + if (patched[index]) return; + patched[index] = true; + patches.unshift([ index, code ]); + } +} + +var beautify_options = { + compress: false, + mangle: false, + output: { + beautify: true, + braces: true, + }, +}; +var minify_options = require("./options.json"); +if (sandbox.is_error(sandbox.run_code("A:if (0) B:; else B:;"))) { + minify_options.forEach(function(o) { + if (!("mangle" in o)) o.mangle = {}; + if (o.mangle) o.mangle.v8 = true; + }); +} +var bug_async_arrow_rest = function() {}; +if (SUPPORT.arrow && SUPPORT.async && SUPPORT.rest && sandbox.is_error(sandbox.run_code("async (a = f(...[], b)) => 0;"))) { + bug_async_arrow_rest = function(ex) { + return ex.name == "SyntaxError" && ex.message == "Rest parameter must be last formal parameter"; + }; +} +var bug_class_static_await = SUPPORT.async && SUPPORT.class_field && sandbox.is_error(sandbox.run_code("var await; class A { static p = await; }")); +var bug_class_static_nontrivial = SUPPORT.class_field && sandbox.is_error(sandbox.run_code("class A { static 42; static get 42() {} }")); +var bug_for_of_async = SUPPORT.for_await_of && sandbox.is_error(sandbox.run_code("var async; for (async of []);")); +var bug_for_of_var = SUPPORT.for_of && SUPPORT.let && sandbox.is_error(sandbox.run_code("try {} catch (e) { for (var e of []); }")); +var bug_proto_stream = function(ex) { + return ex.name == "TypeError" && ex.message == "callback is not a function"; +} +if (SUPPORT.destructuring && sandbox.is_error(sandbox.run_code("console.log([ 1 ], {} = 2);"))) { + beautify_options.output.v8 = true; + minify_options.forEach(function(o) { + if (!("output" in o)) o.output = {}; + o.output.v8 = true; + }); +} +beautify_options = JSON.stringify(beautify_options); +minify_options = minify_options.map(JSON.stringify); +var original_code, original_result, original_erred; +var uglify_code, uglify_result, ok; +var max_timeout = 10000; +for (var round = 1; round <= num_iterations; round++) { + process.stdout.write(round + " of " + num_iterations + "\r"); + + original_code = createTopLevelCode(); + var orig_result = [ run_code(original_code), run_code(original_code, true) ]; + if (orig_result.some(function(result, toplevel) { + if (!sandbox.is_error(result)) return; + println(); + println(); + println("//============================================================="); + println("// original code" + (toplevel ? " (toplevel)" : "")); + try_beautify(original_code, toplevel, result, println); + println(); + println(); + println("original result:"); + println(result); + println(); + if (is_error_timeout(result)) orig_result[toplevel] = result = run_code(original_code, toplevel, max_timeout); + // skip over test cases which take too long to run + if (is_error_timeout(result)) return true; + // ignore v8 parser bug + if (bug_async_arrow_rest(result)) return true; + // ignore Node.js `__proto__` quirks + if (bug_proto_stream(result)) return true; + // ignore runtime platform bugs + if (result.message == "Script execution aborted.") return true; + })) { + num_iterations++; + continue; + } + minify_options.forEach(function(options) { + var o = JSON.parse(options); + if (async && has_await) { + o.module = true; + options = JSON.stringify(o); + } + var toplevel = sandbox.has_toplevel(o); + o.validate = true; + uglify_code = UglifyJS.minify(original_code, o); + original_result = orig_result[toplevel ? 1 : 0]; + original_erred = sandbox.is_error(original_result); + if (!uglify_code.error) { + uglify_code = uglify_code.code; + uglify_result = run_code(uglify_code, toplevel); + ok = sandbox.same_stdout(original_result, uglify_result); + var uglify_erred = sandbox.is_error(uglify_result); + // ignore v8 parser bug + if (!ok && uglify_erred && bug_async_arrow_rest(uglify_result)) ok = true; + // ignore Node.js `__proto__` quirks + if (!ok && uglify_erred && bug_proto_stream(uglify_result)) ok = true; + // ignore runtime platform bugs + if (!ok && uglify_erred && uglify_result.message == "Script execution aborted.") ok = true; + // ignore spurious time-outs + if (!ok && uglify_erred && is_error_timeout(uglify_result)) { + var waited_result = run_code(uglify_code, toplevel, max_timeout); + ok = sandbox.same_stdout(original_result, waited_result); + } + // ignore declaration order of global variables + if (!ok && !toplevel) { + if (!(original_erred && original_result.name == "SyntaxError") && !(uglify_erred && uglify_result.name == "SyntaxError")) { + ok = sandbox.same_stdout(run_code(sort_globals(original_code)), run_code(sort_globals(uglify_code))); + } + } + // ignore numerical imprecision caused by `unsafe_math` + if (!ok && o.compress && o.compress.unsafe_math) { + if (typeof original_result == "string" && typeof uglify_result == "string") { + ok = fuzzy_match(original_result, uglify_result); + } else if (original_erred && uglify_erred) { + ok = original_result.name == uglify_result.name && fuzzy_match(original_result.message, uglify_result.message); + } + if (!ok) { + var fuzzy_result = run_code(original_code.replace(/( - 0\.1){3}/g, " - 0.3"), toplevel); + ok = sandbox.same_stdout(fuzzy_result, uglify_result); + } + } + if (!ok && original_erred && uglify_erred && ( + // ignore difference in error message caused by `in` + is_error_in(original_result) && is_error_in(uglify_result) + // ignore difference in error message caused by Temporal Dead Zone + || is_error_tdz(original_result) && is_error_tdz(uglify_result) + // ignore difference in error message caused by spread syntax + || is_error_spread(original_result) && is_error_spread(uglify_result) + // ignore difference in error message caused by destructuring assignment + || is_error_set_property(original_result) && is_error_set_property(uglify_result) + // ignore difference in error message caused by `import` symbol redeclaration + || /\bimport\b/.test(original_code) && is_error_redeclaration(original_result) && is_error_redeclaration(uglify_result) + // ignore difference in error message caused by destructuring + || is_error_destructuring(original_result) && is_error_destructuring(uglify_result) + // ignore difference in error message caused by call on class + || is_error_class_constructor(original_result) && is_error_class_constructor(uglify_result) + // ignore difference in error message caused by setting getter-only property + || is_error_getter_only_property(original_result) && is_error_getter_only_property(uglify_result) + )) ok = true; + // ignore difference due to `__proto__` assignment + if (!ok && /\b__proto__\b/.test(original_code)) { + var original_proto = run_code("(" + patch_proto + ")();\n" + original_code, toplevel); + var uglify_proto = run_code("(" + patch_proto + ")();\n" + uglify_code, toplevel); + ok = sandbox.same_stdout(original_proto, uglify_proto); + } + // ignore difference in depth of termination caused by infinite recursion + if (!ok && original_erred && is_error_recursion(original_result)) { + if (!uglify_erred || is_error_recursion(uglify_result)) ok = true; + } + // ignore errors above when caught by try-catch + if (!ok) { + var orig_skipped = patch_try_catch(original_code, toplevel); + var uglify_skipped = patch_try_catch(uglify_code, toplevel); + if (orig_skipped && uglify_skipped) { + ok = sandbox.same_stdout(run_code(orig_skipped, toplevel), run_code(uglify_skipped, toplevel)); + } + } + } else { + uglify_code = uglify_code.error; + ok = original_erred && uglify_code.name == original_result.name; + } + if (verbose || (verbose_interval && !(round % INTERVAL_COUNT)) || !ok) log(options); + if (!ok && isFinite(num_iterations)) { + println(); + process.exit(1); + } + }); +} +println(); diff --git a/test/ufuzz/job.js b/test/ufuzz/job.js new file mode 100644 index 00000000000..5cef23e97af --- /dev/null +++ b/test/ufuzz/job.js @@ -0,0 +1,78 @@ +var actions = require("./actions"); +var child_process = require("child_process"); + +var args = [ + "--max-old-space-size=2048", + "test/ufuzz", +]; +var iterations; +switch (process.argv.length) { + case 3: + iterations = +process.argv[2]; + args.push(iterations); + break; + case 5: + actions.init(process.argv[2], process.argv[3], +process.argv[4]); + break; + default: + throw new Error("invalid parameters"); +} +var tasks = [ run(), run() ]; +if (iterations) return; +var alive = setInterval(function() { + actions.should_stop(function() { + clearInterval(alive); + tasks.forEach(function(kill) { + kill(); + }); + }); +}, 8 * 60 * 1000); + +function run() { + var child, stdout, stderr, log; + spawn(); + return function() { + clearInterval(log); + child.removeListener("exit", respawn); + child.kill(); + }; + + function spawn() { + child = child_process.spawn("node", args, { + stdio: [ "ignore", "pipe", "pipe" ] + }).on("exit", respawn); + stdout = ""; + child.stdout.on("data", function(data) { + stdout += data; + }); + stderr = ""; + child.stderr.on("data", trap).pipe(process.stdout); + log = setInterval(function() { + stdout = stdout.replace(/[^\r\n]+\r(?=[^\r\n]+\r)/g, ""); + var end = stdout.lastIndexOf("\r"); + if (end < 0) return; + console.log(stdout.slice(0, end)); + stdout = stdout.slice(end + 1); + }, 5 * 60 * 1000); + } + + function respawn() { + console.log(stdout.replace(/[^\r\n]*\r/g, "")); + clearInterval(log); + if (!iterations) { + spawn(); + } else if (process.exitCode) { + tasks.forEach(function(kill) { + kill(); + }); + } + } + + function trap(data) { + stderr += data; + if (~stderr.indexOf("!!!!!! Failed... round ")) { + process.exitCode = 1; + child.stderr.removeListener("data", trap); + } + } +} diff --git a/test/ufuzz/options.json b/test/ufuzz/options.json new file mode 100644 index 00000000000..ef479f73c8b --- /dev/null +++ b/test/ufuzz/options.json @@ -0,0 +1,52 @@ +[ + { + "compress": false, + "mangle": false, + "module": false, + "output": { + "beautify": true, + "braces": true + }, + "rename": true + }, + { + "compress": false, + "module": false + }, + { + "mangle": false, + "module": false + }, + { + "module": false + }, + { + "ie": true, + "module": false, + "toplevel": true + }, + { + "compress": { + "hoist_vars": true, + "keep_infinity": true, + "passes": 1e6, + "unsafe": true + }, + "keep_fargs": true, + "keep_fnames": true, + "module": false, + "toplevel": true + }, + { + "compress": { + "passes": 1e6, + "sequences": 1e6, + "unsafe": true, + "unsafe_Function": true, + "unsafe_math": true, + "unsafe_proto": true, + "unsafe_regexp": true + }, + "module": false + } +] diff --git a/tools/domprops.html b/tools/domprops.html new file mode 100644 index 00000000000..e217b1731ed --- /dev/null +++ b/tools/domprops.html @@ -0,0 +1,456 @@ + + + + + + diff --git a/tools/domprops.json b/tools/domprops.json new file mode 100644 index 00000000000..1045429dc00 --- /dev/null +++ b/tools/domprops.json @@ -0,0 +1,8325 @@ +[ + "$&", + "$'", + "$*", + "$+", + "$1", + "$2", + "$3", + "$4", + "$5", + "$6", + "$7", + "$8", + "$9", + "$_", + "$`", + "$input", + "-moz-animation", + "-moz-animation-delay", + "-moz-animation-direction", + "-moz-animation-duration", + "-moz-animation-fill-mode", + "-moz-animation-iteration-count", + "-moz-animation-name", + "-moz-animation-play-state", + "-moz-animation-timing-function", + "-moz-appearance", + "-moz-backface-visibility", + "-moz-binding", + "-moz-border-end", + "-moz-border-end-color", + "-moz-border-end-style", + "-moz-border-end-width", + "-moz-border-image", + "-moz-border-start", + "-moz-border-start-color", + "-moz-border-start-style", + "-moz-border-start-width", + "-moz-box-align", + "-moz-box-direction", + "-moz-box-flex", + "-moz-box-ordinal-group", + "-moz-box-orient", + "-moz-box-pack", + "-moz-box-sizing", + "-moz-column-count", + "-moz-column-fill", + "-moz-column-gap", + "-moz-column-rule", + "-moz-column-rule-color", + "-moz-column-rule-style", + "-moz-column-rule-width", + "-moz-column-width", + "-moz-columns", + "-moz-float-edge", + "-moz-font-feature-settings", + "-moz-font-language-override", + "-moz-force-broken-image-icon", + "-moz-hyphens", + "-moz-image-region", + "-moz-margin-end", + "-moz-margin-start", + "-moz-orient", + "-moz-outline-radius", + "-moz-outline-radius-bottomleft", + "-moz-outline-radius-bottomright", + "-moz-outline-radius-topleft", + "-moz-outline-radius-topright", + "-moz-padding-end", + "-moz-padding-start", + "-moz-perspective", + "-moz-perspective-origin", + "-moz-stack-sizing", + "-moz-tab-size", + "-moz-text-size-adjust", + "-moz-transform", + "-moz-transform-origin", + "-moz-transform-style", + "-moz-transition", + "-moz-transition-delay", + "-moz-transition-duration", + "-moz-transition-property", + "-moz-transition-timing-function", + "-moz-user-focus", + "-moz-user-input", + "-moz-user-modify", + "-moz-user-select", + "-moz-window-dragging", + "-webkit-align-content", + "-webkit-align-items", + "-webkit-align-self", + "-webkit-animation", + "-webkit-animation-delay", + "-webkit-animation-direction", + "-webkit-animation-duration", + "-webkit-animation-fill-mode", + "-webkit-animation-iteration-count", + "-webkit-animation-name", + "-webkit-animation-play-state", + "-webkit-animation-timing-function", + "-webkit-appearance", + "-webkit-backface-visibility", + "-webkit-background-clip", + "-webkit-background-origin", + "-webkit-background-size", + "-webkit-border-bottom-left-radius", + "-webkit-border-bottom-right-radius", + "-webkit-border-image", + "-webkit-border-radius", + "-webkit-border-top-left-radius", + "-webkit-border-top-right-radius", + "-webkit-box-align", + "-webkit-box-direction", + "-webkit-box-flex", + "-webkit-box-ordinal-group", + "-webkit-box-orient", + "-webkit-box-pack", + "-webkit-box-shadow", + "-webkit-box-sizing", + "-webkit-filter", + "-webkit-flex", + "-webkit-flex-basis", + "-webkit-flex-direction", + "-webkit-flex-flow", + "-webkit-flex-grow", + "-webkit-flex-shrink", + "-webkit-flex-wrap", + "-webkit-justify-content", + "-webkit-line-clamp", + "-webkit-mask", + "-webkit-mask-clip", + "-webkit-mask-composite", + "-webkit-mask-image", + "-webkit-mask-origin", + "-webkit-mask-position", + "-webkit-mask-position-x", + "-webkit-mask-position-y", + "-webkit-mask-repeat", + "-webkit-mask-size", + "-webkit-order", + "-webkit-perspective", + "-webkit-perspective-origin", + "-webkit-text-fill-color", + "-webkit-text-size-adjust", + "-webkit-text-stroke", + "-webkit-text-stroke-color", + "-webkit-text-stroke-width", + "-webkit-transform", + "-webkit-transform-origin", + "-webkit-transform-style", + "-webkit-transition", + "-webkit-transition-delay", + "-webkit-transition-duration", + "-webkit-transition-property", + "-webkit-transition-timing-function", + "-webkit-user-select", + "0", + "1", + "10", + "11", + "12", + "13", + "14", + "15", + "16", + "17", + "18", + "19", + "2", + "20", + "21", + "22", + "23", + "24", + "25", + "26", + "27", + "28", + "29", + "3", + "30", + "31", + "32", + "33", + "34", + "35", + "36", + "37", + "38", + "39", + "4", + "40", + "41", + "42", + "43", + "44", + "45", + "46", + "47", + "48", + "49", + "5", + "50", + "51", + "6", + "7", + "8", + "9", + "@@iterator", + "ABORT_ERR", + "ACTIVE", + "ACTIVE_ATTRIBUTES", + "ACTIVE_TEXTURE", + "ACTIVE_UNIFORMS", + "ACTIVE_UNIFORM_BLOCKS", + "ADDITION", + "ALIASED_LINE_WIDTH_RANGE", + "ALIASED_POINT_SIZE_RANGE", + "ALLOW_KEYBOARD_INPUT", + "ALLPASS", + "ALPHA", + "ALPHA_BITS", + "ALREADY_SIGNALED", + "ALT_MASK", + "ALWAYS", + "ANDROID", + "ANGLE_instanced_arrays", + "ANY_SAMPLES_PASSED", + "ANY_SAMPLES_PASSED_CONSERVATIVE", + "ANY_TYPE", + "ANY_UNORDERED_NODE_TYPE", + "APP_UPDATE", + "ARM", + "ARRAY_BUFFER", + "ARRAY_BUFFER_BINDING", + "ATTACHED_SHADERS", + "ATTRIBUTE_NODE", + "AT_TARGET", + "AbortController", + "AbortSignal", + "AbsoluteOrientationSensor", + "AbstractRange", + "Accelerometer", + "ActiveXObject", + "AddSearchProvider", + "AesGcmEncryptResult", + "AggregateError", + "AnalyserNode", + "Animation", + "AnimationEffect", + "AnimationEvent", + "AnimationPlaybackEvent", + "AnimationTimeline", + "AnonXMLHttpRequest", + "AppBannerPromptResult", + "ApplicationCache", + "ApplicationCacheErrorEvent", + "Array", + "ArrayBuffer", + "Atomics", + "Attr", + "Audio", + "AudioBuffer", + "AudioBufferSourceNode", + "AudioContext", + "AudioDestinationNode", + "AudioListener", + "AudioNode", + "AudioParam", + "AudioParamMap", + "AudioProcessingEvent", + "AudioScheduledSourceNode", + "AudioStreamTrack", + "AudioTrack", + "AudioTrackList", + "AudioWorklet", + "AudioWorkletNode", + "AuthenticatorAssertionResponse", + "AuthenticatorAttestationResponse", + "AuthenticatorResponse", + "AutocompleteErrorEvent", + "BACK", + "BAD_BOUNDARYPOINTS_ERR", + "BAD_REQUEST", + "BANDPASS", + "BLEND", + "BLEND_COLOR", + "BLEND_DST_ALPHA", + "BLEND_DST_RGB", + "BLEND_EQUATION", + "BLEND_EQUATION_ALPHA", + "BLEND_EQUATION_RGB", + "BLEND_SRC_ALPHA", + "BLEND_SRC_RGB", + "BLUE_BITS", + "BLUR", + "BOOL", + "BOOLEAN_TYPE", + "BOOL_VEC2", + "BOOL_VEC3", + "BOOL_VEC4", + "BOTH", + "BROWSER_DEFAULT_WEBGL", + "BUBBLING_PHASE", + "BUFFER_SIZE", + "BUFFER_USAGE", + "BYTE", + "BYTES_PER_ELEMENT", + "BackgroundFetchManager", + "BackgroundFetchRecord", + "BackgroundFetchRegistration", + "BarProp", + "BarcodeDetector", + "BaseAudioContext", + "BaseHref", + "BatteryManager", + "BeforeInstallPromptEvent", + "BeforeLoadEvent", + "BeforeUnloadEvent", + "BigInt", + "BigInt64Array", + "BigUint64Array", + "BiquadFilterNode", + "Blob", + "BlobEvent", + "Bluetooth", + "BluetoothCharacteristicProperties", + "BluetoothDevice", + "BluetoothRemoteGATTCharacteristic", + "BluetoothRemoteGATTDescriptor", + "BluetoothRemoteGATTServer", + "BluetoothRemoteGATTService", + "BluetoothUUID", + "BookmarkCollection", + "Boolean", + "BroadcastChannel", + "ByteLengthQueuingStrategy", + "CANNOT_RUN", + "CAPTURING_PHASE", + "CCW", + "CDATASection", + "CDATA_SECTION_NODE", + "CHANGE", + "CHARSET_RULE", + "CHECKING", + "CHROME_UPDATE", + "CLAMP_TO_EDGE", + "CLICK", + "CLOSED", + "CLOSING", + "COLOR", + "COLOR_ATTACHMENT0", + "COLOR_ATTACHMENT1", + "COLOR_ATTACHMENT10", + "COLOR_ATTACHMENT11", + "COLOR_ATTACHMENT12", + "COLOR_ATTACHMENT13", + "COLOR_ATTACHMENT14", + "COLOR_ATTACHMENT15", + "COLOR_ATTACHMENT2", + "COLOR_ATTACHMENT3", + "COLOR_ATTACHMENT4", + "COLOR_ATTACHMENT5", + "COLOR_ATTACHMENT6", + "COLOR_ATTACHMENT7", + "COLOR_ATTACHMENT8", + "COLOR_ATTACHMENT9", + "COLOR_BUFFER_BIT", + "COLOR_CLEAR_VALUE", + "COLOR_WRITEMASK", + "COMMENT_NODE", + "COMPARE_REF_TO_TEXTURE", + "COMPILE_STATUS", + "COMPRESSED_RGBA_S3TC_DXT1_EXT", + "COMPRESSED_RGBA_S3TC_DXT3_EXT", + "COMPRESSED_RGBA_S3TC_DXT5_EXT", + "COMPRESSED_RGB_S3TC_DXT1_EXT", + "COMPRESSED_TEXTURE_FORMATS", + "CONDITION_SATISFIED", + "CONFIGURATION_UNSUPPORTED", + "CONNECTING", + "CONSTANT_ALPHA", + "CONSTANT_COLOR", + "CONSTRAINT_ERR", + "CONTENT", + "CONTEXT_LOST_WEBGL", + "CONTROL_MASK", + "COPY_READ_BUFFER", + "COPY_READ_BUFFER_BINDING", + "COPY_WRITE_BUFFER", + "COPY_WRITE_BUFFER_BINDING", + "COUNTER_STYLE_RULE", + "CROS", + "CSS", + "CSS2Properties", + "CSSAnimation", + "CSSCharsetRule", + "CSSConditionRule", + "CSSCounterStyleRule", + "CSSFontFaceRule", + "CSSFontFeatureValuesRule", + "CSSGroupingRule", + "CSSImageValue", + "CSSImportRule", + "CSSKeyframeRule", + "CSSKeyframesRule", + "CSSKeywordValue", + "CSSMathInvert", + "CSSMathMax", + "CSSMathMin", + "CSSMathNegate", + "CSSMathProduct", + "CSSMathSum", + "CSSMathValue", + "CSSMatrixComponent", + "CSSMediaRule", + "CSSMozDocumentRule", + "CSSNameSpaceRule", + "CSSNamespaceRule", + "CSSNumericArray", + "CSSNumericValue", + "CSSPageRule", + "CSSPerspective", + "CSSPositionValue", + "CSSPrimitiveValue", + "CSSRotate", + "CSSRule", + "CSSRuleList", + "CSSScale", + "CSSSkew", + "CSSSkewX", + "CSSSkewY", + "CSSStyleDeclaration", + "CSSStyleRule", + "CSSStyleSheet", + "CSSStyleValue", + "CSSSupportsRule", + "CSSTransformComponent", + "CSSTransformValue", + "CSSTransition", + "CSSTranslate", + "CSSUnitValue", + "CSSUnknownRule", + "CSSUnparsedValue", + "CSSValue", + "CSSValueList", + "CSSVariableReferenceValue", + "CSSVariablesDeclaration", + "CSSVariablesRule", + "CSSViewportRule", + "CSS_ATTR", + "CSS_CM", + "CSS_COUNTER", + "CSS_CUSTOM", + "CSS_DEG", + "CSS_DIMENSION", + "CSS_EMS", + "CSS_EXS", + "CSS_FILTER_BLUR", + "CSS_FILTER_BRIGHTNESS", + "CSS_FILTER_CONTRAST", + "CSS_FILTER_CUSTOM", + "CSS_FILTER_DROP_SHADOW", + "CSS_FILTER_GRAYSCALE", + "CSS_FILTER_HUE_ROTATE", + "CSS_FILTER_INVERT", + "CSS_FILTER_OPACITY", + "CSS_FILTER_REFERENCE", + "CSS_FILTER_SATURATE", + "CSS_FILTER_SEPIA", + "CSS_GRAD", + "CSS_HZ", + "CSS_IDENT", + "CSS_IN", + "CSS_INHERIT", + "CSS_KHZ", + "CSS_MATRIX", + "CSS_MATRIX3D", + "CSS_MM", + "CSS_MS", + "CSS_NUMBER", + "CSS_PC", + "CSS_PERCENTAGE", + "CSS_PERSPECTIVE", + "CSS_PRIMITIVE_VALUE", + "CSS_PT", + "CSS_PX", + "CSS_RAD", + "CSS_RECT", + "CSS_RGBCOLOR", + "CSS_ROTATE", + "CSS_ROTATE3D", + "CSS_ROTATEX", + "CSS_ROTATEY", + "CSS_ROTATEZ", + "CSS_S", + "CSS_SCALE", + "CSS_SCALE3D", + "CSS_SCALEX", + "CSS_SCALEY", + "CSS_SCALEZ", + "CSS_SKEW", + "CSS_SKEWX", + "CSS_SKEWY", + "CSS_STRING", + "CSS_TRANSLATE", + "CSS_TRANSLATE3D", + "CSS_TRANSLATEX", + "CSS_TRANSLATEY", + "CSS_TRANSLATEZ", + "CSS_UNKNOWN", + "CSS_URI", + "CSS_VALUE_LIST", + "CSS_VH", + "CSS_VMAX", + "CSS_VMIN", + "CSS_VW", + "CULL_FACE", + "CULL_FACE_MODE", + "CURRENT_PROGRAM", + "CURRENT_QUERY", + "CURRENT_VERTEX_ATTRIB", + "CUSTOM", + "CW", + "Cache", + "CacheStorage", + "CanvasCaptureMediaStream", + "CanvasCaptureMediaStreamTrack", + "CanvasGradient", + "CanvasPattern", + "CanvasPixelArray", + "CanvasRenderingContext2D", + "CaretPosition", + "ChannelMergerNode", + "ChannelSplitterNode", + "CharacterData", + "Chrome PDF Plugin", + "Chrome PDF Viewer", + "ClientRect", + "ClientRectList", + "Clipboard", + "ClipboardEvent", + "ClipboardItem", + "CloseEvent", + "Collator", + "CollectGarbage", + "CommandEvent", + "Comment", + "CompileError", + "CompositionEvent", + "CompressionStream", + "Console", + "ConstantSourceNode", + "ControlRangeCollection", + "Controllers", + "ConvolverNode", + "Coordinates", + "CountQueuingStrategy", + "Counter", + "Credential", + "CredentialsContainer", + "Crypto", + "CryptoKey", + "CryptoOperation", + "CustomElementRegistry", + "CustomEvent", + "DATABASE_ERR", + "DATA_CLONE_ERR", + "DATA_ERR", + "DBLCLICK", + "DECR", + "DECR_WRAP", + "DELETE_STATUS", + "DEPTH", + "DEPTH24_STENCIL8", + "DEPTH32F_STENCIL8", + "DEPTH_ATTACHMENT", + "DEPTH_BITS", + "DEPTH_BUFFER_BIT", + "DEPTH_CLEAR_VALUE", + "DEPTH_COMPONENT", + "DEPTH_COMPONENT16", + "DEPTH_COMPONENT24", + "DEPTH_COMPONENT32F", + "DEPTH_FUNC", + "DEPTH_RANGE", + "DEPTH_STENCIL", + "DEPTH_STENCIL_ATTACHMENT", + "DEPTH_TEST", + "DEPTH_WRITEMASK", + "DEVICE_INELIGIBLE", + "DIRECTION_DOWN", + "DIRECTION_LEFT", + "DIRECTION_RIGHT", + "DIRECTION_UP", + "DISABLED", + "DISPATCH_REQUEST_ERR", + "DITHER", + "DOCUMENT_FRAGMENT_NODE", + "DOCUMENT_NODE", + "DOCUMENT_POSITION_CONTAINED_BY", + "DOCUMENT_POSITION_CONTAINS", + "DOCUMENT_POSITION_DISCONNECTED", + "DOCUMENT_POSITION_FOLLOWING", + "DOCUMENT_POSITION_IMPLEMENTATION_SPECIFIC", + "DOCUMENT_POSITION_PRECEDING", + "DOCUMENT_TYPE_NODE", + "DOMCursor", + "DOMError", + "DOMException", + "DOMImplementation", + "DOMImplementationLS", + "DOMMatrix", + "DOMMatrixReadOnly", + "DOMParser", + "DOMPoint", + "DOMPointReadOnly", + "DOMQuad", + "DOMRect", + "DOMRectList", + "DOMRectReadOnly", + "DOMRequest", + "DOMSTRING_SIZE_ERR", + "DOMSettableTokenList", + "DOMStringList", + "DOMStringMap", + "DOMTokenList", + "DOMTransactionEvent", + "DOM_DELTA_LINE", + "DOM_DELTA_PAGE", + "DOM_DELTA_PIXEL", + "DOM_INPUT_METHOD_DROP", + "DOM_INPUT_METHOD_HANDWRITING", + "DOM_INPUT_METHOD_IME", + "DOM_INPUT_METHOD_KEYBOARD", + "DOM_INPUT_METHOD_MULTIMODAL", + "DOM_INPUT_METHOD_OPTION", + "DOM_INPUT_METHOD_PASTE", + "DOM_INPUT_METHOD_SCRIPT", + "DOM_INPUT_METHOD_UNKNOWN", + "DOM_INPUT_METHOD_VOICE", + "DOM_KEY_LOCATION_JOYSTICK", + "DOM_KEY_LOCATION_LEFT", + "DOM_KEY_LOCATION_MOBILE", + "DOM_KEY_LOCATION_NUMPAD", + "DOM_KEY_LOCATION_RIGHT", + "DOM_KEY_LOCATION_STANDARD", + "DOM_VK_0", + "DOM_VK_1", + "DOM_VK_2", + "DOM_VK_3", + "DOM_VK_4", + "DOM_VK_5", + "DOM_VK_6", + "DOM_VK_7", + "DOM_VK_8", + "DOM_VK_9", + "DOM_VK_A", + "DOM_VK_ACCEPT", + "DOM_VK_ADD", + "DOM_VK_ALT", + "DOM_VK_ALTGR", + "DOM_VK_AMPERSAND", + "DOM_VK_ASTERISK", + "DOM_VK_AT", + "DOM_VK_ATTN", + "DOM_VK_B", + "DOM_VK_BACKSPACE", + "DOM_VK_BACK_QUOTE", + "DOM_VK_BACK_SLASH", + "DOM_VK_BACK_SPACE", + "DOM_VK_C", + "DOM_VK_CANCEL", + "DOM_VK_CAPS_LOCK", + "DOM_VK_CIRCUMFLEX", + "DOM_VK_CLEAR", + "DOM_VK_CLOSE_BRACKET", + "DOM_VK_CLOSE_CURLY_BRACKET", + "DOM_VK_CLOSE_PAREN", + "DOM_VK_COLON", + "DOM_VK_COMMA", + "DOM_VK_CONTEXT_MENU", + "DOM_VK_CONTROL", + "DOM_VK_CONVERT", + "DOM_VK_CRSEL", + "DOM_VK_CTRL", + "DOM_VK_D", + "DOM_VK_DECIMAL", + "DOM_VK_DELETE", + "DOM_VK_DIVIDE", + "DOM_VK_DOLLAR", + "DOM_VK_DOUBLE_QUOTE", + "DOM_VK_DOWN", + "DOM_VK_E", + "DOM_VK_EISU", + "DOM_VK_END", + "DOM_VK_ENTER", + "DOM_VK_EQUALS", + "DOM_VK_EREOF", + "DOM_VK_ESCAPE", + "DOM_VK_EXCLAMATION", + "DOM_VK_EXECUTE", + "DOM_VK_EXSEL", + "DOM_VK_F", + "DOM_VK_F1", + "DOM_VK_F10", + "DOM_VK_F11", + "DOM_VK_F12", + "DOM_VK_F13", + "DOM_VK_F14", + "DOM_VK_F15", + "DOM_VK_F16", + "DOM_VK_F17", + "DOM_VK_F18", + "DOM_VK_F19", + "DOM_VK_F2", + "DOM_VK_F20", + "DOM_VK_F21", + "DOM_VK_F22", + "DOM_VK_F23", + "DOM_VK_F24", + "DOM_VK_F25", + "DOM_VK_F26", + "DOM_VK_F27", + "DOM_VK_F28", + "DOM_VK_F29", + "DOM_VK_F3", + "DOM_VK_F30", + "DOM_VK_F31", + "DOM_VK_F32", + "DOM_VK_F33", + "DOM_VK_F34", + "DOM_VK_F35", + "DOM_VK_F36", + "DOM_VK_F4", + "DOM_VK_F5", + "DOM_VK_F6", + "DOM_VK_F7", + "DOM_VK_F8", + "DOM_VK_F9", + "DOM_VK_FINAL", + "DOM_VK_FRONT", + "DOM_VK_G", + "DOM_VK_GREATER_THAN", + "DOM_VK_H", + "DOM_VK_HANGUL", + "DOM_VK_HANJA", + "DOM_VK_HASH", + "DOM_VK_HELP", + "DOM_VK_HK_TOGGLE", + "DOM_VK_HOME", + "DOM_VK_HYPHEN_MINUS", + "DOM_VK_I", + "DOM_VK_INSERT", + "DOM_VK_J", + "DOM_VK_JUNJA", + "DOM_VK_K", + "DOM_VK_KANA", + "DOM_VK_KANJI", + "DOM_VK_L", + "DOM_VK_LEFT", + "DOM_VK_LEFT_TAB", + "DOM_VK_LESS_THAN", + "DOM_VK_M", + "DOM_VK_META", + "DOM_VK_MODECHANGE", + "DOM_VK_MULTIPLY", + "DOM_VK_N", + "DOM_VK_NONCONVERT", + "DOM_VK_NUMPAD0", + "DOM_VK_NUMPAD1", + "DOM_VK_NUMPAD2", + "DOM_VK_NUMPAD3", + "DOM_VK_NUMPAD4", + "DOM_VK_NUMPAD5", + "DOM_VK_NUMPAD6", + "DOM_VK_NUMPAD7", + "DOM_VK_NUMPAD8", + "DOM_VK_NUMPAD9", + "DOM_VK_NUM_LOCK", + "DOM_VK_O", + "DOM_VK_OEM_1", + "DOM_VK_OEM_102", + "DOM_VK_OEM_2", + "DOM_VK_OEM_3", + "DOM_VK_OEM_4", + "DOM_VK_OEM_5", + "DOM_VK_OEM_6", + "DOM_VK_OEM_7", + "DOM_VK_OEM_8", + "DOM_VK_OEM_COMMA", + "DOM_VK_OEM_MINUS", + "DOM_VK_OEM_PERIOD", + "DOM_VK_OEM_PLUS", + "DOM_VK_OPEN_BRACKET", + "DOM_VK_OPEN_CURLY_BRACKET", + "DOM_VK_OPEN_PAREN", + "DOM_VK_P", + "DOM_VK_PA1", + "DOM_VK_PAGEDOWN", + "DOM_VK_PAGEUP", + "DOM_VK_PAGE_DOWN", + "DOM_VK_PAGE_UP", + "DOM_VK_PAUSE", + "DOM_VK_PERCENT", + "DOM_VK_PERIOD", + "DOM_VK_PIPE", + "DOM_VK_PLAY", + "DOM_VK_PLUS", + "DOM_VK_PRINT", + "DOM_VK_PRINTSCREEN", + "DOM_VK_PROCESSKEY", + "DOM_VK_PROPERITES", + "DOM_VK_Q", + "DOM_VK_QUESTION_MARK", + "DOM_VK_QUOTE", + "DOM_VK_R", + "DOM_VK_REDO", + "DOM_VK_RETURN", + "DOM_VK_RIGHT", + "DOM_VK_S", + "DOM_VK_SCROLL_LOCK", + "DOM_VK_SELECT", + "DOM_VK_SEMICOLON", + "DOM_VK_SEPARATOR", + "DOM_VK_SHIFT", + "DOM_VK_SLASH", + "DOM_VK_SLEEP", + "DOM_VK_SPACE", + "DOM_VK_SUBTRACT", + "DOM_VK_T", + "DOM_VK_TAB", + "DOM_VK_TILDE", + "DOM_VK_U", + "DOM_VK_UNDERSCORE", + "DOM_VK_UNDO", + "DOM_VK_UNICODE", + "DOM_VK_UP", + "DOM_VK_V", + "DOM_VK_VOLUME_DOWN", + "DOM_VK_VOLUME_MUTE", + "DOM_VK_VOLUME_UP", + "DOM_VK_W", + "DOM_VK_WIN", + "DOM_VK_WINDOW", + "DOM_VK_WIN_ICO_00", + "DOM_VK_WIN_ICO_CLEAR", + "DOM_VK_WIN_ICO_HELP", + "DOM_VK_WIN_OEM_ATTN", + "DOM_VK_WIN_OEM_AUTO", + "DOM_VK_WIN_OEM_BACKTAB", + "DOM_VK_WIN_OEM_CLEAR", + "DOM_VK_WIN_OEM_COPY", + "DOM_VK_WIN_OEM_CUSEL", + "DOM_VK_WIN_OEM_ENLW", + "DOM_VK_WIN_OEM_FINISH", + "DOM_VK_WIN_OEM_FJ_JISHO", + "DOM_VK_WIN_OEM_FJ_LOYA", + "DOM_VK_WIN_OEM_FJ_MASSHOU", + "DOM_VK_WIN_OEM_FJ_ROYA", + "DOM_VK_WIN_OEM_FJ_TOUROKU", + "DOM_VK_WIN_OEM_JUMP", + "DOM_VK_WIN_OEM_PA1", + "DOM_VK_WIN_OEM_PA2", + "DOM_VK_WIN_OEM_PA3", + "DOM_VK_WIN_OEM_RESET", + "DOM_VK_WIN_OEM_WSCTRL", + "DOM_VK_X", + "DOM_VK_XF86XK_ADD_FAVORITE", + "DOM_VK_XF86XK_APPLICATION_LEFT", + "DOM_VK_XF86XK_APPLICATION_RIGHT", + "DOM_VK_XF86XK_AUDIO_CYCLE_TRACK", + "DOM_VK_XF86XK_AUDIO_FORWARD", + "DOM_VK_XF86XK_AUDIO_LOWER_VOLUME", + "DOM_VK_XF86XK_AUDIO_MEDIA", + "DOM_VK_XF86XK_AUDIO_MUTE", + "DOM_VK_XF86XK_AUDIO_NEXT", + "DOM_VK_XF86XK_AUDIO_PAUSE", + "DOM_VK_XF86XK_AUDIO_PLAY", + "DOM_VK_XF86XK_AUDIO_PREV", + "DOM_VK_XF86XK_AUDIO_RAISE_VOLUME", + "DOM_VK_XF86XK_AUDIO_RANDOM_PLAY", + "DOM_VK_XF86XK_AUDIO_RECORD", + "DOM_VK_XF86XK_AUDIO_REPEAT", + "DOM_VK_XF86XK_AUDIO_REWIND", + "DOM_VK_XF86XK_AUDIO_STOP", + "DOM_VK_XF86XK_AWAY", + "DOM_VK_XF86XK_BACK", + "DOM_VK_XF86XK_BACK_FORWARD", + "DOM_VK_XF86XK_BATTERY", + "DOM_VK_XF86XK_BLUE", + "DOM_VK_XF86XK_BLUETOOTH", + "DOM_VK_XF86XK_BOOK", + "DOM_VK_XF86XK_BRIGHTNESS_ADJUST", + "DOM_VK_XF86XK_CALCULATOR", + "DOM_VK_XF86XK_CALENDAR", + "DOM_VK_XF86XK_CD", + "DOM_VK_XF86XK_CLOSE", + "DOM_VK_XF86XK_COMMUNITY", + "DOM_VK_XF86XK_CONTRAST_ADJUST", + "DOM_VK_XF86XK_COPY", + "DOM_VK_XF86XK_CUT", + "DOM_VK_XF86XK_CYCLE_ANGLE", + "DOM_VK_XF86XK_DISPLAY", + "DOM_VK_XF86XK_DOCUMENTS", + "DOM_VK_XF86XK_DOS", + "DOM_VK_XF86XK_EJECT", + "DOM_VK_XF86XK_EXCEL", + "DOM_VK_XF86XK_EXPLORER", + "DOM_VK_XF86XK_FAVORITES", + "DOM_VK_XF86XK_FINANCE", + "DOM_VK_XF86XK_FORWARD", + "DOM_VK_XF86XK_FRAME_BACK", + "DOM_VK_XF86XK_FRAME_FORWARD", + "DOM_VK_XF86XK_GAME", + "DOM_VK_XF86XK_GO", + "DOM_VK_XF86XK_GREEN", + "DOM_VK_XF86XK_HIBERNATE", + "DOM_VK_XF86XK_HISTORY", + "DOM_VK_XF86XK_HOME_PAGE", + "DOM_VK_XF86XK_HOT_LINKS", + "DOM_VK_XF86XK_I_TOUCH", + "DOM_VK_XF86XK_KBD_BRIGHTNESS_DOWN", + "DOM_VK_XF86XK_KBD_BRIGHTNESS_UP", + "DOM_VK_XF86XK_KBD_LIGHT_ON_OFF", + "DOM_VK_XF86XK_LAUNCH0", + "DOM_VK_XF86XK_LAUNCH1", + "DOM_VK_XF86XK_LAUNCH2", + "DOM_VK_XF86XK_LAUNCH3", + "DOM_VK_XF86XK_LAUNCH4", + "DOM_VK_XF86XK_LAUNCH5", + "DOM_VK_XF86XK_LAUNCH6", + "DOM_VK_XF86XK_LAUNCH7", + "DOM_VK_XF86XK_LAUNCH8", + "DOM_VK_XF86XK_LAUNCH9", + "DOM_VK_XF86XK_LAUNCH_A", + "DOM_VK_XF86XK_LAUNCH_B", + "DOM_VK_XF86XK_LAUNCH_C", + "DOM_VK_XF86XK_LAUNCH_D", + "DOM_VK_XF86XK_LAUNCH_E", + "DOM_VK_XF86XK_LAUNCH_F", + "DOM_VK_XF86XK_LIGHT_BULB", + "DOM_VK_XF86XK_LOG_OFF", + "DOM_VK_XF86XK_MAIL", + "DOM_VK_XF86XK_MAIL_FORWARD", + "DOM_VK_XF86XK_MARKET", + "DOM_VK_XF86XK_MEETING", + "DOM_VK_XF86XK_MEMO", + "DOM_VK_XF86XK_MENU_KB", + "DOM_VK_XF86XK_MENU_PB", + "DOM_VK_XF86XK_MESSENGER", + "DOM_VK_XF86XK_MON_BRIGHTNESS_DOWN", + "DOM_VK_XF86XK_MON_BRIGHTNESS_UP", + "DOM_VK_XF86XK_MUSIC", + "DOM_VK_XF86XK_MY_COMPUTER", + "DOM_VK_XF86XK_MY_SITES", + "DOM_VK_XF86XK_NEW", + "DOM_VK_XF86XK_NEWS", + "DOM_VK_XF86XK_OFFICE_HOME", + "DOM_VK_XF86XK_OPEN", + "DOM_VK_XF86XK_OPEN_URL", + "DOM_VK_XF86XK_OPTION", + "DOM_VK_XF86XK_PASTE", + "DOM_VK_XF86XK_PHONE", + "DOM_VK_XF86XK_PICTURES", + "DOM_VK_XF86XK_POWER_DOWN", + "DOM_VK_XF86XK_POWER_OFF", + "DOM_VK_XF86XK_RED", + "DOM_VK_XF86XK_REFRESH", + "DOM_VK_XF86XK_RELOAD", + "DOM_VK_XF86XK_REPLY", + "DOM_VK_XF86XK_ROCKER_DOWN", + "DOM_VK_XF86XK_ROCKER_ENTER", + "DOM_VK_XF86XK_ROCKER_UP", + "DOM_VK_XF86XK_ROTATE_WINDOWS", + "DOM_VK_XF86XK_ROTATION_KB", + "DOM_VK_XF86XK_ROTATION_PB", + "DOM_VK_XF86XK_SAVE", + "DOM_VK_XF86XK_SCREEN_SAVER", + "DOM_VK_XF86XK_SCROLL_CLICK", + "DOM_VK_XF86XK_SCROLL_DOWN", + "DOM_VK_XF86XK_SCROLL_UP", + "DOM_VK_XF86XK_SEARCH", + "DOM_VK_XF86XK_SEND", + "DOM_VK_XF86XK_SHOP", + "DOM_VK_XF86XK_SPELL", + "DOM_VK_XF86XK_SPLIT_SCREEN", + "DOM_VK_XF86XK_STANDBY", + "DOM_VK_XF86XK_START", + "DOM_VK_XF86XK_STOP", + "DOM_VK_XF86XK_SUBTITLE", + "DOM_VK_XF86XK_SUPPORT", + "DOM_VK_XF86XK_SUSPEND", + "DOM_VK_XF86XK_TASK_PANE", + "DOM_VK_XF86XK_TERMINAL", + "DOM_VK_XF86XK_TIME", + "DOM_VK_XF86XK_TOOLS", + "DOM_VK_XF86XK_TOP_MENU", + "DOM_VK_XF86XK_TO_DO_LIST", + "DOM_VK_XF86XK_TRAVEL", + "DOM_VK_XF86XK_USER1KB", + "DOM_VK_XF86XK_USER2KB", + "DOM_VK_XF86XK_USER_PB", + "DOM_VK_XF86XK_UWB", + "DOM_VK_XF86XK_VENDOR_HOME", + "DOM_VK_XF86XK_VIDEO", + "DOM_VK_XF86XK_VIEW", + "DOM_VK_XF86XK_WAKE_UP", + "DOM_VK_XF86XK_WEB_CAM", + "DOM_VK_XF86XK_WHEEL_BUTTON", + "DOM_VK_XF86XK_WLAN", + "DOM_VK_XF86XK_WORD", + "DOM_VK_XF86XK_WWW", + "DOM_VK_XF86XK_XFER", + "DOM_VK_XF86XK_YELLOW", + "DOM_VK_XF86XK_ZOOM_IN", + "DOM_VK_XF86XK_ZOOM_OUT", + "DOM_VK_Y", + "DOM_VK_Z", + "DOM_VK_ZOOM", + "DONE", + "DONT_CARE", + "DOWNLOADING", + "DRAGDROP", + "DRAW_BUFFER0", + "DRAW_BUFFER1", + "DRAW_BUFFER10", + "DRAW_BUFFER11", + "DRAW_BUFFER12", + "DRAW_BUFFER13", + "DRAW_BUFFER14", + "DRAW_BUFFER15", + "DRAW_BUFFER2", + "DRAW_BUFFER3", + "DRAW_BUFFER4", + "DRAW_BUFFER5", + "DRAW_BUFFER6", + "DRAW_BUFFER7", + "DRAW_BUFFER8", + "DRAW_BUFFER9", + "DRAW_FRAMEBUFFER", + "DRAW_FRAMEBUFFER_BINDING", + "DST_ALPHA", + "DST_COLOR", + "DYNAMIC_COPY", + "DYNAMIC_DRAW", + "DYNAMIC_READ", + "DataChannel", + "DataCue", + "DataTransfer", + "DataTransferItem", + "DataTransferItemList", + "DataView", + "Database", + "Date", + "DateTimeFormat", + "Debug", + "DecompressionStream", + "Default Browser Helper", + "DelayNode", + "DesktopNotification", + "DesktopNotificationCenter", + "DeviceAcceleration", + "DeviceLightEvent", + "DeviceMotionEvent", + "DeviceMotionEventAcceleration", + "DeviceMotionEventRotationRate", + "DeviceOrientationEvent", + "DeviceProximityEvent", + "DeviceRotationRate", + "DeviceStorage", + "DeviceStorageChangeEvent", + "Directory", + "DisplayNames", + "Document", + "DocumentFragment", + "DocumentTimeline", + "DocumentType", + "DragEvent", + "DynamicsCompressorNode", + "E", + "ELEMENT_ARRAY_BUFFER", + "ELEMENT_ARRAY_BUFFER_BINDING", + "ELEMENT_NODE", + "EMPTY", + "ENCODING_ERR", + "ENDED", + "END_TO_END", + "END_TO_START", + "ENTITY_NODE", + "ENTITY_REFERENCE_NODE", + "EPSILON", + "EQUAL", + "EQUALPOWER", + "ERROR", + "EXPONENTIAL_DISTANCE", + "EXT_texture_filter_anisotropic", + "Element", + "ElementInternals", + "ElementQuery", + "EnterPictureInPictureEvent", + "Entity", + "EntityReference", + "Enumerator", + "Error", + "ErrorEvent", + "EvalError", + "Event", + "EventException", + "EventSource", + "EventTarget", + "External", + "FASTEST", + "FIDOSDK", + "FILTER_ACCEPT", + "FILTER_INTERRUPT", + "FILTER_REJECT", + "FILTER_SKIP", + "FINISHED_STATE", + "FIRST_ORDERED_NODE_TYPE", + "FLOAT", + "FLOAT_32_UNSIGNED_INT_24_8_REV", + "FLOAT_MAT2", + "FLOAT_MAT2x3", + "FLOAT_MAT2x4", + "FLOAT_MAT3", + "FLOAT_MAT3x2", + "FLOAT_MAT3x4", + "FLOAT_MAT4", + "FLOAT_MAT4x2", + "FLOAT_MAT4x3", + "FLOAT_VEC2", + "FLOAT_VEC3", + "FLOAT_VEC4", + "FOCUS", + "FONT_FACE_RULE", + "FONT_FEATURE_VALUES_RULE", + "FRAGMENT_SHADER", + "FRAGMENT_SHADER_DERIVATIVE_HINT", + "FRAGMENT_SHADER_DERIVATIVE_HINT_OES", + "FRAMEBUFFER", + "FRAMEBUFFER_ATTACHMENT_ALPHA_SIZE", + "FRAMEBUFFER_ATTACHMENT_BLUE_SIZE", + "FRAMEBUFFER_ATTACHMENT_COLOR_ENCODING", + "FRAMEBUFFER_ATTACHMENT_COMPONENT_TYPE", + "FRAMEBUFFER_ATTACHMENT_DEPTH_SIZE", + "FRAMEBUFFER_ATTACHMENT_GREEN_SIZE", + "FRAMEBUFFER_ATTACHMENT_OBJECT_NAME", + "FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE", + "FRAMEBUFFER_ATTACHMENT_RED_SIZE", + "FRAMEBUFFER_ATTACHMENT_STENCIL_SIZE", + "FRAMEBUFFER_ATTACHMENT_TEXTURE_CUBE_MAP_FACE", + "FRAMEBUFFER_ATTACHMENT_TEXTURE_LAYER", + "FRAMEBUFFER_ATTACHMENT_TEXTURE_LEVEL", + "FRAMEBUFFER_BINDING", + "FRAMEBUFFER_COMPLETE", + "FRAMEBUFFER_DEFAULT", + "FRAMEBUFFER_INCOMPLETE_ATTACHMENT", + "FRAMEBUFFER_INCOMPLETE_DIMENSIONS", + "FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT", + "FRAMEBUFFER_INCOMPLETE_MULTISAMPLE", + "FRAMEBUFFER_UNSUPPORTED", + "FRONT", + "FRONT_AND_BACK", + "FRONT_FACE", + "FUNC_ADD", + "FUNC_REVERSE_SUBTRACT", + "FUNC_SUBTRACT", + "FeaturePolicy", + "FederatedCredential", + "Feed", + "FeedEntry", + "File", + "FileError", + "FileList", + "FileReader", + "FileSystem", + "FileSystemDirectoryEntry", + "FileSystemDirectoryReader", + "FileSystemEntry", + "FileSystemFileEntry", + "FinalizationRegistry", + "FindInPage", + "Float32Array", + "Float64Array", + "FocusEvent", + "FontFace", + "FontFaceSet", + "FontFaceSetLoadEvent", + "FormData", + "FormDataEvent", + "FragmentDirective", + "Function", + "GENERATE_MIPMAP_HINT", + "GEQUAL", + "GREATER", + "GREEN_BITS", + "GainNode", + "Gamepad", + "GamepadButton", + "GamepadEvent", + "GamepadHapticActuator", + "GamepadPose", + "Geolocation", + "GeolocationCoordinates", + "GeolocationPosition", + "GeolocationPositionError", + "GestureEvent", + "Global", + "Gyroscope", + "HALF_FLOAT", + "HAVE_CURRENT_DATA", + "HAVE_ENOUGH_DATA", + "HAVE_FUTURE_DATA", + "HAVE_METADATA", + "HAVE_NOTHING", + "HEADERS_RECEIVED", + "HIDDEN", + "HIERARCHY_REQUEST_ERR", + "HIGHPASS", + "HIGHSHELF", + "HIGH_FLOAT", + "HIGH_INT", + "HORIZONTAL", + "HORIZONTAL_AXIS", + "HRTF", + "HTMLAllCollection", + "HTMLAnchorElement", + "HTMLAppletElement", + "HTMLAreaElement", + "HTMLAreasCollection", + "HTMLAudioElement", + "HTMLBGSoundElement", + "HTMLBRElement", + "HTMLBaseElement", + "HTMLBaseFontElement", + "HTMLBlockElement", + "HTMLBlockquoteElement", + "HTMLBodyElement", + "HTMLButtonElement", + "HTMLCanvasElement", + "HTMLCollection", + "HTMLCommandElement", + "HTMLContentElement", + "HTMLDDElement", + "HTMLDListElement", + "HTMLDTElement", + "HTMLDataElement", + "HTMLDataListElement", + "HTMLDetailsElement", + "HTMLDialogElement", + "HTMLDirectoryElement", + "HTMLDivElement", + "HTMLDocument", + "HTMLElement", + "HTMLEmbedElement", + "HTMLFieldSetElement", + "HTMLFontElement", + "HTMLFormControlsCollection", + "HTMLFormElement", + "HTMLFrameElement", + "HTMLFrameSetElement", + "HTMLHRElement", + "HTMLHeadElement", + "HTMLHeadingElement", + "HTMLHtmlElement", + "HTMLIFrameElement", + "HTMLImageElement", + "HTMLInputElement", + "HTMLIsIndexElement", + "HTMLKeygenElement", + "HTMLLIElement", + "HTMLLabelElement", + "HTMLLegendElement", + "HTMLLinkElement", + "HTMLMapElement", + "HTMLMarqueeElement", + "HTMLMediaElement", + "HTMLMenuElement", + "HTMLMenuItemElement", + "HTMLMetaElement", + "HTMLMeterElement", + "HTMLModElement", + "HTMLNextIdElement", + "HTMLOListElement", + "HTMLObjectElement", + "HTMLOptGroupElement", + "HTMLOptionElement", + "HTMLOptionsCollection", + "HTMLOutputElement", + "HTMLParagraphElement", + "HTMLParamElement", + "HTMLPhraseElement", + "HTMLPictureElement", + "HTMLPreElement", + "HTMLProgressElement", + "HTMLPropertiesCollection", + "HTMLQuoteElement", + "HTMLScriptElement", + "HTMLSelectElement", + "HTMLShadowElement", + "HTMLSlotElement", + "HTMLSourceElement", + "HTMLSpanElement", + "HTMLStyleElement", + "HTMLTableCaptionElement", + "HTMLTableCellElement", + "HTMLTableColElement", + "HTMLTableDataCellElement", + "HTMLTableElement", + "HTMLTableHeaderCellElement", + "HTMLTableRowElement", + "HTMLTableSectionElement", + "HTMLTemplateElement", + "HTMLTextAreaElement", + "HTMLTimeElement", + "HTMLTitleElement", + "HTMLTrackElement", + "HTMLUListElement", + "HTMLUnknownElement", + "HTMLVideoElement", + "HashChangeEvent", + "Headers", + "History", + "Hz", + "ICE_CHECKING", + "ICE_CLOSED", + "ICE_COMPLETED", + "ICE_CONNECTED", + "ICE_FAILED", + "ICE_GATHERING", + "ICE_WAITING", + "IDBCursor", + "IDBCursorWithValue", + "IDBDatabase", + "IDBDatabaseException", + "IDBFactory", + "IDBFileHandle", + "IDBFileRequest", + "IDBIndex", + "IDBKeyRange", + "IDBMutableFile", + "IDBObjectStore", + "IDBOpenDBRequest", + "IDBRequest", + "IDBTransaction", + "IDBVersionChangeEvent", + "IDLE", + "IIRFilterNode", + "IMPLEMENTATION_COLOR_READ_FORMAT", + "IMPLEMENTATION_COLOR_READ_TYPE", + "IMPORT_RULE", + "INCR", + "INCR_WRAP", + "INDEX_SIZE_ERR", + "INSTALL", + "INSTALLED", + "INT", + "INTERLEAVED_ATTRIBS", + "INT_2_10_10_10_REV", + "INT_SAMPLER_2D", + "INT_SAMPLER_2D_ARRAY", + "INT_SAMPLER_3D", + "INT_SAMPLER_CUBE", + "INT_VEC2", + "INT_VEC3", + "INT_VEC4", + "INUSE_ATTRIBUTE_ERR", + "INVALID_ACCESS_ERR", + "INVALID_CHARACTER_ERR", + "INVALID_ENUM", + "INVALID_EXPRESSION_ERR", + "INVALID_FRAMEBUFFER_OPERATION", + "INVALID_INDEX", + "INVALID_MODIFICATION_ERR", + "INVALID_NODE_TYPE_ERR", + "INVALID_OPERATION", + "INVALID_STATE_ERR", + "INVALID_VALUE", + "INVERSE_DISTANCE", + "INVERT", + "IceCandidate", + "IdleDeadline", + "Image", + "ImageBitmap", + "ImageBitmapRenderingContext", + "ImageCapture", + "ImageData", + "Infinity", + "InputDeviceCapabilities", + "InputDeviceInfo", + "InputEvent", + "InputMethodContext", + "InstallState", + "InstallTrigger", + "Instance", + "Int16Array", + "Int32Array", + "Int8Array", + "Intent", + "InternalError", + "IntersectionObserver", + "IntersectionObserverEntry", + "Intl", + "IsSearchProviderInstalled", + "Iterator", + "JSON", + "Java Deployment Toolkit 7.0.250.17", + "Java(TM) Platform SE 7 U25", + "KEEP", + "KEYDOWN", + "KEYFRAMES_RULE", + "KEYFRAME_RULE", + "KEYPRESS", + "KEYUP", + "Key", + "KeyEvent", + "KeyOperation", + "KeyPair", + "Keyboard", + "KeyboardEvent", + "KeyboardLayoutMap", + "KeyframeEffect", + "LENGTHADJUST_SPACING", + "LENGTHADJUST_SPACINGANDGLYPHS", + "LENGTHADJUST_UNKNOWN", + "LEQUAL", + "LESS", + "LINEAR", + "LINEAR_DISTANCE", + "LINEAR_MIPMAP_LINEAR", + "LINEAR_MIPMAP_NEAREST", + "LINES", + "LINE_LOOP", + "LINE_STRIP", + "LINE_WIDTH", + "LINK_STATUS", + "LINUX", + "LIVE", + "LN10", + "LN2", + "LOADED", + "LOADING", + "LOCALE", + "LOG10E", + "LOG2E", + "LOWPASS", + "LOWSHELF", + "LOW_FLOAT", + "LOW_INT", + "LSException", + "LSParserFilter", + "LUMINANCE", + "LUMINANCE_ALPHA", + "LargestContentfulPaint", + "LayoutShift", + "LayoutShiftAttribution", + "LinearAccelerationSensor", + "LinkError", + "ListFormat", + "LocalMediaStream", + "Locale", + "Location", + "Lock", + "LockManager", + "MAC", + "MAX", + "MAX_3D_TEXTURE_SIZE", + "MAX_ARRAY_TEXTURE_LAYERS", + "MAX_CLIENT_WAIT_TIMEOUT_WEBGL", + "MAX_COLOR_ATTACHMENTS", + "MAX_COMBINED_FRAGMENT_UNIFORM_COMPONENTS", + "MAX_COMBINED_TEXTURE_IMAGE_UNITS", + "MAX_COMBINED_UNIFORM_BLOCKS", + "MAX_COMBINED_VERTEX_UNIFORM_COMPONENTS", + "MAX_CUBE_MAP_TEXTURE_SIZE", + "MAX_DRAW_BUFFERS", + "MAX_ELEMENTS_INDICES", + "MAX_ELEMENTS_VERTICES", + "MAX_ELEMENT_INDEX", + "MAX_FRAGMENT_INPUT_COMPONENTS", + "MAX_FRAGMENT_UNIFORM_BLOCKS", + "MAX_FRAGMENT_UNIFORM_COMPONENTS", + "MAX_FRAGMENT_UNIFORM_VECTORS", + "MAX_PROGRAM_TEXEL_OFFSET", + "MAX_RENDERBUFFER_SIZE", + "MAX_SAFE_INTEGER", + "MAX_SAMPLES", + "MAX_SERVER_WAIT_TIMEOUT", + "MAX_TEXTURE_IMAGE_UNITS", + "MAX_TEXTURE_LOD_BIAS", + "MAX_TEXTURE_MAX_ANISOTROPY_EXT", + "MAX_TEXTURE_SIZE", + "MAX_TRANSFORM_FEEDBACK_INTERLEAVED_COMPONENTS", + "MAX_TRANSFORM_FEEDBACK_SEPARATE_ATTRIBS", + "MAX_TRANSFORM_FEEDBACK_SEPARATE_COMPONENTS", + "MAX_UNIFORM_BLOCK_SIZE", + "MAX_UNIFORM_BUFFER_BINDINGS", + "MAX_VALUE", + "MAX_VARYING_COMPONENTS", + "MAX_VARYING_VECTORS", + "MAX_VERTEX_ATTRIBS", + "MAX_VERTEX_OUTPUT_COMPONENTS", + "MAX_VERTEX_TEXTURE_IMAGE_UNITS", + "MAX_VERTEX_UNIFORM_BLOCKS", + "MAX_VERTEX_UNIFORM_COMPONENTS", + "MAX_VERTEX_UNIFORM_VECTORS", + "MAX_VIEWPORT_DIMS", + "MEDIA_ERR_ABORTED", + "MEDIA_ERR_DECODE", + "MEDIA_ERR_ENCRYPTED", + "MEDIA_ERR_NETWORK", + "MEDIA_ERR_SRC_NOT_SUPPORTED", + "MEDIA_KEYERR_CLIENT", + "MEDIA_KEYERR_DOMAIN", + "MEDIA_KEYERR_HARDWARECHANGE", + "MEDIA_KEYERR_OUTPUT", + "MEDIA_KEYERR_SERVICE", + "MEDIA_KEYERR_UNKNOWN", + "MEDIA_RULE", + "MEDIUM_FLOAT", + "MEDIUM_INT", + "META_MASK", + "MIDIAccess", + "MIDIConnectionEvent", + "MIDIInput", + "MIDIInputMap", + "MIDIMessageEvent", + "MIDIOutput", + "MIDIOutputMap", + "MIDIPort", + "MIN", + "MIN_PROGRAM_TEXEL_OFFSET", + "MIN_SAFE_INTEGER", + "MIN_VALUE", + "MIRRORED_REPEAT", + "MODE_ASYNCHRONOUS", + "MODE_SYNCHRONOUS", + "MODIFICATION", + "MOUSEDOWN", + "MOUSEDRAG", + "MOUSEMOVE", + "MOUSEOUT", + "MOUSEOVER", + "MOUSEUP", + "MOZ_KEYFRAMES_RULE", + "MOZ_KEYFRAME_RULE", + "MOZ_SOURCE_CURSOR", + "MOZ_SOURCE_ERASER", + "MOZ_SOURCE_KEYBOARD", + "MOZ_SOURCE_MOUSE", + "MOZ_SOURCE_PEN", + "MOZ_SOURCE_TOUCH", + "MOZ_SOURCE_UNKNOWN", + "MSBehaviorUrnsCollection", + "MSBlobBuilder", + "MSCSSMatrix", + "MSCSSProperties", + "MSCSSRuleList", + "MSCompatibleInfo", + "MSCompatibleInfoCollection", + "MSCurrentStyleCSSProperties", + "MSEventObj", + "MSGESTURE_FLAG_BEGIN", + "MSGESTURE_FLAG_CANCEL", + "MSGESTURE_FLAG_END", + "MSGESTURE_FLAG_INERTIA", + "MSGESTURE_FLAG_NONE", + "MSGesture", + "MSGestureEvent", + "MSGraphicsTrust", + "MSInputMethodContext", + "MSManipulationEvent", + "MSMediaKeyError", + "MSMediaKeyMessageEvent", + "MSMediaKeyNeededEvent", + "MSMediaKeySession", + "MSMediaKeys", + "MSMimeTypesCollection", + "MSPOINTER_TYPE_MOUSE", + "MSPOINTER_TYPE_PEN", + "MSPOINTER_TYPE_TOUCH", + "MSPluginsCollection", + "MSPointerEvent", + "MSRangeCollection", + "MSSiteModeEvent", + "MSStream", + "MSStreamReader", + "MSStyleCSSProperties", + "MS_ASYNC_CALLBACK_STATUS_ASSIGN_DELEGATE", + "MS_ASYNC_CALLBACK_STATUS_CANCEL", + "MS_ASYNC_CALLBACK_STATUS_CHOOSEANY", + "MS_ASYNC_CALLBACK_STATUS_ERROR", + "MS_ASYNC_CALLBACK_STATUS_JOIN", + "MS_ASYNC_OP_STATUS_CANCELED", + "MS_ASYNC_OP_STATUS_ERROR", + "MS_ASYNC_OP_STATUS_SUCCESS", + "MS_MANIPULATION_STATE_ACTIVE", + "MS_MANIPULATION_STATE_CANCELLED", + "MS_MANIPULATION_STATE_COMMITTED", + "MS_MANIPULATION_STATE_DRAGGING", + "MS_MANIPULATION_STATE_INERTIA", + "MS_MANIPULATION_STATE_PRESELECT", + "MS_MANIPULATION_STATE_SELECTING", + "MS_MANIPULATION_STATE_STOPPED", + "MS_MEDIA_ERR_ENCRYPTED", + "MS_MEDIA_KEYERR_CLIENT", + "MS_MEDIA_KEYERR_DOMAIN", + "MS_MEDIA_KEYERR_HARDWARECHANGE", + "MS_MEDIA_KEYERR_OUTPUT", + "MS_MEDIA_KEYERR_SERVICE", + "MS_MEDIA_KEYERR_UNKNOWN", + "Map", + "Math", + "MathMLElement", + "MediaCapabilities", + "MediaCapabilitiesInfo", + "MediaController", + "MediaDeviceInfo", + "MediaDevices", + "MediaElementAudioSourceNode", + "MediaEncryptedEvent", + "MediaError", + "MediaKeyError", + "MediaKeyEvent", + "MediaKeyMessageEvent", + "MediaKeyNeededEvent", + "MediaKeySession", + "MediaKeyStatusMap", + "MediaKeySystemAccess", + "MediaKeys", + "MediaList", + "MediaMetadata", + "MediaQueryList", + "MediaQueryListEvent", + "MediaRecorder", + "MediaRecorderErrorEvent", + "MediaSession", + "MediaSettingsRange", + "MediaSource", + "MediaStream", + "MediaStreamAudioDestinationNode", + "MediaStreamAudioSourceNode", + "MediaStreamEvent", + "MediaStreamTrack", + "MediaStreamTrackAudioSourceNode", + "MediaStreamTrackEvent", + "Memory", + "MessageChannel", + "MessageEvent", + "MessagePort", + "Methods", + "Microsoft® DRM", + "MimeType", + "MimeTypeArray", + "Module", + "MouseEvent", + "MouseScrollEvent", + "MouseWheelEvent", + "MozAnimation", + "MozAnimationDelay", + "MozAnimationDirection", + "MozAnimationDuration", + "MozAnimationFillMode", + "MozAnimationIterationCount", + "MozAnimationName", + "MozAnimationPlayState", + "MozAnimationTimingFunction", + "MozAppearance", + "MozBackfaceVisibility", + "MozBinding", + "MozBorderBottomColors", + "MozBorderEnd", + "MozBorderEndColor", + "MozBorderEndStyle", + "MozBorderEndWidth", + "MozBorderImage", + "MozBorderLeftColors", + "MozBorderRightColors", + "MozBorderStart", + "MozBorderStartColor", + "MozBorderStartStyle", + "MozBorderStartWidth", + "MozBorderTopColors", + "MozBoxAlign", + "MozBoxDirection", + "MozBoxFlex", + "MozBoxOrdinalGroup", + "MozBoxOrient", + "MozBoxPack", + "MozBoxSizing", + "MozCSSKeyframeRule", + "MozCSSKeyframesRule", + "MozColumnCount", + "MozColumnFill", + "MozColumnGap", + "MozColumnRule", + "MozColumnRuleColor", + "MozColumnRuleStyle", + "MozColumnRuleWidth", + "MozColumnWidth", + "MozColumns", + "MozContactChangeEvent", + "MozFloatEdge", + "MozFontFeatureSettings", + "MozFontLanguageOverride", + "MozForceBrokenImageIcon", + "MozHyphens", + "MozImageRegion", + "MozMarginEnd", + "MozMarginStart", + "MozMmsEvent", + "MozMmsMessage", + "MozMobileMessageThread", + "MozOSXFontSmoothing", + "MozOrient", + "MozOutlineRadius", + "MozOutlineRadiusBottomleft", + "MozOutlineRadiusBottomright", + "MozOutlineRadiusTopleft", + "MozOutlineRadiusTopright", + "MozPaddingEnd", + "MozPaddingStart", + "MozPerspective", + "MozPerspectiveOrigin", + "MozPowerManager", + "MozSettingsEvent", + "MozSmsEvent", + "MozSmsMessage", + "MozStackSizing", + "MozTabSize", + "MozTextAlignLast", + "MozTextDecorationColor", + "MozTextDecorationLine", + "MozTextDecorationStyle", + "MozTextSizeAdjust", + "MozTransform", + "MozTransformOrigin", + "MozTransformStyle", + "MozTransition", + "MozTransitionDelay", + "MozTransitionDuration", + "MozTransitionProperty", + "MozTransitionTimingFunction", + "MozUserFocus", + "MozUserInput", + "MozUserModify", + "MozUserSelect", + "MozWindowDragging", + "MozWindowShadow", + "MutationEvent", + "MutationObserver", + "MutationRecord", + "NAMESPACE_ERR", + "NAMESPACE_RULE", + "NEAREST", + "NEAREST_MIPMAP_LINEAR", + "NEAREST_MIPMAP_NEAREST", + "NEGATIVE_INFINITY", + "NETWORK_EMPTY", + "NETWORK_ERR", + "NETWORK_IDLE", + "NETWORK_LOADED", + "NETWORK_LOADING", + "NETWORK_NO_SOURCE", + "NEVER", + "NEW", + "NEXT", + "NEXT_NO_DUPLICATE", + "NICEST", + "NODE_AFTER", + "NODE_BEFORE", + "NODE_BEFORE_AND_AFTER", + "NODE_INSIDE", + "NONE", + "NON_TRANSIENT_ERR", + "NOTATION_NODE", + "NOTCH", + "NOTEQUAL", + "NOT_ALLOWED_ERR", + "NOT_FOUND_ERR", + "NOT_INSTALLED", + "NOT_READABLE_ERR", + "NOT_SUPPORTED_ERR", + "NO_DATA_ALLOWED_ERR", + "NO_ERR", + "NO_ERROR", + "NO_MODIFICATION_ALLOWED_ERR", + "NO_UPDATE", + "NUMBER_TYPE", + "NUM_COMPRESSED_TEXTURE_FORMATS", + "NaN", + "NamedNodeMap", + "Native Client", + "NavigationPreloadManager", + "Navigator", + "NearbyLinks", + "NetworkInformation", + "Node", + "NodeFilter", + "NodeIterator", + "NodeList", + "Notation", + "Notification", + "NotifyPaintEvent", + "Number", + "NumberFormat", + "OBJECT_TYPE", + "OBSOLETE", + "OES_element_index_uint", + "OES_standard_derivatives", + "OES_texture_float", + "OES_texture_float_linear", + "OK", + "ONE", + "ONE_MINUS_CONSTANT_ALPHA", + "ONE_MINUS_CONSTANT_COLOR", + "ONE_MINUS_DST_ALPHA", + "ONE_MINUS_DST_COLOR", + "ONE_MINUS_SRC_ALPHA", + "ONE_MINUS_SRC_COLOR", + "OPEN", + "OPENBSD", + "OPENED", + "OPENING", + "ORDERED_NODE_ITERATOR_TYPE", + "ORDERED_NODE_SNAPSHOT_TYPE", + "OS_UPDATE", + "OTHER_ERROR", + "OUT_OF_MEMORY", + "Object", + "OfflineAudioCompletionEvent", + "OfflineAudioContext", + "OfflineResourceList", + "OffscreenCanvas", + "OffscreenCanvasRenderingContext2D", + "OnInstalledReason", + "OnRestartRequiredReason", + "Option", + "OrientationSensor", + "OscillatorNode", + "OverconstrainedError", + "OverconstrainedErrorEvent", + "OverflowEvent", + "PACKAGE", + "PACK_ALIGNMENT", + "PACK_ROW_LENGTH", + "PACK_SKIP_PIXELS", + "PACK_SKIP_ROWS", + "PAGE_RULE", + "PARSE_ERR", + "PATHSEG_ARC_ABS", + "PATHSEG_ARC_REL", + "PATHSEG_CLOSEPATH", + "PATHSEG_CURVETO_CUBIC_ABS", + "PATHSEG_CURVETO_CUBIC_REL", + "PATHSEG_CURVETO_CUBIC_SMOOTH_ABS", + "PATHSEG_CURVETO_CUBIC_SMOOTH_REL", + "PATHSEG_CURVETO_QUADRATIC_ABS", + "PATHSEG_CURVETO_QUADRATIC_REL", + "PATHSEG_CURVETO_QUADRATIC_SMOOTH_ABS", + "PATHSEG_CURVETO_QUADRATIC_SMOOTH_REL", + "PATHSEG_LINETO_ABS", + "PATHSEG_LINETO_HORIZONTAL_ABS", + "PATHSEG_LINETO_HORIZONTAL_REL", + "PATHSEG_LINETO_REL", + "PATHSEG_LINETO_VERTICAL_ABS", + "PATHSEG_LINETO_VERTICAL_REL", + "PATHSEG_MOVETO_ABS", + "PATHSEG_MOVETO_REL", + "PATHSEG_UNKNOWN", + "PATH_EXISTS_ERR", + "PEAKING", + "PERIODIC", + "PERMISSION_DENIED", + "PERSISTENT", + "PI", + "PIXEL_PACK_BUFFER", + "PIXEL_PACK_BUFFER_BINDING", + "PIXEL_UNPACK_BUFFER", + "PIXEL_UNPACK_BUFFER_BINDING", + "PLAYING_STATE", + "POINTS", + "POLYGON_OFFSET_FACTOR", + "POLYGON_OFFSET_FILL", + "POLYGON_OFFSET_UNITS", + "POSITION_UNAVAILABLE", + "POSITIVE_INFINITY", + "PREV", + "PREV_NO_DUPLICATE", + "PROCESSING_INSTRUCTION_NODE", + "PageChangeEvent", + "PageTransitionEvent", + "PaintRequest", + "PaintRequestList", + "PannerNode", + "PasswordCredential", + "Path2D", + "PaymentAddress", + "PaymentInstruments", + "PaymentManager", + "PaymentMethodChangeEvent", + "PaymentRequest", + "PaymentRequestUpdateEvent", + "PaymentResponse", + "Performance", + "PerformanceElementTiming", + "PerformanceEntry", + "PerformanceEventTiming", + "PerformanceLongTaskTiming", + "PerformanceMark", + "PerformanceMeasure", + "PerformanceNavigation", + "PerformanceNavigationTiming", + "PerformanceObserver", + "PerformanceObserverEntryList", + "PerformancePaintTiming", + "PerformanceResourceTiming", + "PerformanceServerTiming", + "PerformanceTiming", + "PeriodicSyncManager", + "PeriodicWave", + "PermissionStatus", + "Permissions", + "PhotoCapabilities", + "PictureInPictureWindow", + "PlatformArch", + "PlatformNaclArch", + "PlatformOs", + "Plugin", + "PluginArray", + "PluralRules", + "PointerEvent", + "PopStateEvent", + "PopupBlockedEvent", + "Position", + "PositionError", + "Presentation", + "PresentationAvailability", + "PresentationConnection", + "PresentationConnectionAvailableEvent", + "PresentationConnectionCloseEvent", + "PresentationConnectionList", + "PresentationReceiver", + "PresentationRequest", + "ProcessingInstruction", + "ProgressEvent", + "Promise", + "PromiseRejectionEvent", + "PropertyNodeList", + "Proxy", + "PublicKeyCredential", + "PushManager", + "PushSubscription", + "PushSubscriptionOptions", + "Q", + "QUERY_RESULT", + "QUERY_RESULT_AVAILABLE", + "QUOTA_ERR", + "QUOTA_EXCEEDED_ERR", + "QueryInterface", + "R11F_G11F_B10F", + "R16F", + "R16I", + "R16UI", + "R32F", + "R32I", + "R32UI", + "R8", + "R8I", + "R8UI", + "R8_SNORM", + "RASTERIZER_DISCARD", + "READY_TO_RUN", + "READ_BUFFER", + "READ_FRAMEBUFFER", + "READ_FRAMEBUFFER_BINDING", + "READ_ONLY", + "READ_ONLY_ERR", + "READ_WRITE", + "RED", + "RED_BITS", + "RED_INTEGER", + "REMOVAL", + "RENDERBUFFER", + "RENDERBUFFER_ALPHA_SIZE", + "RENDERBUFFER_BINDING", + "RENDERBUFFER_BLUE_SIZE", + "RENDERBUFFER_DEPTH_SIZE", + "RENDERBUFFER_GREEN_SIZE", + "RENDERBUFFER_HEIGHT", + "RENDERBUFFER_INTERNAL_FORMAT", + "RENDERBUFFER_RED_SIZE", + "RENDERBUFFER_SAMPLES", + "RENDERBUFFER_STENCIL_SIZE", + "RENDERBUFFER_WIDTH", + "RENDERER", + "RENDERING_INTENT_ABSOLUTE_COLORIMETRIC", + "RENDERING_INTENT_AUTO", + "RENDERING_INTENT_PERCEPTUAL", + "RENDERING_INTENT_RELATIVE_COLORIMETRIC", + "RENDERING_INTENT_SATURATION", + "RENDERING_INTENT_UNKNOWN", + "REPEAT", + "REPLACE", + "RG", + "RG16F", + "RG16I", + "RG16UI", + "RG32F", + "RG32I", + "RG32UI", + "RG8", + "RG8I", + "RG8UI", + "RG8_SNORM", + "RGB", + "RGB10_A2", + "RGB10_A2UI", + "RGB16F", + "RGB16I", + "RGB16UI", + "RGB32F", + "RGB32I", + "RGB32UI", + "RGB565", + "RGB5_A1", + "RGB8", + "RGB8I", + "RGB8UI", + "RGB8_SNORM", + "RGB9_E5", + "RGBA", + "RGBA16F", + "RGBA16I", + "RGBA16UI", + "RGBA32F", + "RGBA32I", + "RGBA32UI", + "RGBA4", + "RGBA8", + "RGBA8I", + "RGBA8UI", + "RGBA8_SNORM", + "RGBA_INTEGER", + "RGBColor", + "RGB_INTEGER", + "RG_INTEGER", + "ROTATION_CLOCKWISE", + "ROTATION_COUNTERCLOCKWISE", + "RTCCertificate", + "RTCDTMFSender", + "RTCDTMFToneChangeEvent", + "RTCDataChannel", + "RTCDataChannelEvent", + "RTCDtlsTransport", + "RTCError", + "RTCErrorEvent", + "RTCIceCandidate", + "RTCIceTransport", + "RTCPeerConnection", + "RTCPeerConnectionIceErrorEvent", + "RTCPeerConnectionIceEvent", + "RTCRtpReceiver", + "RTCRtpSender", + "RTCRtpTransceiver", + "RTCSctpTransport", + "RTCSessionDescription", + "RTCStatsReport", + "RTCTrackEvent", + "RUNNING", + "RadioNodeList", + "Range", + "RangeError", + "RangeException", + "ReadableByteStream", + "ReadableStream", + "ReadableStreamDefaultReader", + "RecordErrorEvent", + "Rect", + "ReferenceError", + "Reflect", + "RegExp", + "RelativeOrientationSensor", + "RelativeTimeFormat", + "RemotePlayback", + "ReportingObserver", + "Request", + "RequestUpdateCheckStatus", + "ResizeObserver", + "ResizeObserverEntry", + "ResizeObserverSize", + "Response", + "RunningState", + "RuntimeError", + "SAMPLER_2D", + "SAMPLER_2D_ARRAY", + "SAMPLER_2D_ARRAY_SHADOW", + "SAMPLER_2D_SHADOW", + "SAMPLER_3D", + "SAMPLER_BINDING", + "SAMPLER_CUBE", + "SAMPLER_CUBE_SHADOW", + "SAMPLES", + "SAMPLE_ALPHA_TO_COVERAGE", + "SAMPLE_BUFFERS", + "SAMPLE_COVERAGE", + "SAMPLE_COVERAGE_INVERT", + "SAMPLE_COVERAGE_VALUE", + "SAWTOOTH", + "SCHEDULED_STATE", + "SCISSOR_BOX", + "SCISSOR_TEST", + "SCROLL_PAGE_DOWN", + "SCROLL_PAGE_UP", + "SDP_ANSWER", + "SDP_OFFER", + "SDP_PRANSWER", + "SECURITY_ERR", + "SELECT", + "SEPARATE_ATTRIBS", + "SERIALIZE_ERR", + "SEVERITY_ERROR", + "SEVERITY_FATAL_ERROR", + "SEVERITY_WARNING", + "SHADER_COMPILER", + "SHADER_TYPE", + "SHADING_LANGUAGE_VERSION", + "SHARED_MODULE_UPDATE", + "SHIFT_MASK", + "SHORT", + "SHOWING", + "SHOW_ALL", + "SHOW_ATTRIBUTE", + "SHOW_CDATA_SECTION", + "SHOW_COMMENT", + "SHOW_DOCUMENT", + "SHOW_DOCUMENT_FRAGMENT", + "SHOW_DOCUMENT_TYPE", + "SHOW_ELEMENT", + "SHOW_ENTITY", + "SHOW_ENTITY_REFERENCE", + "SHOW_NOTATION", + "SHOW_PROCESSING_INSTRUCTION", + "SHOW_TEXT", + "SIGNALED", + "SIGNED_NORMALIZED", + "SINE", + "SKIN", + "SOUNDFIELD", + "SQLError", + "SQLException", + "SQLResultSet", + "SQLResultSetRowList", + "SQLTransaction", + "SQRT1_2", + "SQRT2", + "SQUARE", + "SRC_ALPHA", + "SRC_ALPHA_SATURATE", + "SRC_COLOR", + "SRGB", + "SRGB8", + "SRGB8_ALPHA8", + "START_TO_END", + "START_TO_START", + "STATIC_COPY", + "STATIC_DRAW", + "STATIC_READ", + "STENCIL", + "STENCIL_ATTACHMENT", + "STENCIL_BACK_FAIL", + "STENCIL_BACK_FUNC", + "STENCIL_BACK_PASS_DEPTH_FAIL", + "STENCIL_BACK_PASS_DEPTH_PASS", + "STENCIL_BACK_REF", + "STENCIL_BACK_VALUE_MASK", + "STENCIL_BACK_WRITEMASK", + "STENCIL_BITS", + "STENCIL_BUFFER_BIT", + "STENCIL_CLEAR_VALUE", + "STENCIL_FAIL", + "STENCIL_FUNC", + "STENCIL_INDEX", + "STENCIL_INDEX8", + "STENCIL_PASS_DEPTH_FAIL", + "STENCIL_PASS_DEPTH_PASS", + "STENCIL_REF", + "STENCIL_TEST", + "STENCIL_VALUE_MASK", + "STENCIL_WRITEMASK", + "STREAM_COPY", + "STREAM_DRAW", + "STREAM_READ", + "STRING_TYPE", + "STYLE_RULE", + "SUBPIXEL_BITS", + "SUPPORTS_RULE", + "SVGAElement", + "SVGAltGlyphDefElement", + "SVGAltGlyphElement", + "SVGAltGlyphItemElement", + "SVGAngle", + "SVGAnimateColorElement", + "SVGAnimateElement", + "SVGAnimateMotionElement", + "SVGAnimateTransformElement", + "SVGAnimatedAngle", + "SVGAnimatedBoolean", + "SVGAnimatedEnumeration", + "SVGAnimatedInteger", + "SVGAnimatedLength", + "SVGAnimatedLengthList", + "SVGAnimatedNumber", + "SVGAnimatedNumberList", + "SVGAnimatedPreserveAspectRatio", + "SVGAnimatedRect", + "SVGAnimatedString", + "SVGAnimatedTransformList", + "SVGAnimationElement", + "SVGCircleElement", + "SVGClipPathElement", + "SVGColor", + "SVGComponentTransferFunctionElement", + "SVGCursorElement", + "SVGDefsElement", + "SVGDescElement", + "SVGDiscardElement", + "SVGDocument", + "SVGElement", + "SVGElementInstance", + "SVGElementInstanceList", + "SVGEllipseElement", + "SVGException", + "SVGFEBlendElement", + "SVGFEColorMatrixElement", + "SVGFEComponentTransferElement", + "SVGFECompositeElement", + "SVGFEConvolveMatrixElement", + "SVGFEDiffuseLightingElement", + "SVGFEDisplacementMapElement", + "SVGFEDistantLightElement", + "SVGFEDropShadowElement", + "SVGFEFloodElement", + "SVGFEFuncAElement", + "SVGFEFuncBElement", + "SVGFEFuncGElement", + "SVGFEFuncRElement", + "SVGFEGaussianBlurElement", + "SVGFEImageElement", + "SVGFEMergeElement", + "SVGFEMergeNodeElement", + "SVGFEMorphologyElement", + "SVGFEOffsetElement", + "SVGFEPointLightElement", + "SVGFESpecularLightingElement", + "SVGFESpotLightElement", + "SVGFETileElement", + "SVGFETurbulenceElement", + "SVGFilterElement", + "SVGFontElement", + "SVGFontFaceElement", + "SVGFontFaceFormatElement", + "SVGFontFaceNameElement", + "SVGFontFaceSrcElement", + "SVGFontFaceUriElement", + "SVGForeignObjectElement", + "SVGGElement", + "SVGGeometryElement", + "SVGGlyphElement", + "SVGGlyphRefElement", + "SVGGradientElement", + "SVGGraphicsElement", + "SVGHKernElement", + "SVGImageElement", + "SVGLength", + "SVGLengthList", + "SVGLineElement", + "SVGLinearGradientElement", + "SVGMPathElement", + "SVGMarkerElement", + "SVGMaskElement", + "SVGMatrix", + "SVGMetadataElement", + "SVGMissingGlyphElement", + "SVGNumber", + "SVGNumberList", + "SVGPaint", + "SVGPathElement", + "SVGPathSeg", + "SVGPathSegArcAbs", + "SVGPathSegArcRel", + "SVGPathSegClosePath", + "SVGPathSegCurvetoCubicAbs", + "SVGPathSegCurvetoCubicRel", + "SVGPathSegCurvetoCubicSmoothAbs", + "SVGPathSegCurvetoCubicSmoothRel", + "SVGPathSegCurvetoQuadraticAbs", + "SVGPathSegCurvetoQuadraticRel", + "SVGPathSegCurvetoQuadraticSmoothAbs", + "SVGPathSegCurvetoQuadraticSmoothRel", + "SVGPathSegLinetoAbs", + "SVGPathSegLinetoHorizontalAbs", + "SVGPathSegLinetoHorizontalRel", + "SVGPathSegLinetoRel", + "SVGPathSegLinetoVerticalAbs", + "SVGPathSegLinetoVerticalRel", + "SVGPathSegList", + "SVGPathSegMovetoAbs", + "SVGPathSegMovetoRel", + "SVGPatternElement", + "SVGPoint", + "SVGPointList", + "SVGPolygonElement", + "SVGPolylineElement", + "SVGPreserveAspectRatio", + "SVGRadialGradientElement", + "SVGRect", + "SVGRectElement", + "SVGRenderingIntent", + "SVGSVGElement", + "SVGScriptElement", + "SVGSetElement", + "SVGStopElement", + "SVGStringList", + "SVGStyleElement", + "SVGSwitchElement", + "SVGSymbolElement", + "SVGTRefElement", + "SVGTSpanElement", + "SVGTextContentElement", + "SVGTextElement", + "SVGTextPathElement", + "SVGTextPositioningElement", + "SVGTitleElement", + "SVGTransform", + "SVGTransformList", + "SVGUnitTypes", + "SVGUseElement", + "SVGVKernElement", + "SVGViewElement", + "SVGViewSpec", + "SVGZoomAndPan", + "SVGZoomEvent", + "SVG_ANGLETYPE_DEG", + "SVG_ANGLETYPE_GRAD", + "SVG_ANGLETYPE_RAD", + "SVG_ANGLETYPE_UNKNOWN", + "SVG_ANGLETYPE_UNSPECIFIED", + "SVG_CHANNEL_A", + "SVG_CHANNEL_B", + "SVG_CHANNEL_G", + "SVG_CHANNEL_R", + "SVG_CHANNEL_UNKNOWN", + "SVG_COLORTYPE_CURRENTCOLOR", + "SVG_COLORTYPE_RGBCOLOR", + "SVG_COLORTYPE_RGBCOLOR_ICCCOLOR", + "SVG_COLORTYPE_UNKNOWN", + "SVG_EDGEMODE_DUPLICATE", + "SVG_EDGEMODE_NONE", + "SVG_EDGEMODE_UNKNOWN", + "SVG_EDGEMODE_WRAP", + "SVG_FEBLEND_MODE_COLOR", + "SVG_FEBLEND_MODE_COLOR_BURN", + "SVG_FEBLEND_MODE_COLOR_DODGE", + "SVG_FEBLEND_MODE_DARKEN", + "SVG_FEBLEND_MODE_DIFFERENCE", + "SVG_FEBLEND_MODE_EXCLUSION", + "SVG_FEBLEND_MODE_HARD_LIGHT", + "SVG_FEBLEND_MODE_HUE", + "SVG_FEBLEND_MODE_LIGHTEN", + "SVG_FEBLEND_MODE_LUMINOSITY", + "SVG_FEBLEND_MODE_MULTIPLY", + "SVG_FEBLEND_MODE_NORMAL", + "SVG_FEBLEND_MODE_OVERLAY", + "SVG_FEBLEND_MODE_SATURATION", + "SVG_FEBLEND_MODE_SCREEN", + "SVG_FEBLEND_MODE_SOFT_LIGHT", + "SVG_FEBLEND_MODE_UNKNOWN", + "SVG_FECOLORMATRIX_TYPE_HUEROTATE", + "SVG_FECOLORMATRIX_TYPE_LUMINANCETOALPHA", + "SVG_FECOLORMATRIX_TYPE_MATRIX", + "SVG_FECOLORMATRIX_TYPE_SATURATE", + "SVG_FECOLORMATRIX_TYPE_UNKNOWN", + "SVG_FECOMPONENTTRANSFER_TYPE_DISCRETE", + "SVG_FECOMPONENTTRANSFER_TYPE_GAMMA", + "SVG_FECOMPONENTTRANSFER_TYPE_IDENTITY", + "SVG_FECOMPONENTTRANSFER_TYPE_LINEAR", + "SVG_FECOMPONENTTRANSFER_TYPE_TABLE", + "SVG_FECOMPONENTTRANSFER_TYPE_UNKNOWN", + "SVG_FECOMPOSITE_OPERATOR_ARITHMETIC", + "SVG_FECOMPOSITE_OPERATOR_ATOP", + "SVG_FECOMPOSITE_OPERATOR_IN", + "SVG_FECOMPOSITE_OPERATOR_OUT", + "SVG_FECOMPOSITE_OPERATOR_OVER", + "SVG_FECOMPOSITE_OPERATOR_UNKNOWN", + "SVG_FECOMPOSITE_OPERATOR_XOR", + "SVG_INVALID_VALUE_ERR", + "SVG_LENGTHTYPE_CM", + "SVG_LENGTHTYPE_EMS", + "SVG_LENGTHTYPE_EXS", + "SVG_LENGTHTYPE_IN", + "SVG_LENGTHTYPE_MM", + "SVG_LENGTHTYPE_NUMBER", + "SVG_LENGTHTYPE_PC", + "SVG_LENGTHTYPE_PERCENTAGE", + "SVG_LENGTHTYPE_PT", + "SVG_LENGTHTYPE_PX", + "SVG_LENGTHTYPE_UNKNOWN", + "SVG_MARKERUNITS_STROKEWIDTH", + "SVG_MARKERUNITS_UNKNOWN", + "SVG_MARKERUNITS_USERSPACEONUSE", + "SVG_MARKER_ORIENT_ANGLE", + "SVG_MARKER_ORIENT_AUTO", + "SVG_MARKER_ORIENT_UNKNOWN", + "SVG_MASKTYPE_ALPHA", + "SVG_MASKTYPE_LUMINANCE", + "SVG_MATRIX_NOT_INVERTABLE", + "SVG_MEETORSLICE_MEET", + "SVG_MEETORSLICE_SLICE", + "SVG_MEETORSLICE_UNKNOWN", + "SVG_MORPHOLOGY_OPERATOR_DILATE", + "SVG_MORPHOLOGY_OPERATOR_ERODE", + "SVG_MORPHOLOGY_OPERATOR_UNKNOWN", + "SVG_PAINTTYPE_CURRENTCOLOR", + "SVG_PAINTTYPE_NONE", + "SVG_PAINTTYPE_RGBCOLOR", + "SVG_PAINTTYPE_RGBCOLOR_ICCCOLOR", + "SVG_PAINTTYPE_UNKNOWN", + "SVG_PAINTTYPE_URI", + "SVG_PAINTTYPE_URI_CURRENTCOLOR", + "SVG_PAINTTYPE_URI_NONE", + "SVG_PAINTTYPE_URI_RGBCOLOR", + "SVG_PAINTTYPE_URI_RGBCOLOR_ICCCOLOR", + "SVG_PRESERVEASPECTRATIO_NONE", + "SVG_PRESERVEASPECTRATIO_UNKNOWN", + "SVG_PRESERVEASPECTRATIO_XMAXYMAX", + "SVG_PRESERVEASPECTRATIO_XMAXYMID", + "SVG_PRESERVEASPECTRATIO_XMAXYMIN", + "SVG_PRESERVEASPECTRATIO_XMIDYMAX", + "SVG_PRESERVEASPECTRATIO_XMIDYMID", + "SVG_PRESERVEASPECTRATIO_XMIDYMIN", + "SVG_PRESERVEASPECTRATIO_XMINYMAX", + "SVG_PRESERVEASPECTRATIO_XMINYMID", + "SVG_PRESERVEASPECTRATIO_XMINYMIN", + "SVG_SPREADMETHOD_PAD", + "SVG_SPREADMETHOD_REFLECT", + "SVG_SPREADMETHOD_REPEAT", + "SVG_SPREADMETHOD_UNKNOWN", + "SVG_STITCHTYPE_NOSTITCH", + "SVG_STITCHTYPE_STITCH", + "SVG_STITCHTYPE_UNKNOWN", + "SVG_TRANSFORM_MATRIX", + "SVG_TRANSFORM_ROTATE", + "SVG_TRANSFORM_SCALE", + "SVG_TRANSFORM_SKEWX", + "SVG_TRANSFORM_SKEWY", + "SVG_TRANSFORM_TRANSLATE", + "SVG_TRANSFORM_UNKNOWN", + "SVG_TURBULENCE_TYPE_FRACTALNOISE", + "SVG_TURBULENCE_TYPE_TURBULENCE", + "SVG_TURBULENCE_TYPE_UNKNOWN", + "SVG_UNIT_TYPE_OBJECTBOUNDINGBOX", + "SVG_UNIT_TYPE_UNKNOWN", + "SVG_UNIT_TYPE_USERSPACEONUSE", + "SVG_WRONG_TYPE_ERR", + "SVG_ZOOMANDPAN_DISABLE", + "SVG_ZOOMANDPAN_MAGNIFY", + "SVG_ZOOMANDPAN_UNKNOWN", + "SYNC_CONDITION", + "SYNC_FENCE", + "SYNC_FLAGS", + "SYNC_FLUSH_COMMANDS_BIT", + "SYNC_GPU_COMMANDS_COMPLETE", + "SYNC_STATUS", + "SYNTAX_ERR", + "SavedPages", + "Screen", + "ScreenOrientation", + "Script", + "ScriptEngine", + "ScriptEngineBuildVersion", + "ScriptEngineMajorVersion", + "ScriptEngineMinorVersion", + "ScriptProcessorNode", + "ScrollAreaEvent", + "SecurityPolicyViolationEvent", + "Selection", + "Sensor", + "SensorErrorEvent", + "ServiceWorker", + "ServiceWorkerContainer", + "ServiceWorkerMessageEvent", + "ServiceWorkerRegistration", + "SessionDescription", + "Set", + "ShadowRoot", + "SharedArrayBuffer", + "SharedWorker", + "SimpleGestureEvent", + "SourceBuffer", + "SourceBufferList", + "SpeechSynthesis", + "SpeechSynthesisErrorEvent", + "SpeechSynthesisEvent", + "SpeechSynthesisUtterance", + "SpeechSynthesisVoice", + "StaticRange", + "StereoPannerNode", + "StopIteration", + "Storage", + "StorageEvent", + "StorageManager", + "String", + "StyleMedia", + "StylePropertyMap", + "StylePropertyMapReadOnly", + "StyleSheet", + "StyleSheetList", + "StyleSheetPageList", + "SubmitEvent", + "SubtleCrypto", + "Symbol", + "SyncManager", + "SyntaxError", + "TEMPORARY", + "TEXTPATH_METHODTYPE_ALIGN", + "TEXTPATH_METHODTYPE_STRETCH", + "TEXTPATH_METHODTYPE_UNKNOWN", + "TEXTPATH_SPACINGTYPE_AUTO", + "TEXTPATH_SPACINGTYPE_EXACT", + "TEXTPATH_SPACINGTYPE_UNKNOWN", + "TEXTURE", + "TEXTURE0", + "TEXTURE1", + "TEXTURE10", + "TEXTURE11", + "TEXTURE12", + "TEXTURE13", + "TEXTURE14", + "TEXTURE15", + "TEXTURE16", + "TEXTURE17", + "TEXTURE18", + "TEXTURE19", + "TEXTURE2", + "TEXTURE20", + "TEXTURE21", + "TEXTURE22", + "TEXTURE23", + "TEXTURE24", + "TEXTURE25", + "TEXTURE26", + "TEXTURE27", + "TEXTURE28", + "TEXTURE29", + "TEXTURE3", + "TEXTURE30", + "TEXTURE31", + "TEXTURE4", + "TEXTURE5", + "TEXTURE6", + "TEXTURE7", + "TEXTURE8", + "TEXTURE9", + "TEXTURE_2D", + "TEXTURE_2D_ARRAY", + "TEXTURE_3D", + "TEXTURE_BASE_LEVEL", + "TEXTURE_BINDING_2D", + "TEXTURE_BINDING_2D_ARRAY", + "TEXTURE_BINDING_3D", + "TEXTURE_BINDING_CUBE_MAP", + "TEXTURE_COMPARE_FUNC", + "TEXTURE_COMPARE_MODE", + "TEXTURE_CUBE_MAP", + "TEXTURE_CUBE_MAP_NEGATIVE_X", + "TEXTURE_CUBE_MAP_NEGATIVE_Y", + "TEXTURE_CUBE_MAP_NEGATIVE_Z", + "TEXTURE_CUBE_MAP_POSITIVE_X", + "TEXTURE_CUBE_MAP_POSITIVE_Y", + "TEXTURE_CUBE_MAP_POSITIVE_Z", + "TEXTURE_IMMUTABLE_FORMAT", + "TEXTURE_IMMUTABLE_LEVELS", + "TEXTURE_MAG_FILTER", + "TEXTURE_MAX_ANISOTROPY_EXT", + "TEXTURE_MAX_LEVEL", + "TEXTURE_MAX_LOD", + "TEXTURE_MIN_FILTER", + "TEXTURE_MIN_LOD", + "TEXTURE_WRAP_R", + "TEXTURE_WRAP_S", + "TEXTURE_WRAP_T", + "TEXT_NODE", + "THROTTLED", + "TIMEOUT", + "TIMEOUT_ERR", + "TIMEOUT_EXPIRED", + "TIMEOUT_IGNORED", + "TOO_LARGE_ERR", + "TRANSACTION_INACTIVE_ERR", + "TRANSFORM_FEEDBACK", + "TRANSFORM_FEEDBACK_ACTIVE", + "TRANSFORM_FEEDBACK_BINDING", + "TRANSFORM_FEEDBACK_BUFFER", + "TRANSFORM_FEEDBACK_BUFFER_BINDING", + "TRANSFORM_FEEDBACK_BUFFER_MODE", + "TRANSFORM_FEEDBACK_BUFFER_SIZE", + "TRANSFORM_FEEDBACK_BUFFER_START", + "TRANSFORM_FEEDBACK_PAUSED", + "TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN", + "TRANSFORM_FEEDBACK_VARYINGS", + "TRIANGLE", + "TRIANGLES", + "TRIANGLE_FAN", + "TRIANGLE_STRIP", + "TYPE_BACK_FORWARD", + "TYPE_ERR", + "TYPE_MISMATCH_ERR", + "TYPE_NAVIGATE", + "TYPE_RELOAD", + "TYPE_RESERVED", + "Table", + "TaskAttributionTiming", + "Text", + "TextDecoder", + "TextDecoderStream", + "TextEncoder", + "TextEncoderStream", + "TextEvent", + "TextMetrics", + "TextRange", + "TextRangeCollection", + "TextTrack", + "TextTrackCue", + "TextTrackCueList", + "TextTrackList", + "TimeEvent", + "TimeRanges", + "Touch", + "TouchEvent", + "TouchList", + "TrackEvent", + "TransformStream", + "TransitionEvent", + "TreeWalker", + "TrustedHTML", + "TrustedScript", + "TrustedScriptURL", + "TrustedTypePolicy", + "TrustedTypePolicyFactory", + "TypeError", + "U2F", + "UIEvent", + "UNCACHED", + "UNIFORM_ARRAY_STRIDE", + "UNIFORM_BLOCK_ACTIVE_UNIFORMS", + "UNIFORM_BLOCK_ACTIVE_UNIFORM_INDICES", + "UNIFORM_BLOCK_BINDING", + "UNIFORM_BLOCK_DATA_SIZE", + "UNIFORM_BLOCK_INDEX", + "UNIFORM_BLOCK_REFERENCED_BY_FRAGMENT_SHADER", + "UNIFORM_BLOCK_REFERENCED_BY_VERTEX_SHADER", + "UNIFORM_BUFFER", + "UNIFORM_BUFFER_BINDING", + "UNIFORM_BUFFER_OFFSET_ALIGNMENT", + "UNIFORM_BUFFER_SIZE", + "UNIFORM_BUFFER_START", + "UNIFORM_IS_ROW_MAJOR", + "UNIFORM_MATRIX_STRIDE", + "UNIFORM_OFFSET", + "UNIFORM_SIZE", + "UNIFORM_TYPE", + "UNKNOWN_ERR", + "UNKNOWN_RULE", + "UNMASKED_RENDERER_WEBGL", + "UNMASKED_VENDOR_WEBGL", + "UNORDERED_NODE_ITERATOR_TYPE", + "UNORDERED_NODE_SNAPSHOT_TYPE", + "UNPACK_ALIGNMENT", + "UNPACK_COLORSPACE_CONVERSION_WEBGL", + "UNPACK_FLIP_Y_WEBGL", + "UNPACK_IMAGE_HEIGHT", + "UNPACK_PREMULTIPLY_ALPHA_WEBGL", + "UNPACK_ROW_LENGTH", + "UNPACK_SKIP_IMAGES", + "UNPACK_SKIP_PIXELS", + "UNPACK_SKIP_ROWS", + "UNSCHEDULED_STATE", + "UNSENT", + "UNSIGNALED", + "UNSIGNED_BYTE", + "UNSIGNED_INT", + "UNSIGNED_INT_10F_11F_11F_REV", + "UNSIGNED_INT_24_8", + "UNSIGNED_INT_2_10_10_10_REV", + "UNSIGNED_INT_5_9_9_9_REV", + "UNSIGNED_INT_SAMPLER_2D", + "UNSIGNED_INT_SAMPLER_2D_ARRAY", + "UNSIGNED_INT_SAMPLER_3D", + "UNSIGNED_INT_SAMPLER_CUBE", + "UNSIGNED_INT_VEC2", + "UNSIGNED_INT_VEC3", + "UNSIGNED_INT_VEC4", + "UNSIGNED_NORMALIZED", + "UNSIGNED_SHORT", + "UNSIGNED_SHORT_4_4_4_4", + "UNSIGNED_SHORT_5_5_5_1", + "UNSIGNED_SHORT_5_6_5", + "UNSPECIFIED_EVENT_TYPE_ERR", + "UPDATE", + "UPDATEREADY", + "UPDATE_AVAILABLE", + "URIError", + "URL", + "URLSearchParams", + "URLUnencoded", + "URL_MISMATCH_ERR", + "USB", + "USBAlternateInterface", + "USBConfiguration", + "USBConnectionEvent", + "USBDevice", + "USBEndpoint", + "USBInTransferResult", + "USBInterface", + "USBIsochronousInTransferPacket", + "USBIsochronousInTransferResult", + "USBIsochronousOutTransferPacket", + "USBIsochronousOutTransferResult", + "USBOutTransferResult", + "UTC", + "Uint16Array", + "Uint32Array", + "Uint8Array", + "Uint8ClampedArray", + "UserActivation", + "UserMessageHandler", + "UserMessageHandlersNamespace", + "UserProximityEvent", + "VALIDATE_STATUS", + "VALIDATION_ERR", + "VARIABLES_RULE", + "VBArray", + "VENDOR", + "VERSION", + "VERSION_CHANGE", + "VERSION_ERR", + "VERTEX_ARRAY_BINDING", + "VERTEX_ATTRIB_ARRAY_BUFFER_BINDING", + "VERTEX_ATTRIB_ARRAY_DIVISOR", + "VERTEX_ATTRIB_ARRAY_DIVISOR_ANGLE", + "VERTEX_ATTRIB_ARRAY_ENABLED", + "VERTEX_ATTRIB_ARRAY_INTEGER", + "VERTEX_ATTRIB_ARRAY_NORMALIZED", + "VERTEX_ATTRIB_ARRAY_POINTER", + "VERTEX_ATTRIB_ARRAY_SIZE", + "VERTEX_ATTRIB_ARRAY_STRIDE", + "VERTEX_ATTRIB_ARRAY_TYPE", + "VERTEX_SHADER", + "VERTICAL", + "VERTICAL_AXIS", + "VER_ERR", + "VIEWPORT", + "VIEWPORT_RULE", + "VRDisplay", + "VRDisplayCapabilities", + "VRDisplayEvent", + "VREyeParameters", + "VRFieldOfView", + "VRFrameData", + "VRPose", + "VRStageParameters", + "VTTCue", + "VTTRegion", + "ValidityState", + "VideoPlaybackQuality", + "VideoStreamTrack", + "VideoTrack", + "VideoTrackList", + "VisualViewport", + "WAIT_FAILED", + "WEBGL_compressed_texture_s3tc", + "WEBGL_debug_renderer_info", + "WEBKIT_FILTER_RULE", + "WEBKIT_FORCE_AT_FORCE_MOUSE_DOWN", + "WEBKIT_FORCE_AT_MOUSE_DOWN", + "WEBKIT_KEYFRAMES_RULE", + "WEBKIT_KEYFRAME_RULE", + "WEBKIT_REGION_RULE", + "WIN", + "WRONG_DOCUMENT_ERR", + "WakeLock", + "WakeLockSentinel", + "WaveShaperNode", + "WeakMap", + "WeakRef", + "WeakSet", + "WebAssembly", + "WebGL2RenderingContext", + "WebGLActiveInfo", + "WebGLBuffer", + "WebGLContextEvent", + "WebGLFramebuffer", + "WebGLObject", + "WebGLProgram", + "WebGLQuery", + "WebGLRenderbuffer", + "WebGLRenderingContext", + "WebGLSampler", + "WebGLShader", + "WebGLShaderPrecisionFormat", + "WebGLSync", + "WebGLTexture", + "WebGLTransformFeedback", + "WebGLUniformLocation", + "WebGLVertexArray", + "WebGLVertexArrayObject", + "WebKit built-in PDF", + "WebKitAnimationEvent", + "WebKitBlobBuilder", + "WebKitCSSFilterRule", + "WebKitCSSFilterValue", + "WebKitCSSKeyframeRule", + "WebKitCSSKeyframesRule", + "WebKitCSSMatrix", + "WebKitCSSRegionRule", + "WebKitCSSTransformValue", + "WebKitDataCue", + "WebKitGamepad", + "WebKitMediaKeyError", + "WebKitMediaKeyMessageEvent", + "WebKitMediaKeyNeededEvent", + "WebKitMediaKeySession", + "WebKitMediaKeys", + "WebKitMediaSource", + "WebKitMutationObserver", + "WebKitNamespace", + "WebKitPlaybackTargetAvailabilityEvent", + "WebKitPoint", + "WebKitShadowRoot", + "WebKitSourceBuffer", + "WebKitSourceBufferList", + "WebKitTransitionEvent", + "WebSocket", + "WebkitAlignContent", + "WebkitAlignItems", + "WebkitAlignSelf", + "WebkitAnimation", + "WebkitAnimationDelay", + "WebkitAnimationDirection", + "WebkitAnimationDuration", + "WebkitAnimationFillMode", + "WebkitAnimationIterationCount", + "WebkitAnimationName", + "WebkitAnimationPlayState", + "WebkitAnimationTimingFunction", + "WebkitAppearance", + "WebkitBackfaceVisibility", + "WebkitBackgroundClip", + "WebkitBackgroundOrigin", + "WebkitBackgroundSize", + "WebkitBorderBottomLeftRadius", + "WebkitBorderBottomRightRadius", + "WebkitBorderImage", + "WebkitBorderRadius", + "WebkitBorderTopLeftRadius", + "WebkitBorderTopRightRadius", + "WebkitBoxAlign", + "WebkitBoxDirection", + "WebkitBoxFlex", + "WebkitBoxOrdinalGroup", + "WebkitBoxOrient", + "WebkitBoxPack", + "WebkitBoxShadow", + "WebkitBoxSizing", + "WebkitFilter", + "WebkitFlex", + "WebkitFlexBasis", + "WebkitFlexDirection", + "WebkitFlexFlow", + "WebkitFlexGrow", + "WebkitFlexShrink", + "WebkitFlexWrap", + "WebkitJustifyContent", + "WebkitLineClamp", + "WebkitMask", + "WebkitMaskClip", + "WebkitMaskComposite", + "WebkitMaskImage", + "WebkitMaskOrigin", + "WebkitMaskPosition", + "WebkitMaskPositionX", + "WebkitMaskPositionY", + "WebkitMaskRepeat", + "WebkitMaskSize", + "WebkitOrder", + "WebkitPerspective", + "WebkitPerspectiveOrigin", + "WebkitTextFillColor", + "WebkitTextSizeAdjust", + "WebkitTextStroke", + "WebkitTextStrokeColor", + "WebkitTextStrokeWidth", + "WebkitTransform", + "WebkitTransformOrigin", + "WebkitTransformStyle", + "WebkitTransition", + "WebkitTransitionDelay", + "WebkitTransitionDuration", + "WebkitTransitionProperty", + "WebkitTransitionTimingFunction", + "WebkitUserSelect", + "WheelEvent", + "Window", + "Windows Media Player Plug-in Dynamic Link Library", + "Windows Presentation Foundation", + "Worker", + "Worklet", + "WritableStream", + "WritableStreamDefaultWriter", + "X86_32", + "X86_64", + "XMLDocument", + "XMLHttpRequest", + "XMLHttpRequestEventTarget", + "XMLHttpRequestException", + "XMLHttpRequestProgressEvent", + "XMLHttpRequestUpload", + "XMLSerializer", + "XMLStylesheetProcessingInstruction", + "XPathEvaluator", + "XPathException", + "XPathExpression", + "XPathNSResolver", + "XPathResult", + "XR", + "XRBoundedReferenceSpace", + "XRDOMOverlayState", + "XRFrame", + "XRHitTestResult", + "XRHitTestSource", + "XRInputSource", + "XRInputSourceArray", + "XRInputSourceEvent", + "XRInputSourcesChangeEvent", + "XRLayer", + "XRPose", + "XRRay", + "XRReferenceSpace", + "XRReferenceSpaceEvent", + "XRRenderState", + "XRRigidTransform", + "XRSession", + "XRSessionEvent", + "XRSpace", + "XRSystem", + "XRTransientInputHitTestResult", + "XRTransientInputHitTestSource", + "XRView", + "XRViewerPose", + "XRViewport", + "XRWebGLLayer", + "XSLTProcessor", + "ZERO", + "_XD0M_", + "_YD0M_", + "__defineGetter__", + "__defineSetter__", + "__lookupGetter__", + "__lookupSetter__", + "__opera", + "__proto__", + "__relevantExtensionKeys", + "_browserjsran", + "a", + "aLink", + "abbr", + "abort", + "aborted", + "abs", + "absolute", + "acceleration", + "accelerationIncludingGravity", + "accelerator", + "accept", + "acceptCharset", + "acceptNode", + "accessKey", + "accessKeyLabel", + "accuracy", + "acos", + "acosh", + "action", + "actionURL", + "actions", + "activated", + "active", + "activeCues", + "activeElement", + "activeSourceBuffers", + "activeSourceCount", + "activeTexture", + "activeVRDisplays", + "actualBoundingBoxAscent", + "actualBoundingBoxDescent", + "actualBoundingBoxLeft", + "actualBoundingBoxRight", + "add", + "addAll", + "addBehavior", + "addCandidate", + "addColorStop", + "addCue", + "addElement", + "addEventListener", + "addFilter", + "addFromString", + "addFromUri", + "addIceCandidate", + "addImport", + "addListener", + "addModule", + "addNamed", + "addPageRule", + "addPath", + "addPointer", + "addRange", + "addRegion", + "addRule", + "addRules", + "addSearchEngine", + "addSourceBuffer", + "addStream", + "addTextTrack", + "addTrack", + "addTransceiver", + "addWakeLockListener", + "added", + "addedNodes", + "additionalName", + "additiveSymbols", + "addons", + "address", + "addressLine", + "adoptNode", + "adoptText", + "adoptedCallback", + "adoptedStyleSheets", + "adr", + "advance", + "after", + "album", + "alert", + "algorithm", + "align", + "align-content", + "align-items", + "align-self", + "alignContent", + "alignItems", + "alignSelf", + "alignmentBaseline", + "alinkColor", + "all", + "allSettled", + "allow", + "allowFullscreen", + "allowPaymentRequest", + "allowTransparency", + "allowedDirections", + "allowedFeatures", + "allowsFeature", + "alpha", + "alphabeticBaseline", + "alt", + "altGraphKey", + "altHtml", + "altKey", + "altLeft", + "alternate", + "alternateSetting", + "alternates", + "altitude", + "altitudeAccuracy", + "amplitude", + "ancestorOrigins", + "anchor", + "anchorNode", + "anchorOffset", + "anchors", + "and", + "angle", + "angularAcceleration", + "angularVelocity", + "animVal", + "animate", + "animatedInstanceRoot", + "animatedNormalizedPathSegList", + "animatedPathSegList", + "animatedPoints", + "animation", + "animation-delay", + "animation-direction", + "animation-duration", + "animation-fill-mode", + "animation-iteration-count", + "animation-name", + "animation-play-state", + "animation-timing-function", + "animationDelay", + "animationDirection", + "animationDuration", + "animationFillMode", + "animationIterationCount", + "animationName", + "animationPlayState", + "animationStartTime", + "animationTimingFunction", + "animationsPaused", + "anniversary", + "antialias", + "any", + "app", + "appCodeName", + "appMinorVersion", + "appName", + "appNotifications", + "appVersion", + "appearance", + "append", + "appendBuffer", + "appendChild", + "appendData", + "appendItem", + "appendMedium", + "appendNamed", + "appendRule", + "appendStream", + "appendWindowEnd", + "appendWindowStart", + "appleTrailingWord", + "applets", + "application/apple-default-browser", + "application/asx", + "application/java-deployment-toolkit", + "application/pdf", + "application/postscript", + "application/x-drm", + "application/x-drm-v2", + "application/x-google-chrome-pdf", + "application/x-java-applet", + "application/x-java-applet;deploy=10.25.2", + "application/x-java-applet;javafx=2.2.25", + "application/x-java-applet;jpi-version=1.7.0_25", + "application/x-java-applet;version=1.1", + "application/x-java-applet;version=1.1.1", + "application/x-java-applet;version=1.1.2", + "application/x-java-applet;version=1.1.3", + "application/x-java-applet;version=1.2", + "application/x-java-applet;version=1.2.1", + "application/x-java-applet;version=1.2.2", + "application/x-java-applet;version=1.3", + "application/x-java-applet;version=1.3.1", + "application/x-java-applet;version=1.4", + "application/x-java-applet;version=1.4.1", + "application/x-java-applet;version=1.4.2", + "application/x-java-applet;version=1.5", + "application/x-java-applet;version=1.6", + "application/x-java-applet;version=1.7", + "application/x-java-bean", + "application/x-java-bean;jpi-version=1.7.0_25", + "application/x-java-bean;version=1.1", + "application/x-java-bean;version=1.1.1", + "application/x-java-bean;version=1.1.2", + "application/x-java-bean;version=1.1.3", + "application/x-java-bean;version=1.2", + "application/x-java-bean;version=1.2.1", + "application/x-java-bean;version=1.2.2", + "application/x-java-bean;version=1.3", + "application/x-java-bean;version=1.3.1", + "application/x-java-bean;version=1.4", + "application/x-java-bean;version=1.4.1", + "application/x-java-bean;version=1.4.2", + "application/x-java-bean;version=1.5", + "application/x-java-bean;version=1.6", + "application/x-java-bean;version=1.7", + "application/x-java-vm", + "application/x-java-vm-npruntime", + "application/x-mplayer2", + "application/x-ms-xbap", + "application/x-nacl", + "application/x-pnacl", + "application/xaml+xml", + "applicationCache", + "applicationServerKey", + "apply", + "applyConstraints", + "applyElement", + "arc", + "arcTo", + "archive", + "areas", + "arguments", + "aria-activedescendant", + "aria-busy", + "aria-checked", + "aria-controls", + "aria-describedby", + "aria-disabled", + "aria-expanded", + "aria-flowto", + "aria-haspopup", + "aria-hidden", + "aria-invalid", + "aria-labelledby", + "aria-level", + "aria-live", + "aria-multiselectable", + "aria-owns", + "aria-posinset", + "aria-pressed", + "aria-readonly", + "aria-relevant", + "aria-required", + "aria-secret", + "aria-selected", + "aria-setsize", + "aria-valuemax", + "aria-valuemin", + "aria-valuenow", + "ariaAtomic", + "ariaAutoComplete", + "ariaBusy", + "ariaChecked", + "ariaColCount", + "ariaColIndex", + "ariaColSpan", + "ariaCurrent", + "ariaDescription", + "ariaDisabled", + "ariaExpanded", + "ariaHasPopup", + "ariaHidden", + "ariaKeyShortcuts", + "ariaLabel", + "ariaLevel", + "ariaLive", + "ariaModal", + "ariaMultiLine", + "ariaMultiSelectable", + "ariaOrientation", + "ariaPlaceholder", + "ariaPosInSet", + "ariaPressed", + "ariaReadOnly", + "ariaRelevant", + "ariaRequired", + "ariaRoleDescription", + "ariaRowCount", + "ariaRowIndex", + "ariaRowSpan", + "ariaSelected", + "ariaSetSize", + "ariaSort", + "ariaValueMax", + "ariaValueMin", + "ariaValueNow", + "ariaValueText", + "arrayBuffer", + "artist", + "artwork", + "as", + "asIntN", + "asUintN", + "asin", + "asinh", + "assert", + "assign", + "assignedElements", + "assignedNodes", + "assignedSlot", + "async", + "asyncIterator", + "atEnd", + "atan", + "atan2", + "atanh", + "atob", + "atomic", + "attachEvent", + "attachInternals", + "attachShader", + "attachShadow", + "attachments", + "attack", + "attestationObject", + "attrChange", + "attrName", + "attributeChangedCallback", + "attributeFilter", + "attributeName", + "attributeNamespace", + "attributeOldValue", + "attributeStyleMap", + "attributes", + "attribution", + "audio/x-ms-wax", + "audio/x-ms-wma", + "audioBitsPerSecond", + "audioTracks", + "audioWorklet", + "authenticatedSignedWrites", + "authenticatorData", + "autoIncrement", + "autobuffer", + "autocapitalize", + "autocomplete", + "autocorrect", + "autofocus", + "automationRate", + "autoplay", + "availHeight", + "availLeft", + "availTop", + "availWidth", + "availability", + "available", + "aversion", + "ax", + "axes", + "axis", + "ay", + "azimuth", + "b", + "back", + "backdropFilter", + "backface-visibility", + "backfaceVisibility", + "background", + "background-attachment", + "background-blend-mode", + "background-clip", + "background-color", + "background-image", + "background-origin", + "background-position", + "background-position-x", + "background-position-y", + "background-repeat", + "background-size", + "backgroundAttachment", + "backgroundBlendMode", + "backgroundClip", + "backgroundColor", + "backgroundFetch", + "backgroundImage", + "backgroundOrigin", + "backgroundPosition", + "backgroundPositionX", + "backgroundPositionY", + "backgroundRepeat", + "backgroundRepeatX", + "backgroundRepeatY", + "backgroundSize", + "badInput", + "badge", + "balance", + "baseFrequencyX", + "baseFrequencyY", + "baseLatency", + "baseLayer", + "baseName", + "baseNode", + "baseOffset", + "baseURI", + "baseVal", + "baselineShift", + "battery", + "bday", + "before", + "beginElement", + "beginElementAt", + "beginPath", + "beginQuery", + "beginTransformFeedback", + "behavior", + "behaviorCookie", + "behaviorPart", + "behaviorUrns", + "beta", + "bezierCurveTo", + "bgColor", + "bgProperties", + "bias", + "big", + "binaryType", + "bind", + "bindAttribLocation", + "bindBuffer", + "bindBufferBase", + "bindBufferRange", + "bindFramebuffer", + "bindRenderbuffer", + "bindSampler", + "bindTexture", + "bindTransformFeedback", + "bindVertexArray", + "blendColor", + "blendEquation", + "blendEquationSeparate", + "blendFunc", + "blendFuncSeparate", + "blink", + "blitFramebuffer", + "blob", + "block-size", + "blockDirection", + "blockSize", + "blockedURI", + "blue", + "bluetooth", + "blur", + "body", + "bodyUsed", + "bold", + "bookmarks", + "booleanValue", + "border", + "border-block", + "border-block-color", + "border-block-end", + "border-block-end-color", + "border-block-end-style", + "border-block-end-width", + "border-block-start", + "border-block-start-color", + "border-block-start-style", + "border-block-start-width", + "border-block-style", + "border-block-width", + "border-bottom", + "border-bottom-color", + "border-bottom-left-radius", + "border-bottom-right-radius", + "border-bottom-style", + "border-bottom-width", + "border-collapse", + "border-color", + "border-end-end-radius", + "border-end-start-radius", + "border-image", + "border-image-outset", + "border-image-repeat", + "border-image-slice", + "border-image-source", + "border-image-width", + "border-inline", + "border-inline-color", + "border-inline-end", + "border-inline-end-color", + "border-inline-end-style", + "border-inline-end-width", + "border-inline-start", + "border-inline-start-color", + "border-inline-start-style", + "border-inline-start-width", + "border-inline-style", + "border-inline-width", + "border-left", + "border-left-color", + "border-left-style", + "border-left-width", + "border-radius", + "border-right", + "border-right-color", + "border-right-style", + "border-right-width", + "border-spacing", + "border-start-end-radius", + "border-start-start-radius", + "border-style", + "border-top", + "border-top-color", + "border-top-left-radius", + "border-top-right-radius", + "border-top-style", + "border-top-width", + "border-width", + "borderBlock", + "borderBlockColor", + "borderBlockEnd", + "borderBlockEndColor", + "borderBlockEndStyle", + "borderBlockEndWidth", + "borderBlockStart", + "borderBlockStartColor", + "borderBlockStartStyle", + "borderBlockStartWidth", + "borderBlockStyle", + "borderBlockWidth", + "borderBottom", + "borderBottomColor", + "borderBottomLeftRadius", + "borderBottomRightRadius", + "borderBottomStyle", + "borderBottomWidth", + "borderBoxSize", + "borderCollapse", + "borderColor", + "borderColorDark", + "borderColorLight", + "borderEndEndRadius", + "borderEndStartRadius", + "borderImage", + "borderImageOutset", + "borderImageRepeat", + "borderImageSlice", + "borderImageSource", + "borderImageWidth", + "borderInline", + "borderInlineColor", + "borderInlineEnd", + "borderInlineEndColor", + "borderInlineEndStyle", + "borderInlineEndWidth", + "borderInlineStart", + "borderInlineStartColor", + "borderInlineStartStyle", + "borderInlineStartWidth", + "borderInlineStyle", + "borderInlineWidth", + "borderLeft", + "borderLeftColor", + "borderLeftStyle", + "borderLeftWidth", + "borderRadius", + "borderRight", + "borderRightColor", + "borderRightStyle", + "borderRightWidth", + "borderSpacing", + "borderStartEndRadius", + "borderStartStartRadius", + "borderStyle", + "borderTop", + "borderTopColor", + "borderTopLeftRadius", + "borderTopRightRadius", + "borderTopStyle", + "borderTopWidth", + "borderWidth", + "bottom", + "bottomMargin", + "bound", + "boundElements", + "boundingClientRect", + "boundingHeight", + "boundingLeft", + "boundingTop", + "boundingWidth", + "bounds", + "boundsGeometry", + "box-decoration-break", + "box-shadow", + "box-sizing", + "boxDecorationBreak", + "boxShadow", + "boxSizing", + "break-after", + "break-before", + "break-inside", + "breakAfter", + "breakBefore", + "breakInside", + "breakType", + "broadcast", + "browserLanguage", + "btoa", + "bubbles", + "buffer", + "bufferData", + "bufferDepth", + "bufferSize", + "bufferSubData", + "buffered", + "bufferedAmount", + "bufferedAmountLowThreshold", + "bufferedRendering", + "buildID", + "buildNumber", + "button", + "buttonID", + "buttons", + "byteLength", + "byteOffset", + "bytesWritten", + "c", + "cache", + "caches", + "calendar", + "call", + "caller", + "canBeFormatted", + "canBeMounted", + "canBeShared", + "canHaveChildren", + "canHaveHTML", + "canInsertDTMF", + "canMakePayment", + "canPlayType", + "canPresent", + "canTrickleIceCandidates", + "cancel", + "cancelAndHoldAtTime", + "cancelAnimationFrame", + "cancelBubble", + "cancelIdleCallback", + "cancelScheduledValues", + "cancelVideoFrameCallback", + "cancelWatchAvailability", + "cancelable", + "candidate", + "canonicalUUID", + "canvas", + "capabilities", + "caption", + "caption-side", + "captionSide", + "capture", + "captureEvents", + "captureStackTrace", + "captureStream", + "caret-color", + "caretBidiLevel", + "caretColor", + "caretPositionFromPoint", + "caretRangeFromPoint", + "caseFirst", + "cast", + "catch", + "category", + "cbrt", + "cd", + "ceil", + "cellIndex", + "cellPadding", + "cellSpacing", + "cells", + "ch", + "chOff", + "chain", + "challenge", + "changeType", + "changeVersion", + "changedTouches", + "channel", + "channelCount", + "channelCountMode", + "channelInterpretation", + "char", + "charAt", + "charCode", + "charCodeAt", + "charIndex", + "charLength", + "characterData", + "characterDataOldValue", + "characterSet", + "characteristic", + "charging", + "chargingTime", + "charset", + "check", + "checkEnclosure", + "checkFramebufferStatus", + "checkInstalled", + "checkIntersection", + "checkValidity", + "checked", + "childElementCount", + "childList", + "childNodes", + "children", + "chrome", + "ciphertext", + "cite", + "city", + "claimInterface", + "claimed", + "classList", + "className", + "classid", + "clear", + "clearAppBadge", + "clearAttributes", + "clearBufferfi", + "clearBufferfv", + "clearBufferiv", + "clearBufferuiv", + "clearColor", + "clearData", + "clearDepth", + "clearHalt", + "clearImmediate", + "clearInterval", + "clearLiveSeekableRange", + "clearMarks", + "clearMeasures", + "clearParameters", + "clearRect", + "clearResourceTimings", + "clearShadow", + "clearStencil", + "clearTimeout", + "clearWatch", + "click", + "clickCount", + "clientDataJSON", + "clientHeight", + "clientInformation", + "clientLeft", + "clientRect", + "clientRects", + "clientTop", + "clientWaitSync", + "clientWidth", + "clientX", + "clientY", + "clip", + "clip-path", + "clip-rule", + "clipBottom", + "clipLeft", + "clipPath", + "clipPathUnits", + "clipRight", + "clipRule", + "clipTop", + "clipboard", + "clipboardData", + "clone", + "cloneContents", + "cloneNode", + "cloneRange", + "close", + "closePath", + "closed", + "closest", + "clz", + "clz32", + "cm", + "cmp", + "code", + "codeBase", + "codePointAt", + "codeType", + "colSpan", + "collapse", + "collapseToEnd", + "collapseToStart", + "collapsed", + "collation", + "collect", + "colno", + "color", + "color-adjust", + "color-interpolation", + "color-interpolation-filters", + "colorAdjust", + "colorDepth", + "colorInterpolation", + "colorInterpolationFilters", + "colorMask", + "colorProfile", + "colorRendering", + "colorScheme", + "colorType", + "cols", + "column", + "column-count", + "column-fill", + "column-gap", + "column-rule", + "column-rule-color", + "column-rule-style", + "column-rule-width", + "column-span", + "column-width", + "columnCount", + "columnFill", + "columnGap", + "columnNumber", + "columnRule", + "columnRuleColor", + "columnRuleStyle", + "columnRuleWidth", + "columnSpan", + "columnWidth", + "columns", + "command", + "commit", + "commitLoadTime", + "commitPreferences", + "commitStyles", + "commonAncestorContainer", + "compact", + "compare", + "compareBoundaryPoints", + "compareDocumentPosition", + "compareEndPoints", + "compareExchange", + "compareNode", + "comparePoint", + "compatMode", + "compatible", + "compile", + "compileShader", + "compileStreaming", + "complete", + "component", + "componentFromPoint", + "composed", + "composedPath", + "composite", + "compositionEndOffset", + "compositionStartOffset", + "compressedTexImage2D", + "compressedTexImage3D", + "compressedTexSubImage2D", + "compressedTexSubImage3D", + "computedStyleMap", + "concat", + "conditionText", + "coneInnerAngle", + "coneOuterAngle", + "coneOuterGain", + "configuration", + "configurationName", + "configurationValue", + "configurations", + "confirm", + "confirmComposition", + "confirmSiteSpecificTrackingException", + "confirmWebWideTrackingException", + "connect", + "connectEnd", + "connectStart", + "connected", + "connectedCallback", + "connection", + "connectionInfo", + "connectionList", + "connectionSpeed", + "connectionState", + "connections", + "console", + "consoleHistory", + "consolidate", + "constraint", + "constrictionActive", + "construct", + "constructor", + "contactID", + "contain", + "containIntrinsicSize", + "containerId", + "containerName", + "containerSrc", + "containerType", + "contains", + "containsNode", + "content", + "contentBoxSize", + "contentDocument", + "contentEditable", + "contentHint", + "contentOverflow", + "contentRect", + "contentScriptType", + "contentStyleType", + "contentType", + "contentWindow", + "context", + "contextMenu", + "contextmenu", + "continue", + "continuePrimaryKey", + "continuous", + "control", + "controlTransferIn", + "controlTransferOut", + "controller", + "controls", + "controlsList", + "convertToBlob", + "convertToSpecifiedUnits", + "cookie", + "cookieEnabled", + "coords", + "copyBufferSubData", + "copyFromChannel", + "copyTexImage2D", + "copyTexSubImage2D", + "copyTexSubImage3D", + "copyToChannel", + "copyWithin", + "correspondingElement", + "correspondingUseElement", + "corruptedVideoFrames", + "cos", + "cosh", + "count", + "countReset", + "counter-increment", + "counter-reset", + "counter-set", + "counterIncrement", + "counterReset", + "counterSet", + "country", + "cpuClass", + "cpuSleepAllowed", + "create", + "createAnalyser", + "createAnswer", + "createAttribute", + "createAttributeNS", + "createBiquadFilter", + "createBuffer", + "createBufferSource", + "createCDATASection", + "createCSSStyleSheet", + "createCaption", + "createChannelMerger", + "createChannelSplitter", + "createComment", + "createConstantSource", + "createContextualFragment", + "createControlRange", + "createConvolver", + "createDTMFSender", + "createDataChannel", + "createDelay", + "createDelayNode", + "createDocument", + "createDocumentFragment", + "createDocumentType", + "createDynamicsCompressor", + "createElement", + "createElementNS", + "createEntityReference", + "createEvent", + "createEventObject", + "createExpression", + "createFramebuffer", + "createFunction", + "createGain", + "createGainNode", + "createHTML", + "createHTMLDocument", + "createIIRFilter", + "createImageBitmap", + "createImageData", + "createIndex", + "createJavaScriptNode", + "createLinearGradient", + "createMediaElementSource", + "createMediaKeys", + "createMediaStreamDestination", + "createMediaStreamSource", + "createMediaStreamTrackSource", + "createMutableFile", + "createNSResolver", + "createNodeIterator", + "createNotification", + "createObjectStore", + "createObjectURL", + "createOffer", + "createOscillator", + "createPanner", + "createPattern", + "createPeriodicWave", + "createPolicy", + "createPopup", + "createProcessingInstruction", + "createProgram", + "createQuery", + "createRadialGradient", + "createRange", + "createRangeCollection", + "createReader", + "createRenderbuffer", + "createSVGAngle", + "createSVGLength", + "createSVGMatrix", + "createSVGNumber", + "createSVGPathSegArcAbs", + "createSVGPathSegArcRel", + "createSVGPathSegClosePath", + "createSVGPathSegCurvetoCubicAbs", + "createSVGPathSegCurvetoCubicRel", + "createSVGPathSegCurvetoCubicSmoothAbs", + "createSVGPathSegCurvetoCubicSmoothRel", + "createSVGPathSegCurvetoQuadraticAbs", + "createSVGPathSegCurvetoQuadraticRel", + "createSVGPathSegCurvetoQuadraticSmoothAbs", + "createSVGPathSegCurvetoQuadraticSmoothRel", + "createSVGPathSegLinetoAbs", + "createSVGPathSegLinetoHorizontalAbs", + "createSVGPathSegLinetoHorizontalRel", + "createSVGPathSegLinetoRel", + "createSVGPathSegLinetoVerticalAbs", + "createSVGPathSegLinetoVerticalRel", + "createSVGPathSegMovetoAbs", + "createSVGPathSegMovetoRel", + "createSVGPoint", + "createSVGRect", + "createSVGTransform", + "createSVGTransformFromMatrix", + "createSampler", + "createScript", + "createScriptProcessor", + "createScriptURL", + "createSession", + "createShader", + "createShadowRoot", + "createStereoPanner", + "createStyleSheet", + "createTBody", + "createTFoot", + "createTHead", + "createTextNode", + "createTextRange", + "createTexture", + "createTouch", + "createTouchList", + "createTransformFeedback", + "createTreeWalker", + "createVertexArray", + "createWaveShaper", + "creationTime", + "credentials", + "crossOrigin", + "crossOriginIsolated", + "crypto", + "csi", + "csp", + "cssFloat", + "cssRules", + "cssText", + "cssValueType", + "ctrlKey", + "ctrlLeft", + "cues", + "cullFace", + "currency", + "currencyDisplay", + "current", + "currentDirection", + "currentLocalDescription", + "currentNode", + "currentPage", + "currentRect", + "currentRemoteDescription", + "currentScale", + "currentScript", + "currentSrc", + "currentState", + "currentStyle", + "currentTarget", + "currentTime", + "currentTranslate", + "currentView", + "cursor", + "curve", + "customElements", + "customError", + "customSections", + "cx", + "cy", + "d", + "data", + "dataFld", + "dataFormatAs", + "dataLoss", + "dataLossMessage", + "dataPageSize", + "dataSrc", + "dataTransfer", + "database", + "databases", + "dataset", + "dateTime", + "day", + "db", + "debug", + "debuggerEnabled", + "declare", + "decode", + "decodeAudioData", + "decodeURI", + "decodeURIComponent", + "decodedBodySize", + "decoding", + "decodingInfo", + "decrypt", + "default", + "defaultCharset", + "defaultChecked", + "defaultMuted", + "defaultPlaybackRate", + "defaultPolicy", + "defaultPrevented", + "defaultRequest", + "defaultSelected", + "defaultStatus", + "defaultURL", + "defaultValue", + "defaultView", + "defaultstatus", + "defer", + "define", + "defineMagicFunction", + "defineMagicVariable", + "defineProperties", + "defineProperty", + "deg", + "delay", + "delayTime", + "delegatesFocus", + "delete", + "deleteBuffer", + "deleteCaption", + "deleteCell", + "deleteContents", + "deleteData", + "deleteDatabase", + "deleteFramebuffer", + "deleteFromDocument", + "deleteIndex", + "deleteMedium", + "deleteObjectStore", + "deleteProgram", + "deleteProperty", + "deleteQuery", + "deleteRenderbuffer", + "deleteRow", + "deleteRule", + "deleteSampler", + "deleteShader", + "deleteSync", + "deleteTFoot", + "deleteTHead", + "deleteTexture", + "deleteTransformFeedback", + "deleteVertexArray", + "deliverChangeRecords", + "delivery", + "deliveryInfo", + "deliveryStatus", + "deliveryTimestamp", + "delta", + "deltaMode", + "deltaX", + "deltaY", + "deltaZ", + "dependentLocality", + "depthFar", + "depthFunc", + "depthMask", + "depthNear", + "depthRange", + "deref", + "deriveBits", + "deriveKey", + "description", + "deselectAll", + "designMode", + "desiredSize", + "destination", + "destinationURL", + "detach", + "detachEvent", + "detachShader", + "detail", + "details", + "detect", + "detune", + "device", + "deviceClass", + "deviceId", + "deviceMemory", + "devicePixelContentBoxSize", + "devicePixelRatio", + "deviceProtocol", + "deviceSessionId", + "deviceSubclass", + "deviceVersionMajor", + "deviceVersionMinor", + "deviceVersionSubminor", + "deviceXDPI", + "deviceYDPI", + "didTimeout", + "diffuseConstant", + "digest", + "dimensions", + "dir", + "dirName", + "dirXml", + "direction", + "dirxml", + "disable", + "disablePictureInPicture", + "disableRemotePlayback", + "disableVertexAttribArray", + "disabled", + "dischargingTime", + "disconnect", + "disconnectedCallback", + "dispatch", + "dispatchEvent", + "dispatchToListener", + "display", + "displayId", + "displayName", + "disposition", + "distanceModel", + "div", + "divisor", + "djsapi", + "djsproxy", + "doImport", + "doNotTrack", + "doScroll", + "doctype", + "document", + "documentElement", + "documentMode", + "documentURI", + "dolphin", + "dolphinGameCenter", + "dolphininfo", + "dolphinmeta", + "domComplete", + "domContentLoadedEventEnd", + "domContentLoadedEventStart", + "domInteractive", + "domLoading", + "domOverlayState", + "domain", + "domainLookupEnd", + "domainLookupStart", + "dominant-baseline", + "dominantBaseline", + "done", + "dopplerFactor", + "dotAll", + "downDegrees", + "downlink", + "download", + "downloadTotal", + "downloaded", + "dpcm", + "dpi", + "dppx", + "dragDrop", + "draggable", + "drawArrays", + "drawArraysInstanced", + "drawArraysInstancedANGLE", + "drawBuffers", + "drawCustomFocusRing", + "drawElements", + "drawElementsInstanced", + "drawElementsInstancedANGLE", + "drawFocusIfNeeded", + "drawImage", + "drawImageFromRect", + "drawRangeElements", + "drawSystemFocusRing", + "drawingBufferHeight", + "drawingBufferWidth", + "dropEffect", + "droppedVideoFrames", + "dropzone", + "dtmf", + "dump", + "duplicate", + "durability", + "duration", + "dvname", + "dvnum", + "dx", + "dy", + "dynsrc", + "e", + "edgeMode", + "effect", + "effectAllowed", + "effectiveDirective", + "effectiveType", + "elapsedTime", + "element", + "elementFromPoint", + "elementTiming", + "elements", + "elementsFromPoint", + "elevation", + "ellipse", + "em", + "emHeightAscent", + "emHeightDescent", + "email", + "embeds", + "emma", + "empty", + "empty-cells", + "emptyCells", + "emptyHTML", + "emptyScript", + "emulatedPosition", + "enable", + "enableBackground", + "enableDelegations", + "enableStyleSheetsForSet", + "enableVertexAttribArray", + "enabled", + "enabledPlugin", + "encode", + "encodeInto", + "encodeURI", + "encodeURIComponent", + "encodedBodySize", + "encoding", + "encodingInfo", + "encrypt", + "enctype", + "end", + "endContainer", + "endElement", + "endElementAt", + "endOfStream", + "endOffset", + "endQuery", + "endTime", + "endTransformFeedback", + "ended", + "endpoint", + "endpointNumber", + "endpoints", + "endsWith", + "enterKeyHint", + "entities", + "entries", + "entryType", + "enumerate", + "enumerateDevices", + "enumerateEditable", + "environmentBlendMode", + "epubCaptionSide", + "epubTextCombine", + "epubTextEmphasis", + "epubTextEmphasisColor", + "epubTextEmphasisStyle", + "epubTextOrientation", + "epubTextTransform", + "epubWordBreak", + "epubWritingMode", + "equals", + "era", + "error", + "errorCode", + "errorDetail", + "errorText", + "escape", + "estimate", + "eval", + "evaluate", + "event", + "eventPhase", + "every", + "ex", + "exception", + "exchange", + "exec", + "execCommand", + "execCommandShowHelp", + "execScript", + "executeSql", + "exitFullscreen", + "exitPictureInPicture", + "exitPointerLock", + "exitPresent", + "exp", + "expand", + "expandEntityReferences", + "expando", + "expansion", + "expiration", + "expirationTime", + "expires", + "expiryDate", + "explicitOriginalTarget", + "expm1", + "exponent", + "exponentialRampToValueAtTime", + "exportKey", + "exports", + "extend", + "extensions", + "extentNode", + "extentOffset", + "external", + "externalResourcesRequired", + "extractContents", + "extractable", + "eye", + "f", + "face", + "factoryReset", + "failureReason", + "fallback", + "family", + "familyName", + "farthestViewportElement", + "fastSeek", + "fatal", + "featurePolicy", + "featureSettings", + "features", + "fenceSync", + "fetch", + "fetchStart", + "fftSize", + "fgColor", + "fieldOfView", + "file", + "fileCreatedDate", + "fileHandle", + "fileModifiedDate", + "fileName", + "fileSize", + "fileUpdatedDate", + "filename", + "files", + "filesystem", + "fill", + "fill-opacity", + "fill-rule", + "fillLightMode", + "fillOpacity", + "fillRect", + "fillRule", + "fillStyle", + "fillText", + "filter", + "filterResX", + "filterResY", + "filterUnits", + "filters", + "finally", + "find", + "findIndex", + "findRule", + "findText", + "finish", + "finishDocumentLoadTime", + "finishLoadTime", + "finished", + "fireEvent", + "firesTouchEvents", + "first", + "firstChild", + "firstElementChild", + "firstPage", + "firstPaintAfterLoadTime", + "firstPaintTime", + "fixed", + "flags", + "flat", + "flatMap", + "flex", + "flex-basis", + "flex-direction", + "flex-flow", + "flex-grow", + "flex-shrink", + "flex-wrap", + "flexBasis", + "flexDirection", + "flexFlow", + "flexGrow", + "flexShrink", + "flexWrap", + "flipX", + "flipY", + "float", + "flood-color", + "flood-opacity", + "floodColor", + "floodOpacity", + "floor", + "flush", + "focus", + "focusNode", + "focusOffset", + "font", + "font-family", + "font-feature-settings", + "font-kerning", + "font-language-override", + "font-size", + "font-size-adjust", + "font-stretch", + "font-style", + "font-synthesis", + "font-variant", + "font-variant-alternates", + "font-variant-caps", + "font-variant-east-asian", + "font-variant-ligatures", + "font-variant-numeric", + "font-variant-position", + "font-weight", + "fontBoundingBoxAscent", + "fontBoundingBoxDescent", + "fontDisplay", + "fontFamily", + "fontFeatureSettings", + "fontKerning", + "fontLanguageOverride", + "fontOpticalSizing", + "fontSize", + "fontSizeAdjust", + "fontSmoothingEnabled", + "fontStretch", + "fontStyle", + "fontSynthesis", + "fontVariant", + "fontVariantAlternates", + "fontVariantCaps", + "fontVariantEastAsian", + "fontVariantLigatures", + "fontVariantNumeric", + "fontVariantPosition", + "fontVariationSettings", + "fontWeight", + "fontcolor", + "fontfaces", + "fonts", + "fontsize", + "for", + "forEach", + "force", + "forceRedraw", + "form", + "formAction", + "formData", + "formEnctype", + "formMethod", + "formNoValidate", + "formTarget", + "format", + "formatRange", + "formatRangeToParts", + "formatToParts", + "forms", + "forward", + "forwardX", + "forwardY", + "forwardZ", + "foundation", + "fr", + "fragmentDirective", + "frame", + "frameBorder", + "frameElement", + "frameSpacing", + "framebuffer", + "framebufferHeight", + "framebufferRenderbuffer", + "framebufferTexture2D", + "framebufferTextureLayer", + "framebufferWidth", + "frames", + "freeSpace", + "freeze", + "frequency", + "frequencyBinCount", + "from", + "fromCharCode", + "fromCodePoint", + "fromElement", + "fromEntries", + "fromFloat32Array", + "fromFloat64Array", + "fromMatrix", + "fromPoint", + "fromQuad", + "fromRect", + "frontFace", + "fround", + "fullPath", + "fullScreen", + "fullscreen", + "fullscreenElement", + "fullscreenEnabled", + "fx", + "fy", + "gain", + "gamepad", + "gamma", + "gap", + "gatheringState", + "gatt", + "genderIdentity", + "generateCertificate", + "generateKey", + "generateMipmap", + "generateRequest", + "geolocation", + "gestureObject", + "get", + "getActiveAttrib", + "getActiveUniform", + "getActiveUniformBlockName", + "getActiveUniformBlockParameter", + "getActiveUniforms", + "getAdditionalLanguages", + "getAdjacentText", + "getAll", + "getAllKeys", + "getAllResponseHeaders", + "getAllowlistForFeature", + "getAnimations", + "getAsFile", + "getAsString", + "getAttachedShaders", + "getAttribLocation", + "getAttribute", + "getAttributeNS", + "getAttributeNames", + "getAttributeNode", + "getAttributeNodeNS", + "getAttributeType", + "getAudioTracks", + "getAvailability", + "getBBox", + "getBattery", + "getBigInt64", + "getBigUint64", + "getBlob", + "getBookmark", + "getBoundingClientRect", + "getBounds", + "getBufferParameter", + "getBufferSubData", + "getByteFrequencyData", + "getByteTimeDomainData", + "getCSSCanvasContext", + "getCTM", + "getCandidateWindowClientRect", + "getCanonicalLocales", + "getCapabilities", + "getChannelData", + "getCharNumAtPosition", + "getCharacteristic", + "getCharacteristics", + "getClientExtensionResults", + "getClientRect", + "getClientRects", + "getCoalescedEvents", + "getCompositionAlternatives", + "getComputedStyle", + "getComputedTextLength", + "getComputedTiming", + "getConfiguration", + "getConstraints", + "getContext", + "getContextAttributes", + "getContributingSources", + "getCount", + "getCounterValue", + "getCueAsHTML", + "getCueById", + "getCurrentPosition", + "getCurrentTime", + "getData", + "getDatabaseNames", + "getDate", + "getDay", + "getDefaultComputedStyle", + "getDescriptor", + "getDescriptors", + "getDestinationInsertionPoints", + "getDetails", + "getDevices", + "getDirectory", + "getDisplayMedia", + "getDistributedNodes", + "getEditable", + "getElementById", + "getElementsByClassName", + "getElementsByName", + "getElementsByTagName", + "getElementsByTagNameNS", + "getEnclosureList", + "getEndPositionOfChar", + "getEntries", + "getEntriesByName", + "getEntriesByType", + "getError", + "getExtension", + "getExtentOfChar", + "getEyeParameters", + "getFeature", + "getFile", + "getFiles", + "getFilesAndDirectories", + "getFingerprints", + "getFloat32", + "getFloat64", + "getFloatFrequencyData", + "getFloatTimeDomainData", + "getFloatValue", + "getFragDataLocation", + "getFrameData", + "getFramebufferAttachmentParameter", + "getFrequencyResponse", + "getFullYear", + "getGamepads", + "getHitTestResults", + "getHitTestResultsForTransientInput", + "getHours", + "getIdentityAssertion", + "getIds", + "getImageData", + "getIndexedParameter", + "getInstalled", + "getInstalledRelatedApps", + "getInt16", + "getInt32", + "getInt8", + "getInternalformatParameter", + "getIntersectionList", + "getIsInstalled", + "getItem", + "getItems", + "getKey", + "getKeyframes", + "getLayers", + "getLayoutMap", + "getLineDash", + "getLocalCandidates", + "getLocalParameters", + "getLocalStreams", + "getLocalizationResource", + "getMarks", + "getMatchedCSSRules", + "getMeasures", + "getMetadata", + "getMilliseconds", + "getMinutes", + "getModifierState", + "getMonth", + "getNamedItem", + "getNamedItemNS", + "getNativeFramebufferScaleFactor", + "getNotifications", + "getNotifier", + "getNumberOfChars", + "getOffsetReferenceSpace", + "getOutputTimestamp", + "getOverrideHistoryNavigationMode", + "getOverrideStyle", + "getOwnPropertyDescriptor", + "getOwnPropertyDescriptors", + "getOwnPropertyNames", + "getOwnPropertySymbols", + "getParameter", + "getParameters", + "getParent", + "getPathSegAtLength", + "getPhotoCapabilities", + "getPhotoSettings", + "getPointAtLength", + "getPose", + "getPredictedEvents", + "getPreference", + "getPreferenceDefault", + "getPresentationAttribute", + "getPreventDefault", + "getPrimaryService", + "getPrimaryServices", + "getProgramInfoLog", + "getProgramParameter", + "getPropertyCSSValue", + "getPropertyPriority", + "getPropertyShorthand", + "getPropertyType", + "getPropertyValue", + "getPrototypeOf", + "getQuery", + "getQueryParameter", + "getRGBColorValue", + "getRandomValues", + "getRangeAt", + "getReader", + "getReceivers", + "getRectValue", + "getRegistration", + "getRegistrations", + "getRemoteCandidates", + "getRemoteCertificates", + "getRemoteParameters", + "getRemoteStreams", + "getRenderbufferParameter", + "getResponseHeader", + "getRevision", + "getRoot", + "getRootNode", + "getRotationOfChar", + "getRules", + "getSVGDocument", + "getSamplerParameter", + "getScreenCTM", + "getSeconds", + "getSelectedCandidatePair", + "getSelection", + "getSelf", + "getSenders", + "getService", + "getSettings", + "getShaderInfoLog", + "getShaderParameter", + "getShaderPrecisionFormat", + "getShaderSource", + "getSimpleDuration", + "getSiteIcons", + "getSources", + "getSpeculativeParserUrls", + "getStartDate", + "getStartPositionOfChar", + "getStartTime", + "getState", + "getStats", + "getStatusForPolicy", + "getStorageUpdates", + "getStreamById", + "getStringValue", + "getSubStringLength", + "getSubscription", + "getSupportedConstraints", + "getSupportedExtensions", + "getSupportedFormats", + "getSyncParameter", + "getSynchronizationSources", + "getTags", + "getTargetRanges", + "getTexParameter", + "getTime", + "getTimezoneOffset", + "getTiming", + "getTotalLength", + "getTrackById", + "getTracks", + "getTransceivers", + "getTransform", + "getTransformFeedbackVarying", + "getTransformToElement", + "getTransports", + "getType", + "getTypeMapping", + "getUTCDate", + "getUTCDay", + "getUTCFullYear", + "getUTCHours", + "getUTCMilliseconds", + "getUTCMinutes", + "getUTCMonth", + "getUTCSeconds", + "getUint16", + "getUint32", + "getUint8", + "getUniform", + "getUniformBlockIndex", + "getUniformIndices", + "getUniformLocation", + "getUserMedia", + "getVRDisplays", + "getValues", + "getVarDate", + "getVariableValue", + "getVertexAttrib", + "getVertexAttribOffset", + "getVideoPlaybackQuality", + "getVideoTracks", + "getViewerPose", + "getViewport", + "getVoices", + "getWakeLockState", + "getWriter", + "getYear", + "givenName", + "global", + "globalAlpha", + "globalCompositeOperation", + "globalThis", + "glyphOrientationHorizontal", + "glyphOrientationVertical", + "glyphRef", + "go", + "grabFrame", + "grad", + "gradientTransform", + "gradientUnits", + "grammars", + "green", + "grid", + "grid-area", + "grid-auto-columns", + "grid-auto-flow", + "grid-auto-rows", + "grid-column", + "grid-column-end", + "grid-column-gap", + "grid-column-start", + "grid-gap", + "grid-row", + "grid-row-end", + "grid-row-gap", + "grid-row-start", + "grid-template", + "grid-template-areas", + "grid-template-columns", + "grid-template-rows", + "gridArea", + "gridAutoColumns", + "gridAutoFlow", + "gridAutoRows", + "gridColumn", + "gridColumnEnd", + "gridColumnGap", + "gridColumnStart", + "gridGap", + "gridRow", + "gridRowEnd", + "gridRowGap", + "gridRowStart", + "gridTemplate", + "gridTemplateAreas", + "gridTemplateColumns", + "gridTemplateRows", + "gripSpace", + "group", + "groupCollapsed", + "groupEnd", + "groupId", + "grow", + "hadRecentInput", + "hand", + "handedness", + "hangingBaseline", + "hangingPunctuation", + "hapticActuators", + "hardwareConcurrency", + "has", + "hasAttribute", + "hasAttributeNS", + "hasAttributes", + "hasBeenActive", + "hasChildNodes", + "hasComposition", + "hasEnrolledInstrument", + "hasExtension", + "hasExternalDisplay", + "hasFeature", + "hasFocus", + "hasInstance", + "hasLayout", + "hasListener", + "hasListeners", + "hasOrientation", + "hasOwnProperty", + "hasPointerCapture", + "hasPosition", + "hasReading", + "hasStorageAccess", + "hash", + "head", + "headers", + "heading", + "height", + "hidden", + "hide", + "hideFocus", + "high", + "highWaterMark", + "hint", + "history", + "honorificPrefix", + "honorificSuffix", + "horizontalOverflow", + "host", + "hostCandidate", + "hostname", + "hour", + "hour12", + "hourCycle", + "href", + "hrefTranslate", + "hreflang", + "hspace", + "html5TagCheckInerface", + "htmlFor", + "htmlText", + "httpEquiv", + "httpRequestStatusCode", + "hwTimestamp", + "hyphens", + "hypot", + "iccId", + "iceConnectionState", + "iceGatheringState", + "iceTransport", + "icon", + "iconURL", + "id", + "identifier", + "identity", + "ideographicBaseline", + "idpLoginUrl", + "ignoreBOM", + "ignoreCase", + "ignoreDepthValues", + "ignoreMutedMedia", + "ignorePunctuation", + "image-orientation", + "image-rendering", + "imageHeight", + "imageOrientation", + "imageRendering", + "imageSizes", + "imageSmoothingEnabled", + "imageSmoothingQuality", + "imageSrcset", + "imageWidth", + "images", + "ime-mode", + "imeMode", + "implementation", + "import", + "importKey", + "importNode", + "importStylesheet", + "imports", + "impp", + "imul", + "in", + "in1", + "in2", + "inBandMetadataTrackDispatchType", + "inRange", + "includes", + "incremental", + "indeterminate", + "index", + "indexNames", + "indexOf", + "indexedDB", + "indicate", + "inertiaDestinationX", + "inertiaDestinationY", + "info", + "init", + "initAnimationEvent", + "initBeforeLoadEvent", + "initClipboardEvent", + "initCloseEvent", + "initCommandEvent", + "initCompositionEvent", + "initCustomEvent", + "initData", + "initDataType", + "initDeviceMotionEvent", + "initDeviceOrientationEvent", + "initDragEvent", + "initErrorEvent", + "initEvent", + "initFocusEvent", + "initGestureEvent", + "initHashChangeEvent", + "initKeyEvent", + "initKeyboardEvent", + "initMSManipulationEvent", + "initMessageEvent", + "initMouseEvent", + "initMouseScrollEvent", + "initMouseWheelEvent", + "initMutationEvent", + "initNSMouseEvent", + "initOverflowEvent", + "initPageEvent", + "initPageTransitionEvent", + "initPointerEvent", + "initPopStateEvent", + "initProgressEvent", + "initScrollAreaEvent", + "initSimpleGestureEvent", + "initStorageEvent", + "initTextEvent", + "initTimeEvent", + "initTouchEvent", + "initTransitionEvent", + "initUIEvent", + "initWebKitAnimationEvent", + "initWebKitTransitionEvent", + "initWebKitWheelEvent", + "initWheelEvent", + "initialTime", + "initialize", + "initiatorType", + "inline-size", + "inlineSize", + "inlineVerticalFieldOfView", + "inner", + "innerHTML", + "innerHeight", + "innerText", + "innerWidth", + "input", + "inputBuffer", + "inputEncoding", + "inputMethod", + "inputMode", + "inputSource", + "inputSources", + "inputType", + "inputs", + "insertAdjacentElement", + "insertAdjacentHTML", + "insertAdjacentText", + "insertBefore", + "insertCell", + "insertDTMF", + "insertData", + "insertId", + "insertItemBefore", + "insertNode", + "insertRow", + "insertRule", + "inset", + "inset-block", + "inset-block-end", + "inset-block-start", + "inset-inline", + "inset-inline-end", + "inset-inline-start", + "insetBlock", + "insetBlockEnd", + "insetBlockStart", + "insetInline", + "insetInlineEnd", + "insetInlineStart", + "install", + "installChrome", + "installPackage", + "installState", + "installing", + "instanceRoot", + "instantiate", + "instantiateStreaming", + "instruments", + "integrity", + "interactionMode", + "intercept", + "interfaceClass", + "interfaceName", + "interfaceNumber", + "interfaceProtocol", + "interfaceSubclass", + "interfaces", + "interimResults", + "internalSubset", + "interpretation", + "intersectionRatio", + "intersectionRect", + "intersectsNode", + "interval", + "invalidIteratorState", + "invalidateFramebuffer", + "invalidateSubFramebuffer", + "inverse", + "invertSelf", + "is", + "is2D", + "isActive", + "isAlternate", + "isArray", + "isBingCurrentSearchDefault", + "isBuffer", + "isCandidateWindowVisible", + "isChar", + "isCollapsed", + "isComposing", + "isConcatSpreadable", + "isConnected", + "isContentEditable", + "isContentHandlerRegistered", + "isContextLost", + "isDefaultNamespace", + "isDirectory", + "isDisabled", + "isEnabled", + "isEqual", + "isEqualNode", + "isExtensible", + "isExternalCTAP2SecurityKeySupported", + "isFile", + "isFinite", + "isFramebuffer", + "isFrozen", + "isGenerator", + "isHTML", + "isHistoryNavigation", + "isId", + "isIdentity", + "isInjected", + "isInstalled", + "isInteger", + "isIntersecting", + "isLockFree", + "isMap", + "isMultiLine", + "isNaN", + "isOpen", + "isPointInFill", + "isPointInPath", + "isPointInRange", + "isPointInStroke", + "isPrefAlternate", + "isPresenting", + "isPrimary", + "isProgram", + "isPropertyImplicit", + "isProtocolHandlerRegistered", + "isPrototypeOf", + "isQuery", + "isRenderbuffer", + "isSafeInteger", + "isSameNode", + "isSampler", + "isScript", + "isScriptURL", + "isSealed", + "isSecureContext", + "isSessionSupported", + "isShader", + "isSupported", + "isSync", + "isTextEdit", + "isTexture", + "isTransformFeedback", + "isTrusted", + "isTypeSupported", + "isTypeSupportedWithFeatures", + "isUserVerifyingPlatformAuthenticatorAvailable", + "isVertexArray", + "isView", + "isVisible", + "isochronousTransferIn", + "isochronousTransferOut", + "isolation", + "italics", + "item", + "itemId", + "itemProp", + "itemRef", + "itemScope", + "itemType", + "itemValue", + "items", + "iterateNext", + "iterator", + "javaEnabled", + "jobTitle", + "join", + "jsHeapSizeLimit", + "json", + "justify-content", + "justify-items", + "justify-self", + "justifyContent", + "justifyItems", + "justifySelf", + "k1", + "k2", + "k3", + "k4", + "kHz", + "keepalive", + "kernelMatrix", + "kernelUnitLengthX", + "kernelUnitLengthY", + "kerning", + "key", + "keyCode", + "keyFor", + "keyIdentifier", + "keyLightEnabled", + "keyLocation", + "keyPath", + "keyStatuses", + "keySystem", + "keyText", + "keyUsage", + "keyboard", + "keys", + "keytype", + "kind", + "knee", + "label", + "labels", + "lang", + "language", + "languages", + "largeArcFlag", + "lastActivePanel", + "lastChild", + "lastElementChild", + "lastEventId", + "lastIndex", + "lastIndexOf", + "lastInputTime", + "lastMatch", + "lastMessageSubject", + "lastMessageType", + "lastModified", + "lastModifiedDate", + "lastPage", + "lastParen", + "lastState", + "lastStyleSheetSet", + "latitude", + "layerX", + "layerY", + "layoutFlow", + "layoutGrid", + "layoutGridChar", + "layoutGridLine", + "layoutGridMode", + "layoutGridType", + "lbound", + "left", + "leftContext", + "leftDegrees", + "leftMargin", + "leftProjectionMatrix", + "leftViewMatrix", + "length", + "lengthAdjust", + "lengthComputable", + "letter-spacing", + "letterSpacing", + "level", + "lighting-color", + "lightingColor", + "limitingConeAngle", + "line", + "line-break", + "line-height", + "lineAlign", + "lineBreak", + "lineCap", + "lineDashOffset", + "lineHeight", + "lineJoin", + "lineNumber", + "lineTo", + "lineWidth", + "linearAcceleration", + "linearRampToValueAtTime", + "linearVelocity", + "lineno", + "lines", + "link", + "linkColor", + "linkProgram", + "links", + "list", + "list-style", + "list-style-image", + "list-style-position", + "list-style-type", + "listStyle", + "listStyleImage", + "listStylePosition", + "listStyleType", + "listener", + "load", + "loadEventEnd", + "loadEventStart", + "loadTime", + "loadTimes", + "loaded", + "loading", + "localDescription", + "localName", + "localService", + "localStorage", + "locale", + "localeCompare", + "location", + "locationbar", + "lock", + "locked", + "lockedFile", + "locks", + "log", + "log10", + "log1p", + "log2", + "logicalXDPI", + "logicalYDPI", + "longDesc", + "longitude", + "lookupNamespaceURI", + "lookupPrefix", + "loop", + "loopEnd", + "loopStart", + "looping", + "low", + "lower", + "lowerBound", + "lowerOpen", + "lowsrc", + "m11", + "m12", + "m13", + "m14", + "m21", + "m22", + "m23", + "m24", + "m31", + "m32", + "m33", + "m34", + "m41", + "m42", + "m43", + "m44", + "makeXRCompatible", + "manifest", + "manufacturer", + "manufacturerName", + "map", + "mapping", + "margin", + "margin-block", + "margin-block-end", + "margin-block-start", + "margin-bottom", + "margin-inline", + "margin-inline-end", + "margin-inline-start", + "margin-left", + "margin-right", + "margin-top", + "marginBlock", + "marginBlockEnd", + "marginBlockStart", + "marginBottom", + "marginHeight", + "marginInline", + "marginInlineEnd", + "marginInlineStart", + "marginLeft", + "marginRight", + "marginTop", + "marginWidth", + "mark", + "markTimeline", + "marker", + "marker-end", + "marker-mid", + "marker-offset", + "marker-start", + "markerEnd", + "markerHeight", + "markerMid", + "markerOffset", + "markerStart", + "markerUnits", + "markerWidth", + "marks", + "mask", + "mask-clip", + "mask-composite", + "mask-image", + "mask-mode", + "mask-origin", + "mask-position", + "mask-position-x", + "mask-position-y", + "mask-repeat", + "mask-size", + "mask-type", + "maskClip", + "maskComposite", + "maskContentUnits", + "maskImage", + "maskMode", + "maskOrigin", + "maskPosition", + "maskPositionX", + "maskPositionY", + "maskRepeat", + "maskSize", + "maskType", + "maskUnits", + "match", + "matchAll", + "matchMedia", + "matchMedium", + "matches", + "matrix", + "matrixTransform", + "max", + "max-block-size", + "max-height", + "max-inline-size", + "max-width", + "maxActions", + "maxAlternatives", + "maxBlockSize", + "maxChannelCount", + "maxChannels", + "maxConnectionsPerServer", + "maxDecibels", + "maxDistance", + "maxHeight", + "maxInlineSize", + "maxLayers", + "maxLength", + "maxMessageSize", + "maxPacketLifeTime", + "maxRetransmits", + "maxTouchPoints", + "maxValue", + "maxWidth", + "maxZoom", + "maximize", + "maximumFractionDigits", + "measure", + "measureText", + "media", + "mediaCapabilities", + "mediaDevices", + "mediaElement", + "mediaGroup", + "mediaKeys", + "mediaSession", + "mediaStream", + "mediaText", + "meetOrSlice", + "memory", + "menubar", + "mergeAttributes", + "message", + "messageClass", + "messageHandlers", + "messageType", + "metaKey", + "metadata", + "method", + "methodDetails", + "methodName", + "mid", + "mimeType", + "mimeTypes", + "min", + "min-block-size", + "min-height", + "min-inline-size", + "min-width", + "minBlockSize", + "minDecibels", + "minHeight", + "minInlineSize", + "minLength", + "minValue", + "minWidth", + "minZoom", + "minimize", + "minimumFractionDigits", + "minimumIntegerDigits", + "minute", + "miterLimit", + "mix-blend-mode", + "mixBlendMode", + "mm", + "mode", + "modify", + "month", + "motion", + "motionOffset", + "motionPath", + "motionRotation", + "mount", + "move", + "moveBy", + "moveEnd", + "moveFirst", + "moveFocusDown", + "moveFocusLeft", + "moveFocusRight", + "moveFocusUp", + "moveNext", + "moveRow", + "moveStart", + "moveTo", + "moveToBookmark", + "moveToElementText", + "moveToPoint", + "movementX", + "movementY", + "mozAdd", + "mozAnimationStartTime", + "mozAnon", + "mozApps", + "mozAudioCaptured", + "mozAudioChannelType", + "mozAutoplayEnabled", + "mozCancelAnimationFrame", + "mozCancelFullScreen", + "mozCancelRequestAnimationFrame", + "mozCaptureStream", + "mozCaptureStreamUntilEnded", + "mozClearDataAt", + "mozContact", + "mozContacts", + "mozCreateFileHandle", + "mozCurrentTransform", + "mozCurrentTransformInverse", + "mozCursor", + "mozDash", + "mozDashOffset", + "mozDecodedFrames", + "mozExitPointerLock", + "mozFillRule", + "mozFragmentEnd", + "mozFrameDelay", + "mozFullScreen", + "mozFullScreenElement", + "mozFullScreenEnabled", + "mozGetAll", + "mozGetAllKeys", + "mozGetAsFile", + "mozGetDataAt", + "mozGetMetadata", + "mozGetUserMedia", + "mozHasAudio", + "mozHasItem", + "mozHidden", + "mozImageSmoothingEnabled", + "mozIndexedDB", + "mozInnerScreenX", + "mozInnerScreenY", + "mozInputSource", + "mozIsTextField", + "mozItem", + "mozItemCount", + "mozItems", + "mozLength", + "mozLockOrientation", + "mozMatchesSelector", + "mozMovementX", + "mozMovementY", + "mozOpaque", + "mozOrientation", + "mozPaintCount", + "mozPaintedFrames", + "mozParsedFrames", + "mozPay", + "mozPointerLockElement", + "mozPresentedFrames", + "mozPreservesPitch", + "mozPressure", + "mozPrintCallback", + "mozRTCIceCandidate", + "mozRTCPeerConnection", + "mozRTCSessionDescription", + "mozRemove", + "mozRequestAnimationFrame", + "mozRequestFullScreen", + "mozRequestPointerLock", + "mozSetDataAt", + "mozSetImageElement", + "mozSourceNode", + "mozSrcObject", + "mozSystem", + "mozTCPSocket", + "mozTextStyle", + "mozTypesAt", + "mozUnlockOrientation", + "mozUserCancelled", + "mozVisibilityState", + "ms", + "msAnimation", + "msAnimationDelay", + "msAnimationDirection", + "msAnimationDuration", + "msAnimationFillMode", + "msAnimationIterationCount", + "msAnimationName", + "msAnimationPlayState", + "msAnimationStartTime", + "msAnimationTimingFunction", + "msBackfaceVisibility", + "msBlockProgression", + "msCSSOMElementFloatMetrics", + "msCaching", + "msCachingEnabled", + "msCancelRequestAnimationFrame", + "msCapsLockWarningOff", + "msClearImmediate", + "msClose", + "msContentZoomChaining", + "msContentZoomFactor", + "msContentZoomLimit", + "msContentZoomLimitMax", + "msContentZoomLimitMin", + "msContentZoomSnap", + "msContentZoomSnapPoints", + "msContentZoomSnapType", + "msContentZooming", + "msConvertURL", + "msCrypto", + "msDoNotTrack", + "msElementsFromPoint", + "msElementsFromRect", + "msExitFullscreen", + "msExtendedCode", + "msFillRule", + "msFirstPaint", + "msFlex", + "msFlexAlign", + "msFlexDirection", + "msFlexFlow", + "msFlexItemAlign", + "msFlexLinePack", + "msFlexNegative", + "msFlexOrder", + "msFlexPack", + "msFlexPositive", + "msFlexPreferredSize", + "msFlexWrap", + "msFlowFrom", + "msFlowInto", + "msFontFeatureSettings", + "msFullscreenElement", + "msFullscreenEnabled", + "msGetInputContext", + "msGetRegionContent", + "msGetUntransformedBounds", + "msGraphicsTrustStatus", + "msGridColumn", + "msGridColumnAlign", + "msGridColumnSpan", + "msGridColumns", + "msGridRow", + "msGridRowAlign", + "msGridRowSpan", + "msGridRows", + "msHidden", + "msHighContrastAdjust", + "msHyphenateLimitChars", + "msHyphenateLimitLines", + "msHyphenateLimitZone", + "msHyphens", + "msImageSmoothingEnabled", + "msImeAlign", + "msIndexedDB", + "msInterpolationMode", + "msIsStaticHTML", + "msKeySystem", + "msKeys", + "msLaunchUri", + "msLockOrientation", + "msManipulationViewsEnabled", + "msMatchMedia", + "msMatchesSelector", + "msMaxTouchPoints", + "msOrientation", + "msOverflowStyle", + "msPerspective", + "msPerspectiveOrigin", + "msPlayToDisabled", + "msPlayToPreferredSourceUri", + "msPlayToPrimary", + "msPointerEnabled", + "msRegionOverflow", + "msReleasePointerCapture", + "msRequestAnimationFrame", + "msRequestFullscreen", + "msSaveBlob", + "msSaveOrOpenBlob", + "msScrollChaining", + "msScrollLimit", + "msScrollLimitXMax", + "msScrollLimitXMin", + "msScrollLimitYMax", + "msScrollLimitYMin", + "msScrollRails", + "msScrollSnapPointsX", + "msScrollSnapPointsY", + "msScrollSnapType", + "msScrollSnapX", + "msScrollSnapY", + "msScrollTranslation", + "msSetImmediate", + "msSetMediaKeys", + "msSetPointerCapture", + "msTextCombineHorizontal", + "msTextSizeAdjust", + "msToBlob", + "msTouchAction", + "msTouchSelect", + "msTraceAsyncCallbackCompleted", + "msTraceAsyncCallbackStarting", + "msTraceAsyncOperationCompleted", + "msTraceAsyncOperationStarting", + "msTransform", + "msTransformOrigin", + "msTransformStyle", + "msTransition", + "msTransitionDelay", + "msTransitionDuration", + "msTransitionProperty", + "msTransitionTimingFunction", + "msUnlockOrientation", + "msUpdateAsyncCallbackRelation", + "msUserSelect", + "msVisibilityState", + "msWrapFlow", + "msWrapMargin", + "msWrapThrough", + "msWriteProfilerMark", + "msZoom", + "msZoomTo", + "mt", + "mul", + "multiEntry", + "multiSelectionObj", + "multiline", + "multiple", + "multiply", + "multiplySelf", + "mutableFile", + "muted", + "n", + "name", + "nameProp", + "namedItem", + "namedRecordset", + "names", + "namespaceURI", + "namespaces", + "naturalHeight", + "naturalWidth", + "navigate", + "navigation", + "navigationMode", + "navigationPreload", + "navigationStart", + "navigationType", + "navigator", + "near", + "nearestViewportElement", + "negative", + "negotiated", + "netscape", + "networkState", + "newScale", + "newTranslate", + "newURL", + "newValue", + "newValueSpecifiedUnits", + "newVersion", + "newhome", + "next", + "nextElementSibling", + "nextHopProtocol", + "nextNode", + "nextPage", + "nextSibling", + "nickname", + "noHref", + "noModule", + "noResize", + "noShade", + "noValidate", + "noWrap", + "node", + "nodeName", + "nodeType", + "nodeValue", + "nonce", + "normalize", + "normalizedPathSegList", + "notationName", + "notations", + "note", + "noteGrainOn", + "noteOff", + "noteOn", + "notify", + "now", + "npnNegotiatedProtocol", + "numOctaves", + "number", + "numberOfChannels", + "numberOfInputs", + "numberOfItems", + "numberOfOutputs", + "numberValue", + "numberingSystem", + "numeric", + "oMatchesSelector", + "object", + "object-fit", + "object-position", + "objectFit", + "objectPosition", + "objectStore", + "objectStoreNames", + "observe", + "observedAttributes", + "of", + "offscreenBuffering", + "offset", + "offset-anchor", + "offset-block-end", + "offset-block-start", + "offset-distance", + "offset-inline-end", + "offset-inline-start", + "offset-path", + "offset-rotate", + "offsetAnchor", + "offsetBlockEnd", + "offsetBlockStart", + "offsetDistance", + "offsetHeight", + "offsetInlineEnd", + "offsetInlineStart", + "offsetLeft", + "offsetNode", + "offsetParent", + "offsetPath", + "offsetRotate", + "offsetTop", + "offsetWidth", + "offsetX", + "offsetY", + "ok", + "oldURL", + "oldValue", + "oldVersion", + "olderShadowRoot", + "onDownloadProgress", + "onInstallStageChanged", + "onLine", + "onabort", + "onabsolutedeviceorientation", + "onactivate", + "onactive", + "onaddsourcebuffer", + "onaddstream", + "onaddtrack", + "onafterprint", + "onafterscriptexecute", + "onafterupdate", + "onanimationcancel", + "onanimationend", + "onanimationiteration", + "onanimationstart", + "onappinstalled", + "onaudioend", + "onaudioprocess", + "onaudiostart", + "onautocomplete", + "onautocompleteerror", + "onauxclick", + "onbeforeactivate", + "onbeforecopy", + "onbeforecut", + "onbeforedeactivate", + "onbeforeeditfocus", + "onbeforeinput", + "onbeforeinstallprompt", + "onbeforeload", + "onbeforepaste", + "onbeforeprint", + "onbeforescriptexecute", + "onbeforeunload", + "onbeforeupdate", + "onbeforexrselect", + "onbegin", + "onblocked", + "onblur", + "onbounce", + "onboundary", + "onbufferedamountlow", + "oncached", + "oncancel", + "oncandidatewindowhide", + "oncandidatewindowshow", + "oncandidatewindowupdate", + "oncanplay", + "oncanplaythrough", + "once", + "oncellchange", + "onchange", + "oncharacteristicvaluechanged", + "onchargingchange", + "onchargingtimechange", + "onchecking", + "onclick", + "onclose", + "onclosing", + "oncompassneedscalibration", + "oncomplete", + "onconnect", + "onconnecting", + "onconnectionavailable", + "onconnectionstatechange", + "oncontactchange", + "oncontextmenu", + "oncontrollerchange", + "oncontrolselect", + "oncopy", + "oncuechange", + "oncut", + "ondataavailable", + "ondatachannel", + "ondatasetchanged", + "ondatasetcomplete", + "ondblclick", + "ondeactivate", + "ondevicechange", + "ondevicelight", + "ondevicemotion", + "ondeviceorientation", + "ondeviceorientationabsolute", + "ondeviceproximity", + "ondischargingtimechange", + "ondisconnect", + "ondisplay", + "ondownloading", + "ondrag", + "ondragend", + "ondragenter", + "ondragexit", + "ondragleave", + "ondragover", + "ondragstart", + "ondrop", + "ondurationchange", + "onemptied", + "onencrypted", + "onend", + "onended", + "onenter", + "onenterpictureinpicture", + "onerror", + "onerrorupdate", + "onexit", + "onfilterchange", + "onfinish", + "onfocus", + "onfocusin", + "onfocusout", + "onformdata", + "onfreeze", + "onfullscreenchange", + "onfullscreenerror", + "ongatheringstatechange", + "ongattserverdisconnected", + "ongesturechange", + "ongestureend", + "ongesturestart", + "ongotpointercapture", + "onhashchange", + "onhelp", + "onicecandidate", + "onicecandidateerror", + "oniceconnectionstatechange", + "onicegatheringstatechange", + "oninactive", + "oninput", + "oninputsourceschange", + "oninvalid", + "onkeydown", + "onkeypress", + "onkeystatuseschange", + "onkeyup", + "onlanguagechange", + "onlayoutcomplete", + "onleavepictureinpicture", + "onlevelchange", + "onload", + "onloadT", + "onloadeddata", + "onloadedmetadata", + "onloadend", + "onloading", + "onloadingdone", + "onloadingerror", + "onloadstart", + "onlosecapture", + "onlostpointercapture", + "only", + "onmark", + "onmessage", + "onmessageerror", + "onmidimessage", + "onmousedown", + "onmouseenter", + "onmouseleave", + "onmousemove", + "onmouseout", + "onmouseover", + "onmouseup", + "onmousewheel", + "onmove", + "onmoveend", + "onmovestart", + "onmozfullscreenchange", + "onmozfullscreenerror", + "onmozorientationchange", + "onmozpointerlockchange", + "onmozpointerlockerror", + "onmscontentzoom", + "onmsfullscreenchange", + "onmsfullscreenerror", + "onmsgesturechange", + "onmsgesturedoubletap", + "onmsgestureend", + "onmsgesturehold", + "onmsgesturestart", + "onmsgesturetap", + "onmsgotpointercapture", + "onmsinertiastart", + "onmslostpointercapture", + "onmsmanipulationstatechanged", + "onmsneedkey", + "onmsorientationchange", + "onmspointercancel", + "onmspointerdown", + "onmspointerenter", + "onmspointerhover", + "onmspointerleave", + "onmspointermove", + "onmspointerout", + "onmspointerover", + "onmspointerup", + "onmssitemodejumplistitemremoved", + "onmsthumbnailclick", + "onmute", + "onnegotiationneeded", + "onnomatch", + "onnoupdate", + "onobsolete", + "onoffline", + "ononline", + "onopen", + "onorientationchange", + "onoverconstrained", + "onpage", + "onpagechange", + "onpagehide", + "onpageshow", + "onpaste", + "onpause", + "onpayerdetailchange", + "onpaymentmethodchange", + "onplay", + "onplaying", + "onpluginstreamstart", + "onpointercancel", + "onpointerdown", + "onpointerenter", + "onpointerleave", + "onpointerlockchange", + "onpointerlockerror", + "onpointermove", + "onpointerout", + "onpointerover", + "onpointerrawupdate", + "onpointerup", + "onpopstate", + "onprocessorerror", + "onprogress", + "onpropertychange", + "onratechange", + "onreading", + "onreadystatechange", + "onrejectionhandled", + "onrelease", + "onremove", + "onremovesourcebuffer", + "onremovestream", + "onremovetrack", + "onrepeat", + "onreset", + "onresize", + "onresizeend", + "onresizestart", + "onresourcetimingbufferfull", + "onresult", + "onresume", + "onrowenter", + "onrowexit", + "onrowsdelete", + "onrowsinserted", + "onscroll", + "onsearch", + "onsecuritypolicyviolation", + "onseeked", + "onseeking", + "onselect", + "onselectedcandidatepairchange", + "onselectend", + "onselectionchange", + "onselectstart", + "onshippingaddresschange", + "onshippingoptionchange", + "onshow", + "onsignalingstatechange", + "onsoundend", + "onsoundstart", + "onsourceclose", + "onsourceclosed", + "onsourceended", + "onsourceopen", + "onspeechend", + "onspeechstart", + "onsqueeze", + "onsqueezeend", + "onsqueezestart", + "onstalled", + "onstart", + "onstatechange", + "onstop", + "onstorage", + "onstoragecommit", + "onsubmit", + "onsuccess", + "onsuspend", + "onterminate", + "ontextinput", + "ontimeout", + "ontimeupdate", + "ontoggle", + "ontonechange", + "ontouchcancel", + "ontouchend", + "ontouchmove", + "ontouchstart", + "ontrack", + "ontransitioncancel", + "ontransitionend", + "ontransitionrun", + "ontransitionstart", + "onunhandledrejection", + "onunload", + "onunmute", + "onupdate", + "onupdateend", + "onupdatefound", + "onupdateready", + "onupdatestart", + "onupgradeneeded", + "onuserproximity", + "onversionchange", + "onvisibilitychange", + "onvoiceschanged", + "onvolumechange", + "onvrdisplayactivate", + "onvrdisplayconnect", + "onvrdisplaydeactivate", + "onvrdisplaydisconnect", + "onvrdisplaypresentchange", + "onwaiting", + "onwaitingforkey", + "onwarning", + "onwebkitanimationend", + "onwebkitanimationiteration", + "onwebkitanimationstart", + "onwebkitcurrentplaybacktargetiswirelesschanged", + "onwebkitfullscreenchange", + "onwebkitfullscreenerror", + "onwebkitkeyadded", + "onwebkitkeyerror", + "onwebkitkeymessage", + "onwebkitmouseforcechanged", + "onwebkitmouseforcedown", + "onwebkitmouseforceup", + "onwebkitmouseforcewillbegin", + "onwebkitneedkey", + "onwebkitorientationchange", + "onwebkitplaybacktargetavailabilitychanged", + "onwebkitpointerlockchange", + "onwebkitpointerlockerror", + "onwebkitresourcetimingbufferfull", + "onwebkittransitionend", + "onwheel", + "onzoom", + "opacity", + "open", + "openCursor", + "openDatabase", + "openKeyCursor", + "opened", + "opener", + "opera", + "operationType", + "operator", + "opr", + "opsProfile", + "optimum", + "options", + "or", + "order", + "orderX", + "orderY", + "ordered", + "org", + "organization", + "orient", + "orientAngle", + "orientType", + "orientation", + "orientationX", + "orientationY", + "orientationZ", + "origin", + "originalPolicy", + "originalTarget", + "orphans", + "oscpu", + "outcome", + "outerHTML", + "outerHeight", + "outerText", + "outerWidth", + "outline", + "outline-color", + "outline-offset", + "outline-style", + "outline-width", + "outlineColor", + "outlineOffset", + "outlineStyle", + "outlineWidth", + "outputBuffer", + "outputLatency", + "outputs", + "overflow", + "overflow-anchor", + "overflow-block", + "overflow-inline", + "overflow-wrap", + "overflow-x", + "overflow-y", + "overflowAnchor", + "overflowBlock", + "overflowInline", + "overflowWrap", + "overflowX", + "overflowY", + "overrideMimeType", + "oversample", + "overscroll-behavior", + "overscroll-behavior-block", + "overscroll-behavior-inline", + "overscroll-behavior-x", + "overscroll-behavior-y", + "overscrollBehavior", + "overscrollBehaviorBlock", + "overscrollBehaviorInline", + "overscrollBehaviorX", + "overscrollBehaviorY", + "ownKeys", + "ownerDocument", + "ownerElement", + "ownerNode", + "ownerRule", + "ownerSVGElement", + "owningElement", + "p1", + "p2", + "p3", + "p4", + "packetSize", + "packets", + "pad", + "padEnd", + "padStart", + "padding", + "padding-block", + "padding-block-end", + "padding-block-start", + "padding-bottom", + "padding-inline", + "padding-inline-end", + "padding-inline-start", + "padding-left", + "padding-right", + "padding-top", + "paddingBlock", + "paddingBlockEnd", + "paddingBlockStart", + "paddingBottom", + "paddingInline", + "paddingInlineEnd", + "paddingInlineStart", + "paddingLeft", + "paddingRight", + "paddingTop", + "page", + "page-break-after", + "page-break-before", + "page-break-inside", + "pageBreakAfter", + "pageBreakBefore", + "pageBreakInside", + "pageCount", + "pageLeft", + "pageT", + "pageTop", + "pageX", + "pageXOffset", + "pageY", + "pageYOffset", + "pages", + "paint-order", + "paintOrder", + "paintRequests", + "paintType", + "paintWorklet", + "palette", + "pan", + "panningModel", + "parameters", + "parent", + "parentElement", + "parentNode", + "parentRule", + "parentStyleSheet", + "parentTextEdit", + "parentWindow", + "parse", + "parseAll", + "parseFloat", + "parseFromString", + "parseInt", + "part", + "participants", + "passive", + "password", + "pasteHTML", + "path", + "pathLength", + "pathSegList", + "pathSegType", + "pathSegTypeAsLetter", + "pathname", + "pattern", + "patternContentUnits", + "patternMismatch", + "patternTransform", + "patternUnits", + "pause", + "pauseAnimations", + "pauseOnExit", + "pauseTransformFeedback", + "paused", + "payerEmail", + "payerName", + "payerPhone", + "paymentManager", + "pc", + "peerIdentity", + "pending", + "pendingLocalDescription", + "pendingRemoteDescription", + "percent", + "performance", + "periodicSync", + "permission", + "permissionState", + "permissions", + "persist", + "persisted", + "personalbar", + "perspective", + "perspective-origin", + "perspectiveOrigin", + "perspectiveOriginX", + "perspectiveOriginY", + "phone", + "phoneticFamilyName", + "phoneticGivenName", + "photo", + "pictureInPictureElement", + "pictureInPictureEnabled", + "pictureInPictureWindow", + "ping", + "pipeThrough", + "pipeTo", + "pitch", + "pixelBottom", + "pixelDepth", + "pixelHeight", + "pixelLeft", + "pixelRight", + "pixelStorei", + "pixelTop", + "pixelUnitToMillimeterX", + "pixelUnitToMillimeterY", + "pixelWidth", + "place-content", + "place-items", + "place-self", + "placeContent", + "placeItems", + "placeSelf", + "placeholder", + "platform", + "platforms", + "play", + "playEffect", + "playState", + "playbackRate", + "playbackState", + "playbackTime", + "played", + "playoutDelayHint", + "playsInline", + "plugins", + "pluginspage", + "pname", + "pointer-events", + "pointerBeforeReferenceNode", + "pointerEnabled", + "pointerEvents", + "pointerId", + "pointerLockElement", + "pointerType", + "points", + "pointsAtX", + "pointsAtY", + "pointsAtZ", + "polygonOffset", + "pop", + "populateMatrix", + "popupWindowFeatures", + "popupWindowName", + "popupWindowURI", + "port", + "port1", + "port2", + "ports", + "posBottom", + "posHeight", + "posLeft", + "posRight", + "posTop", + "posWidth", + "pose", + "position", + "positionAlign", + "positionX", + "positionY", + "positionZ", + "postError", + "postMessage", + "postalCode", + "poster", + "pow", + "powerEfficient", + "powerOff", + "preMultiplySelf", + "precision", + "preferredStyleSheetSet", + "preferredStylesheetSet", + "prefix", + "preload", + "prepend", + "presentation", + "preserveAlpha", + "preserveAspectRatio", + "preserveAspectRatioString", + "pressed", + "pressure", + "prevValue", + "preventDefault", + "preventExtensions", + "preventSilentAccess", + "previousElementSibling", + "previousNode", + "previousPage", + "previousRect", + "previousScale", + "previousSibling", + "previousTranslate", + "primaryKey", + "primitiveType", + "primitiveUnits", + "principals", + "print", + "priority", + "privateKey", + "probablySupportsContext", + "process", + "processIceMessage", + "processingEnd", + "processingStart", + "product", + "productId", + "productName", + "productSub", + "profile", + "profileEnd", + "profiles", + "projectionMatrix", + "promise", + "prompt", + "properties", + "propertyIsEnumerable", + "propertyName", + "protocol", + "protocolLong", + "prototype", + "provider", + "pseudoClass", + "pseudoElement", + "pt", + "publicId", + "publicKey", + "published", + "pulse", + "push", + "pushManager", + "pushNotification", + "pushState", + "put", + "putImageData", + "px", + "quadraticCurveTo", + "qualifier", + "quaternion", + "query", + "queryCommandEnabled", + "queryCommandIndeterm", + "queryCommandState", + "queryCommandSupported", + "queryCommandText", + "queryCommandValue", + "querySelector", + "querySelectorAll", + "queryUsageAndQuota", + "queueMicrotask", + "quote", + "quotes", + "r", + "r1", + "r2", + "race", + "rad", + "radiogroup", + "radiusX", + "radiusY", + "random", + "range", + "rangeCount", + "rangeMax", + "rangeMin", + "rangeOffset", + "rangeOverflow", + "rangeParent", + "rangeUnderflow", + "rate", + "ratio", + "raw", + "rawId", + "read", + "readAsArrayBuffer", + "readAsBinaryString", + "readAsBlob", + "readAsDataURL", + "readAsText", + "readBuffer", + "readEntries", + "readOnly", + "readPixels", + "readReportRequested", + "readText", + "readTransaction", + "readValue", + "readable", + "ready", + "readyState", + "reason", + "reboot", + "receivedAlert", + "receivedTime", + "receiver", + "receivers", + "recipient", + "reconnect", + "record", + "recordEnd", + "recordNumber", + "recordsAvailable", + "recordset", + "rect", + "red", + "redEyeReduction", + "redirect", + "redirectCount", + "redirectEnd", + "redirectStart", + "redirected", + "reduce", + "reduceRight", + "reduction", + "refDistance", + "refX", + "refY", + "referenceNode", + "referenceSpace", + "referrer", + "referrerPolicy", + "refresh", + "region", + "regionAnchorX", + "regionAnchorY", + "regionId", + "regions", + "register", + "registerContentHandler", + "registerElement", + "registerProperty", + "registerProtocolHandler", + "reject", + "rel", + "relList", + "relatedAddress", + "relatedNode", + "relatedPort", + "relatedTarget", + "release", + "releaseCapture", + "releaseEvents", + "releaseInterface", + "releaseLock", + "releasePointerCapture", + "releaseShaderCompiler", + "reliable", + "reliableWrite", + "reload", + "rem", + "remainingSpace", + "remote", + "remoteDescription", + "remove", + "removeAllRanges", + "removeAttribute", + "removeAttributeNS", + "removeAttributeNode", + "removeBehavior", + "removeChild", + "removeCue", + "removeEventListener", + "removeFilter", + "removeImport", + "removeItem", + "removeListener", + "removeNamedItem", + "removeNamedItemNS", + "removeNode", + "removeParameter", + "removeProperty", + "removeRange", + "removeRegion", + "removeRule", + "removeRules", + "removeSiteSpecificTrackingException", + "removeSourceBuffer", + "removeStream", + "removeTrack", + "removeVariable", + "removeWakeLockListener", + "removeWebWideTrackingException", + "removed", + "removedNodes", + "renderHeight", + "renderState", + "renderTime", + "renderWidth", + "renderbufferStorage", + "renderbufferStorageMultisample", + "renderedBuffer", + "renderingMode", + "renotify", + "repeat", + "replace", + "replaceAdjacentText", + "replaceAll", + "replaceChild", + "replaceChildren", + "replaceData", + "replaceId", + "replaceItem", + "replaceNode", + "replaceState", + "replaceSync", + "replaceTrack", + "replaceWholeText", + "replaceWith", + "reportValidity", + "request", + "requestAnimationFrame", + "requestAutocomplete", + "requestData", + "requestDevice", + "requestFrame", + "requestFullscreen", + "requestHitTestSource", + "requestHitTestSourceForTransientInput", + "requestId", + "requestIdleCallback", + "requestMIDIAccess", + "requestMediaKeySystemAccess", + "requestPermission", + "requestPictureInPicture", + "requestPointerLock", + "requestPresent", + "requestQuota", + "requestReferenceSpace", + "requestSession", + "requestStart", + "requestStorageAccess", + "requestSubmit", + "requestTime", + "requestVideoFrameCallback", + "requestedLocale", + "requestingWindow", + "requireInteraction", + "required", + "requiredExtensions", + "requiredFeatures", + "reset", + "resetPose", + "resetTransform", + "resize", + "resizeBy", + "resizeTo", + "resolve", + "resolved", + "resolvedOptions", + "resource-history", + "resourcesFramesExpanded", + "response", + "responseBody", + "responseEnd", + "responseReady", + "responseStart", + "responseText", + "responseType", + "responseURL", + "responseXML", + "restartIce", + "restore", + "result", + "resultIndex", + "resultType", + "results", + "resume", + "resumeTransformFeedback", + "retry", + "returnValue", + "rev", + "reverse", + "reversed", + "revocable", + "revokeObjectURL", + "rgbColor", + "right", + "rightContext", + "rightDegrees", + "rightMargin", + "rightProjectionMatrix", + "rightViewMatrix", + "role", + "rolloffFactor", + "root", + "rootBounds", + "rootElement", + "rootMargin", + "rotate", + "rotateAxisAngle", + "rotateAxisAngleSelf", + "rotateFromVector", + "rotateFromVectorSelf", + "rotateSelf", + "rotation", + "rotationAngle", + "rotationRate", + "round", + "row-gap", + "rowGap", + "rowIndex", + "rowSpan", + "rows", + "rowsAffected", + "rtcpTransport", + "rtt", + "ruby-align", + "ruby-position", + "rubyAlign", + "rubyOverhang", + "rubyPosition", + "rules", + "runningState", + "runtime", + "runtimeStyle", + "rx", + "ry", + "s", + "safari", + "sample", + "sampleCoverage", + "sampleRate", + "samplerParameterf", + "samplerParameteri", + "sandbox", + "save", + "saveData", + "scale", + "scale3d", + "scale3dSelf", + "scaleNonUniform", + "scaleNonUniformSelf", + "scaleSelf", + "scheme", + "scissor", + "scope", + "scopeName", + "scoped", + "screen", + "screenBrightness", + "screenEnabled", + "screenLeft", + "screenPixelToMillimeterX", + "screenPixelToMillimeterY", + "screenTop", + "screenX", + "screenY", + "script", + "scriptURL", + "scripts", + "scroll", + "scroll-behavior", + "scroll-margin", + "scroll-margin-block", + "scroll-margin-block-end", + "scroll-margin-block-start", + "scroll-margin-bottom", + "scroll-margin-inline", + "scroll-margin-inline-end", + "scroll-margin-inline-start", + "scroll-margin-left", + "scroll-margin-right", + "scroll-margin-top", + "scroll-padding", + "scroll-padding-block", + "scroll-padding-block-end", + "scroll-padding-block-start", + "scroll-padding-bottom", + "scroll-padding-inline", + "scroll-padding-inline-end", + "scroll-padding-inline-start", + "scroll-padding-left", + "scroll-padding-right", + "scroll-padding-top", + "scroll-snap-align", + "scroll-snap-coordinate", + "scroll-snap-destination", + "scroll-snap-points-x", + "scroll-snap-points-y", + "scroll-snap-type", + "scroll-snap-type-x", + "scroll-snap-type-y", + "scrollAmount", + "scrollBehavior", + "scrollBy", + "scrollByLines", + "scrollByPages", + "scrollDelay", + "scrollHeight", + "scrollIntoView", + "scrollIntoViewIfNeeded", + "scrollLeft", + "scrollLeftMax", + "scrollMargin", + "scrollMarginBlock", + "scrollMarginBlockEnd", + "scrollMarginBlockStart", + "scrollMarginBottom", + "scrollMarginInline", + "scrollMarginInlineEnd", + "scrollMarginInlineStart", + "scrollMarginLeft", + "scrollMarginRight", + "scrollMarginTop", + "scrollMaxX", + "scrollMaxY", + "scrollPadding", + "scrollPaddingBlock", + "scrollPaddingBlockEnd", + "scrollPaddingBlockStart", + "scrollPaddingBottom", + "scrollPaddingInline", + "scrollPaddingInlineEnd", + "scrollPaddingInlineStart", + "scrollPaddingLeft", + "scrollPaddingRight", + "scrollPaddingTop", + "scrollRestoration", + "scrollSnapAlign", + "scrollSnapCoordinate", + "scrollSnapDestination", + "scrollSnapMargin", + "scrollSnapMarginBottom", + "scrollSnapMarginLeft", + "scrollSnapMarginRight", + "scrollSnapMarginTop", + "scrollSnapPointsX", + "scrollSnapPointsY", + "scrollSnapStop", + "scrollSnapType", + "scrollSnapTypeX", + "scrollSnapTypeY", + "scrollTo", + "scrollTop", + "scrollTopMax", + "scrollWidth", + "scrollX", + "scrollY", + "scrollbar-color", + "scrollbar-width", + "scrollbar3dLightColor", + "scrollbarArrowColor", + "scrollbarBaseColor", + "scrollbarColor", + "scrollbarDarkShadowColor", + "scrollbarFaceColor", + "scrollbarHighlightColor", + "scrollbarShadowColor", + "scrollbarTrackColor", + "scrollbarWidth", + "scrollbars", + "scrolling", + "scrollingElement", + "sctp", + "sctpCauseCode", + "sdp", + "sdpLineNumber", + "sdpMLineIndex", + "sdpMid", + "seal", + "search", + "searchBox", + "searchBoxJavaBridge_", + "searchParams", + "second", + "sectionRowIndex", + "secureConnectionStart", + "security", + "seed", + "seekToNextFrame", + "seekable", + "seeking", + "select", + "selectAllChildren", + "selectAlternateInterface", + "selectConfiguration", + "selectNode", + "selectNodeContents", + "selectNodes", + "selectSingleNode", + "selectSubString", + "selected", + "selectedIndex", + "selectedOption", + "selectedOptions", + "selectedStyleSheetSet", + "selectedStylesheetSet", + "selection", + "selectionDirection", + "selectionEnd", + "selectionStart", + "selector", + "selectorText", + "self", + "send", + "sendAsBinary", + "sendBeacon", + "sendMessage", + "sender", + "sensitivity", + "sentAlert", + "sentTimestamp", + "separator", + "serialNumber", + "serializeToString", + "serverTiming", + "service", + "serviceWorker", + "session", + "sessionId", + "sessionStorage", + "set", + "setActionHandler", + "setActive", + "setAlpha", + "setAppBadge", + "setAttribute", + "setAttributeNS", + "setAttributeNode", + "setAttributeNodeNS", + "setBaseAndExtent", + "setBigInt64", + "setBigUint64", + "setBingCurrentSearchDefault", + "setCapture", + "setCodecPreferences", + "setColor", + "setCompositeOperation", + "setConfiguration", + "setCurrentTime", + "setCustomValidity", + "setData", + "setDate", + "setDirection", + "setDragImage", + "setEnd", + "setEndAfter", + "setEndBefore", + "setEndPoint", + "setFillColor", + "setFilterRes", + "setFloat32", + "setFloat64", + "setFloatValue", + "setFormValue", + "setFullYear", + "setHeaderValue", + "setHours", + "setIdentityProvider", + "setImmediate", + "setInt16", + "setInt32", + "setInt8", + "setInterval", + "setItem", + "setKeyframes", + "setLineCap", + "setLineDash", + "setLineJoin", + "setLineWidth", + "setLiveSeekableRange", + "setLocalDescription", + "setMatrix", + "setMatrixValue", + "setMediaKeys", + "setMilliseconds", + "setMinutes", + "setMiterLimit", + "setMonth", + "setNamedItem", + "setNamedItemNS", + "setNonUserCodeExceptions", + "setOrientToAngle", + "setOrientToAuto", + "setOrientation", + "setOverrideHistoryNavigationMode", + "setPaint", + "setParameter", + "setParameters", + "setPeriodicWave", + "setPointerCapture", + "setPosition", + "setPositionState", + "setPreference", + "setProperty", + "setPrototypeOf", + "setRGBColor", + "setRGBColorICCColor", + "setRadius", + "setRangeText", + "setRemoteDescription", + "setRequestHeader", + "setResizable", + "setResourceTimingBufferSize", + "setRotate", + "setScale", + "setSeconds", + "setSelectionRange", + "setServerCertificate", + "setShadow", + "setSinkId", + "setSkewX", + "setSkewY", + "setStart", + "setStartAfter", + "setStartBefore", + "setStdDeviation", + "setStreams", + "setStringValue", + "setStrokeColor", + "setSuggestResult", + "setTargetAtTime", + "setTargetValueAtTime", + "setTime", + "setTimeout", + "setTransform", + "setTranslate", + "setUTCDate", + "setUTCFullYear", + "setUTCHours", + "setUTCMilliseconds", + "setUTCMinutes", + "setUTCMonth", + "setUTCSeconds", + "setUint16", + "setUint32", + "setUint8", + "setUri", + "setValidity", + "setValueAtTime", + "setValueCurveAtTime", + "setVariable", + "setVelocity", + "setVersion", + "setYear", + "settingName", + "settingValue", + "sex", + "shaderSource", + "shadowBlur", + "shadowColor", + "shadowOffsetX", + "shadowOffsetY", + "shadowRoot", + "shape", + "shape-image-threshold", + "shape-margin", + "shape-outside", + "shape-rendering", + "shapeImageThreshold", + "shapeMargin", + "shapeOutside", + "shapeRendering", + "sheet", + "shift", + "shiftKey", + "shiftLeft", + "shippingAddress", + "shippingOption", + "shippingType", + "show", + "showHelp", + "showModal", + "showModalDialog", + "showModelessDialog", + "showNotification", + "sidebar", + "sign", + "signal", + "signalingState", + "signature", + "silent", + "sin", + "singleNodeValue", + "sinh", + "sinkId", + "sittingToStandingTransform", + "size", + "sizeToContent", + "sizeX", + "sizeZ", + "sizes", + "skewX", + "skewXSelf", + "skewY", + "skewYSelf", + "slice", + "slope", + "slot", + "small", + "smil", + "smooth", + "smoothingTimeConstant", + "snapToLines", + "snapshotItem", + "snapshotLength", + "some", + "sort", + "sortingCode", + "source", + "sourceBuffer", + "sourceBuffers", + "sourceCapabilities", + "sourceFile", + "sourceIndex", + "sourceURL", + "sources", + "spacing", + "span", + "speak", + "speakAs", + "speaking", + "species", + "specified", + "specularConstant", + "specularExponent", + "speechSynthesis", + "speed", + "speedOfSound", + "spellcheck", + "splice", + "split", + "splitText", + "spreadMethod", + "sqrt", + "src", + "srcElement", + "srcFilter", + "srcObject", + "srcUrn", + "srcdoc", + "srclang", + "srcset", + "stack", + "stackTraceLimit", + "stacktrace", + "stageParameters", + "standalone", + "standby", + "start", + "startContainer", + "startE", + "startIce", + "startLoadTime", + "startMessages", + "startNotifications", + "startOffset", + "startRendering", + "startSoftwareUpdate", + "startTime", + "startsWith", + "state", + "status", + "statusCode", + "statusMessage", + "statusText", + "statusbar", + "stdDeviationX", + "stdDeviationY", + "stencilFunc", + "stencilFuncSeparate", + "stencilMask", + "stencilMaskSeparate", + "stencilOp", + "stencilOpSeparate", + "step", + "stepDown", + "stepMismatch", + "stepUp", + "sticky", + "stitchTiles", + "stop", + "stop-color", + "stop-opacity", + "stopColor", + "stopImmediatePropagation", + "stopNotifications", + "stopOpacity", + "stopPropagation", + "stopped", + "storage", + "storageArea", + "storageName", + "storageStatus", + "store", + "storeSiteSpecificTrackingException", + "storeWebWideTrackingException", + "stpVersion", + "stream", + "streams", + "strength", + "stretch", + "strike", + "stringValue", + "stringify", + "stroke", + "stroke-dasharray", + "stroke-dashoffset", + "stroke-linecap", + "stroke-linejoin", + "stroke-miterlimit", + "stroke-opacity", + "stroke-width", + "strokeColor", + "strokeDasharray", + "strokeDashoffset", + "strokeLinecap", + "strokeLinejoin", + "strokeMiterlimit", + "strokeOpacity", + "strokeRect", + "strokeStyle", + "strokeText", + "strokeWidth", + "style", + "styleFloat", + "styleMap", + "styleMedia", + "styleSheet", + "styleSheetSets", + "styleSheets", + "sub", + "subarray", + "subject", + "submit", + "submitFrame", + "submitter", + "subscribe", + "substr", + "substring", + "substringData", + "subtle", + "subtree", + "suffix", + "suffixes", + "summary", + "sup", + "supported", + "supportedContentEncodings", + "supportedEntryTypes", + "supportedLocalesOf", + "supports", + "supportsSession", + "surfaceScale", + "surroundContents", + "suspend", + "suspendRedraw", + "swapCache", + "swapNode", + "sweepFlag", + "symbols", + "sync", + "sysexEnabled", + "system", + "systemCode", + "systemId", + "systemLanguage", + "systemXDPI", + "systemYDPI", + "tBodies", + "tFoot", + "tHead", + "tabIndex", + "tabSize", + "table", + "table-layout", + "tableLayout", + "tableValues", + "tag", + "tagName", + "tagUrn", + "tags", + "taintEnabled", + "takeHeapSnapshot", + "takePhoto", + "takeRecords", + "tan", + "tangentialPressure", + "tanh", + "target", + "targetElement", + "targetRayMode", + "targetRaySpace", + "targetTouches", + "targetX", + "targetY", + "tcpType", + "tee", + "tel", + "terminate", + "test", + "texImage2D", + "texImage3D", + "texParameterf", + "texParameteri", + "texStorage2D", + "texStorage3D", + "texSubImage2D", + "texSubImage3D", + "text", + "text-align", + "text-align-last", + "text-anchor", + "text-combine-upright", + "text-decoration", + "text-decoration-color", + "text-decoration-line", + "text-decoration-skip-ink", + "text-decoration-style", + "text-decoration-thickness", + "text-emphasis", + "text-emphasis-color", + "text-emphasis-position", + "text-emphasis-style", + "text-indent", + "text-justify", + "text-orientation", + "text-overflow", + "text-rendering", + "text-shadow", + "text-transform", + "text-underline-offset", + "text-underline-position", + "text/pdf", + "textAlign", + "textAlignLast", + "textAnchor", + "textAutospace", + "textBaseline", + "textCombineUpright", + "textContent", + "textDecoration", + "textDecorationBlink", + "textDecorationColor", + "textDecorationLine", + "textDecorationLineThrough", + "textDecorationNone", + "textDecorationOverline", + "textDecorationSkipInk", + "textDecorationStyle", + "textDecorationThickness", + "textDecorationUnderline", + "textEmphasis", + "textEmphasisColor", + "textEmphasisPosition", + "textEmphasisStyle", + "textIndent", + "textJustify", + "textJustifyTrim", + "textKashida", + "textKashidaSpace", + "textLength", + "textOrientation", + "textOverflow", + "textRendering", + "textShadow", + "textSizeAdjust", + "textTracks", + "textTransform", + "textUnderlineOffset", + "textUnderlinePosition", + "then", + "threadId", + "threshold", + "thresholds", + "tiltX", + "tiltY", + "time", + "timeEnd", + "timeLog", + "timeOrigin", + "timeRemaining", + "timeStamp", + "timeZone", + "timeZoneName", + "timecode", + "timeline", + "timelineEnd", + "timelineTime", + "timeout", + "timestamp", + "timestampOffset", + "timing", + "title", + "to", + "toArray", + "toBlob", + "toDataURL", + "toDateString", + "toElement", + "toExponential", + "toFixed", + "toFloat32Array", + "toFloat64Array", + "toGMTString", + "toISOString", + "toJSON", + "toLocaleDateString", + "toLocaleFormat", + "toLocaleLowerCase", + "toLocaleString", + "toLocaleTimeString", + "toLocaleUpperCase", + "toLowerCase", + "toMatrix", + "toMethod", + "toPrecision", + "toPrimitive", + "toSdp", + "toSource", + "toStaticHTML", + "toString", + "toStringTag", + "toSum", + "toTimeString", + "toUTCString", + "toUpperCase", + "toggle", + "toggleAttribute", + "toggleLongPressEnabled", + "tone", + "toneBuffer", + "tooLong", + "tooShort", + "toolbar", + "top", + "topMargin", + "total", + "totalFrameDelay", + "totalJSHeapSize", + "totalSize", + "totalVideoFrames", + "touch-action", + "touchAction", + "touched", + "touches", + "trace", + "track", + "trackVisibility", + "tran", + "transaction", + "transactions", + "transceiver", + "transferControlToOffscreen", + "transferFromImageBitmap", + "transferImageBitmap", + "transferIn", + "transferOut", + "transferSize", + "transferToImageBitmap", + "transform", + "transform-box", + "transform-origin", + "transform-style", + "transformBox", + "transformFeedbackVaryings", + "transformOrigin", + "transformOriginX", + "transformOriginY", + "transformOriginZ", + "transformPoint", + "transformString", + "transformStyle", + "transformToDocument", + "transformToFragment", + "transition", + "transition-delay", + "transition-duration", + "transition-property", + "transition-timing-function", + "transitionDelay", + "transitionDuration", + "transitionProperty", + "transitionTimingFunction", + "translate", + "translateSelf", + "translationX", + "translationY", + "transport", + "trim", + "trimEnd", + "trimLeft", + "trimRight", + "trimStart", + "trueSpeed", + "trunc", + "truncate", + "trustedTypes", + "turn", + "twist", + "type", + "typeDetail", + "typeMismatch", + "typeMustMatch", + "types", + "tz", + "u2f", + "ubound", + "undefined", + "unescape", + "uneval", + "unicode", + "unicode-bidi", + "unicodeBidi", + "unicodeRange", + "uniform1f", + "uniform1fv", + "uniform1i", + "uniform1iv", + "uniform1ui", + "uniform1uiv", + "uniform2f", + "uniform2fv", + "uniform2i", + "uniform2iv", + "uniform2ui", + "uniform2uiv", + "uniform3f", + "uniform3fv", + "uniform3i", + "uniform3iv", + "uniform3ui", + "uniform3uiv", + "uniform4f", + "uniform4fv", + "uniform4i", + "uniform4iv", + "uniform4ui", + "uniform4uiv", + "uniformBlockBinding", + "uniformMatrix2fv", + "uniformMatrix2x3fv", + "uniformMatrix2x4fv", + "uniformMatrix3fv", + "uniformMatrix3x2fv", + "uniformMatrix3x4fv", + "uniformMatrix4fv", + "uniformMatrix4x2fv", + "uniformMatrix4x3fv", + "unique", + "uniqueID", + "uniqueNumber", + "unit", + "unitType", + "units", + "unloadEventEnd", + "unloadEventStart", + "unlock", + "unmount", + "unobserve", + "unpause", + "unpauseAnimations", + "unreadCount", + "unregister", + "unregisterContentHandler", + "unregisterProtocolHandler", + "unscopables", + "unselectable", + "unshift", + "unsubscribe", + "unsuspendRedraw", + "unsuspendRedrawAll", + "unwatch", + "unwrapKey", + "upDegrees", + "upX", + "upY", + "upZ", + "update", + "updateCommands", + "updateEnabled", + "updateIce", + "updateInterval", + "updatePlaybackRate", + "updateRenderState", + "updateSettings", + "updateTiming", + "updateViaCache", + "updateWith", + "updated", + "updating", + "upgrade", + "upload", + "uploadTotal", + "uploaded", + "upper", + "upperBound", + "upperOpen", + "uri", + "url", + "urn", + "urns", + "usage", + "usages", + "usb", + "usbVersionMajor", + "usbVersionMinor", + "usbVersionSubminor", + "useCurrentView", + "useGrouping", + "useMap", + "useProgram", + "usedJSHeapSize", + "usedSpace", + "user-select", + "userActivation", + "userAgent", + "userChoice", + "userHandle", + "userHint", + "userLanguage", + "userProfile", + "userSelect", + "userVisibleOnly", + "userZoom", + "username", + "usernameFragment", + "utterance", + "uuid", + "v8BreakIterator", + "v8Parse", + "vAlign", + "vLink", + "valid", + "validate", + "validateProgram", + "validationMessage", + "validity", + "value", + "valueAsDate", + "valueAsNumber", + "valueAsString", + "valueInSpecifiedUnits", + "valueMissing", + "valueOf", + "valueText", + "valueType", + "values", + "variable", + "variant", + "vector-effect", + "vectorEffect", + "velocityAngular", + "velocityExpansion", + "velocityX", + "velocityY", + "vendor", + "vendorId", + "vendorSub", + "verify", + "version", + "vertexAttrib1f", + "vertexAttrib1fv", + "vertexAttrib2f", + "vertexAttrib2fv", + "vertexAttrib3f", + "vertexAttrib3fv", + "vertexAttrib4f", + "vertexAttrib4fv", + "vertexAttribDivisor", + "vertexAttribDivisorANGLE", + "vertexAttribI4i", + "vertexAttribI4iv", + "vertexAttribI4ui", + "vertexAttribI4uiv", + "vertexAttribIPointer", + "vertexAttribPointer", + "vertical", + "vertical-align", + "verticalAlign", + "verticalOverflow", + "vh", + "vibrate", + "vibrationActuator", + "video/x-ms-asf", + "video/x-ms-asf-plugin", + "video/x-ms-wm", + "video/x-ms-wmv", + "video/x-ms-wvx", + "videoBitsPerSecond", + "videoHeight", + "videoTracks", + "videoWidth", + "view", + "viewBox", + "viewBoxString", + "viewTarget", + "viewTargetString", + "viewport", + "viewportAnchorX", + "viewportAnchorY", + "viewportElement", + "views", + "violatedDirective", + "visibility", + "visibilityState", + "visible", + "visualViewport", + "vlinkColor", + "vmax", + "vmin", + "voice", + "voiceURI", + "volume", + "vrml", + "vspace", + "vw", + "w", + "wait", + "waitSync", + "waiting", + "wake", + "wakeLock", + "wand", + "warn", + "wasAlternateProtocolAvailable", + "wasClean", + "wasDiscarded", + "wasFetchedViaSpdy", + "wasNpnNegotiated", + "watch", + "watchAvailability", + "watchPosition", + "webdriver", + "webkitAddKey", + "webkitAlignContent", + "webkitAlignItems", + "webkitAlignSelf", + "webkitAnimation", + "webkitAnimationDelay", + "webkitAnimationDirection", + "webkitAnimationDuration", + "webkitAnimationFillMode", + "webkitAnimationIterationCount", + "webkitAnimationName", + "webkitAnimationPlayState", + "webkitAnimationTimingFunction", + "webkitAppRegion", + "webkitAppearance", + "webkitAspectRatio", + "webkitAudioContext", + "webkitAudioDecodedByteCount", + "webkitAudioPannerNode", + "webkitBackdropFilter", + "webkitBackfaceVisibility", + "webkitBackground", + "webkitBackgroundAttachment", + "webkitBackgroundClip", + "webkitBackgroundColor", + "webkitBackgroundComposite", + "webkitBackgroundImage", + "webkitBackgroundOrigin", + "webkitBackgroundPosition", + "webkitBackgroundPositionX", + "webkitBackgroundPositionY", + "webkitBackgroundRepeat", + "webkitBackgroundSize", + "webkitBackingStorePixelRatio", + "webkitBorderAfter", + "webkitBorderAfterColor", + "webkitBorderAfterStyle", + "webkitBorderAfterWidth", + "webkitBorderBefore", + "webkitBorderBeforeColor", + "webkitBorderBeforeStyle", + "webkitBorderBeforeWidth", + "webkitBorderBottomLeftRadius", + "webkitBorderBottomRightRadius", + "webkitBorderEnd", + "webkitBorderEndColor", + "webkitBorderEndStyle", + "webkitBorderEndWidth", + "webkitBorderFit", + "webkitBorderHorizontalSpacing", + "webkitBorderImage", + "webkitBorderImageOutset", + "webkitBorderImageRepeat", + "webkitBorderImageSlice", + "webkitBorderImageSource", + "webkitBorderImageWidth", + "webkitBorderRadius", + "webkitBorderStart", + "webkitBorderStartColor", + "webkitBorderStartStyle", + "webkitBorderStartWidth", + "webkitBorderTopLeftRadius", + "webkitBorderTopRightRadius", + "webkitBorderVerticalSpacing", + "webkitBoxAlign", + "webkitBoxDecorationBreak", + "webkitBoxDirection", + "webkitBoxFlex", + "webkitBoxFlexGroup", + "webkitBoxLines", + "webkitBoxOrdinalGroup", + "webkitBoxOrient", + "webkitBoxPack", + "webkitBoxReflect", + "webkitBoxShadow", + "webkitBoxSizing", + "webkitCancelAnimationFrame", + "webkitCancelFullScreen", + "webkitCancelKeyRequest", + "webkitCancelRequestAnimationFrame", + "webkitClearResourceTimings", + "webkitClipPath", + "webkitClosedCaptionsVisible", + "webkitColumnAxis", + "webkitColumnBreakAfter", + "webkitColumnBreakBefore", + "webkitColumnBreakInside", + "webkitColumnCount", + "webkitColumnGap", + "webkitColumnProgression", + "webkitColumnRule", + "webkitColumnRuleColor", + "webkitColumnRuleStyle", + "webkitColumnRuleWidth", + "webkitColumnSpan", + "webkitColumnWidth", + "webkitColumns", + "webkitConvertPointFromNodeToPage", + "webkitConvertPointFromPageToNode", + "webkitCreateShadowRoot", + "webkitCurrentFullScreenElement", + "webkitCurrentPlaybackTargetIsWireless", + "webkitCursorVisibility", + "webkitDashboardRegion", + "webkitDecodedFrameCount", + "webkitDirectionInvertedFromDevice", + "webkitDisplayingFullscreen", + "webkitDroppedFrameCount", + "webkitEnterFullScreen", + "webkitEnterFullscreen", + "webkitEntries", + "webkitExitFullScreen", + "webkitExitFullscreen", + "webkitExitPointerLock", + "webkitFilter", + "webkitFlex", + "webkitFlexBasis", + "webkitFlexDirection", + "webkitFlexFlow", + "webkitFlexGrow", + "webkitFlexShrink", + "webkitFlexWrap", + "webkitFontFeatureSettings", + "webkitFontKerning", + "webkitFontSizeDelta", + "webkitFontSmoothing", + "webkitForce", + "webkitFullScreenKeyboardInputAllowed", + "webkitFullscreenElement", + "webkitFullscreenEnabled", + "webkitGenerateKeyRequest", + "webkitGetAsEntry", + "webkitGetDatabaseNames", + "webkitGetEntries", + "webkitGetEntriesByName", + "webkitGetEntriesByType", + "webkitGetFlowByName", + "webkitGetGamepads", + "webkitGetImageDataHD", + "webkitGetNamedFlows", + "webkitGetRegionFlowRanges", + "webkitGetUserMedia", + "webkitHasClosedCaptions", + "webkitHidden", + "webkitHighlight", + "webkitHyphenateCharacter", + "webkitHyphenateLimitAfter", + "webkitHyphenateLimitBefore", + "webkitHyphenateLimitLines", + "webkitHyphens", + "webkitIDBCursor", + "webkitIDBDatabase", + "webkitIDBDatabaseError", + "webkitIDBDatabaseException", + "webkitIDBFactory", + "webkitIDBIndex", + "webkitIDBKeyRange", + "webkitIDBObjectStore", + "webkitIDBRequest", + "webkitIDBTransaction", + "webkitImageSmoothingEnabled", + "webkitIndexedDB", + "webkitInitMessageEvent", + "webkitInitialLetter", + "webkitIsFullScreen", + "webkitJustifyContent", + "webkitKeys", + "webkitLineAlign", + "webkitLineBoxContain", + "webkitLineBreak", + "webkitLineClamp", + "webkitLineDash", + "webkitLineDashOffset", + "webkitLineGrid", + "webkitLineSnap", + "webkitLocale", + "webkitLockOrientation", + "webkitLogicalHeight", + "webkitLogicalWidth", + "webkitMarginAfter", + "webkitMarginAfterCollapse", + "webkitMarginBefore", + "webkitMarginBeforeCollapse", + "webkitMarginBottomCollapse", + "webkitMarginCollapse", + "webkitMarginEnd", + "webkitMarginStart", + "webkitMarginTopCollapse", + "webkitMarquee", + "webkitMarqueeDirection", + "webkitMarqueeIncrement", + "webkitMarqueeRepetition", + "webkitMarqueeSpeed", + "webkitMarqueeStyle", + "webkitMask", + "webkitMaskBoxImage", + "webkitMaskBoxImageOutset", + "webkitMaskBoxImageRepeat", + "webkitMaskBoxImageSlice", + "webkitMaskBoxImageSource", + "webkitMaskBoxImageWidth", + "webkitMaskClip", + "webkitMaskComposite", + "webkitMaskImage", + "webkitMaskOrigin", + "webkitMaskPosition", + "webkitMaskPositionX", + "webkitMaskPositionY", + "webkitMaskRepeat", + "webkitMaskRepeatX", + "webkitMaskRepeatY", + "webkitMaskSize", + "webkitMaskSourceType", + "webkitMatchesSelector", + "webkitMaxLogicalHeight", + "webkitMaxLogicalWidth", + "webkitMediaStream", + "webkitMinLogicalHeight", + "webkitMinLogicalWidth", + "webkitNbspMode", + "webkitNotifications", + "webkitOfflineAudioContext", + "webkitOpacity", + "webkitOrder", + "webkitOrientation", + "webkitPaddingAfter", + "webkitPaddingBefore", + "webkitPaddingEnd", + "webkitPaddingStart", + "webkitPeerConnection00", + "webkitPersistentStorage", + "webkitPerspective", + "webkitPerspectiveOrigin", + "webkitPerspectiveOriginX", + "webkitPerspectiveOriginY", + "webkitPointerLockElement", + "webkitPostMessage", + "webkitPreservesPitch", + "webkitPrintColorAdjust", + "webkitPutImageDataHD", + "webkitRTCPeerConnection", + "webkitRegionOverset", + "webkitRelativePath", + "webkitRequestAnimationFrame", + "webkitRequestFileSystem", + "webkitRequestFullScreen", + "webkitRequestFullscreen", + "webkitRequestPointerLock", + "webkitResolveLocalFileSystemURL", + "webkitRtlOrdering", + "webkitRubyPosition", + "webkitSetMediaKeys", + "webkitSetResourceTimingBufferSize", + "webkitShadowRoot", + "webkitShapeImageThreshold", + "webkitShapeMargin", + "webkitShapeOutside", + "webkitShowPlaybackTargetPicker", + "webkitSlice", + "webkitSpeechGrammar", + "webkitSpeechGrammarList", + "webkitSpeechRecognition", + "webkitSpeechRecognitionError", + "webkitSpeechRecognitionEvent", + "webkitStorageInfo", + "webkitSupportsFullscreen", + "webkitSvgShadow", + "webkitTapHighlightColor", + "webkitTemporaryStorage", + "webkitTextCombine", + "webkitTextDecoration", + "webkitTextDecorationColor", + "webkitTextDecorationLine", + "webkitTextDecorationSkip", + "webkitTextDecorationStyle", + "webkitTextDecorationsInEffect", + "webkitTextEmphasis", + "webkitTextEmphasisColor", + "webkitTextEmphasisPosition", + "webkitTextEmphasisStyle", + "webkitTextFillColor", + "webkitTextOrientation", + "webkitTextSecurity", + "webkitTextSizeAdjust", + "webkitTextStroke", + "webkitTextStrokeColor", + "webkitTextStrokeWidth", + "webkitTextUnderlinePosition", + "webkitTextZoom", + "webkitTransform", + "webkitTransformOrigin", + "webkitTransformOriginX", + "webkitTransformOriginY", + "webkitTransformOriginZ", + "webkitTransformStyle", + "webkitTransition", + "webkitTransitionDelay", + "webkitTransitionDuration", + "webkitTransitionProperty", + "webkitTransitionTimingFunction", + "webkitURL", + "webkitUnlockOrientation", + "webkitUserDrag", + "webkitUserModify", + "webkitUserSelect", + "webkitVideoDecodedByteCount", + "webkitVisibilityState", + "webkitWirelessVideoPlaybackDisabled", + "webkitWritingMode", + "webkitdirectory", + "webkitdropzone", + "webstore", + "weekday", + "weight", + "whatToShow", + "wheelDelta", + "wheelDeltaX", + "wheelDeltaY", + "whenDefined", + "which", + "white-space", + "whiteSpace", + "wholeText", + "widows", + "width", + "will-change", + "willChange", + "willValidate", + "window", + "withCredentials", + "word-break", + "word-spacing", + "word-wrap", + "wordBreak", + "wordSpacing", + "wordWrap", + "workerStart", + "wrap", + "wrapKey", + "writable", + "writableAuxiliaries", + "write", + "writeText", + "writeValue", + "writeWithoutResponse", + "writeln", + "writing-mode", + "writingMode", + "x", + "x1", + "x2", + "xChannelSelector", + "xmlEncoding", + "xmlStandalone", + "xmlVersion", + "xmlbase", + "xmllang", + "xmlspace", + "xor", + "xr", + "y", + "y1", + "y2", + "yChannelSelector", + "yandex", + "year", + "z", + "z-index", + "zIndex", + "zoom", + "zoomAndPan", + "zoomRectScreen" +] diff --git a/tools/exports.js b/tools/exports.js new file mode 100644 index 00000000000..dbd21d72854 --- /dev/null +++ b/tools/exports.js @@ -0,0 +1,12 @@ +exports["Dictionary"] = Dictionary; +exports["is_statement"] = is_statement; +exports["List"] = List; +exports["minify"] = minify; +exports["parse"] = parse; +exports["push_uniq"] = push_uniq; +exports["TreeTransformer"] = TreeTransformer; +exports["TreeWalker"] = TreeWalker; +exports["extractFeatures"] = extractFeatures; +exports["Parse_Error"] = Parse_Error; +exports["Minified_Error"] = Minified_Error; +exports["OutputStream"] = OutputStream; diff --git a/tools/node.js b/tools/node.js index a5eb7fff4dc..f85d3ad5785 100644 --- a/tools/node.js +++ b/tools/node.js @@ -1,170 +1,74 @@ -var path = require("path"); var fs = require("fs"); -var vm = require("vm"); -var sys = require("util"); -var UglifyJS = vm.createContext({ - sys : sys, - console : console, - process : process, - Buffer : Buffer, - MOZ_SourceMap : require("source-map") -}); - -function load_global(file) { - file = path.resolve(path.dirname(module.filename), file); - try { - var code = fs.readFileSync(file, "utf8"); - return vm.runInContext(code, UglifyJS, file); - } catch(ex) { - // XXX: in case of a syntax error, the message is kinda - // useless. (no location information). - sys.debug("ERROR in file: " + file + " / " + ex); - process.exit(1); - } -}; - -var FILES = exports.FILES = [ - "../lib/utils.js", - "../lib/ast.js", - "../lib/parse.js", - "../lib/transform.js", - "../lib/scope.js", - "../lib/output.js", - "../lib/compress.js", - "../lib/sourcemap.js", - "../lib/mozilla-ast.js", - "../lib/feature_extractor.js" -].map(function(file){ - return fs.realpathSync(path.join(path.dirname(__filename), file)); -}); - -FILES.forEach(load_global); - -UglifyJS.AST_Node.warn_function = function(txt) { - sys.error("WARN: " + txt); -}; - -// XXX: perhaps we shouldn't export everything but heck, I'm lazy. -for (var i in UglifyJS) { - if (UglifyJS.hasOwnProperty(i)) { - exports[i] = UglifyJS[i]; - } -} - -exports.minify = function(files, options) { - options = UglifyJS.defaults(options, { - spidermonkey : false, - outSourceMap : null, - sourceRoot : null, - inSourceMap : null, - fromString : false, - warnings : false, - mangle : {}, - output : null, - compress : {} +exports.FILES = [ + require.resolve("../lib/utils.js"), + require.resolve("../lib/ast.js"), + require.resolve("../lib/transform.js"), + require.resolve("../lib/parse.js"), + require.resolve("../lib/scope.js"), + require.resolve("../lib/compress.js"), + require.resolve("../lib/output.js"), + require.resolve("../lib/sourcemap.js"), + require.resolve("../lib/mozilla-ast.js"), + require.resolve("../lib/feature_extractor.js"), + require.resolve("../lib/propmangle.js"), + require.resolve("../lib/minify.js"), + require.resolve("./exports.js"), +]; + +new Function("domprops", "exports", function() { + var code = exports.FILES.map(function(file) { + return fs.readFileSync(file, "utf8"); }); - UglifyJS.base54.reset(); - - // 1. parse - var toplevel = null, - sourcesContent = {}; - - if (options.spidermonkey) { - toplevel = UglifyJS.AST_Node.from_mozilla_ast(files); - } else { - if (typeof files == "string") - files = [ files ]; - files.forEach(function(file){ - var code = options.fromString - ? file - : fs.readFileSync(file, "utf8"); - sourcesContent[file] = code; - toplevel = UglifyJS.parse(code, { - filename: options.fromString ? "?" : file, - toplevel: toplevel - }); - }); - } - - // 2. compress - if (options.compress) { - var compress = { warnings: options.warnings }; - UglifyJS.merge(compress, options.compress); - toplevel.figure_out_scope(); - var sq = UglifyJS.Compressor(compress); - toplevel = toplevel.transform(sq); - } - - // 3. mangle - if (options.mangle) { - toplevel.figure_out_scope(options.mangle); - toplevel.compute_char_frequency(options.mangle); - toplevel.mangle_names(options.mangle); - } - - // 4. output - var inMap = options.inSourceMap; - var output = {}; - if (typeof options.inSourceMap == "string") { - inMap = fs.readFileSync(options.inSourceMap, "utf8"); - } - if (options.outSourceMap) { - output.source_map = UglifyJS.SourceMap({ - file: options.outSourceMap, - orig: inMap, - root: options.sourceRoot - }); - if (options.sourceMapIncludeSources) { - for (var file in sourcesContent) { - if (sourcesContent.hasOwnProperty(file)) { - output.source_map.get().setSourceContent(file, sourcesContent[file]); - } - } - } + code.push("exports.describe_ast = " + describe_ast.toString()); + return code.join("\n\n"); +}())(require("./domprops.json"), exports); + +function to_comment(value) { + if (typeof value != "string") value = JSON.stringify(value, function(key, value) { + return typeof value == "function" ? "<[ " + value + " ]>" : value; + }, 2); + return "// " + value.replace(/\n/g, "\n// "); +} +if (+process.env["UGLIFY_BUG_REPORT"]) exports.minify = function(files, options) { + if (typeof options == "undefined") options = "<>"; + var code = [ + "// UGLIFY_BUG_REPORT", + to_comment(options), + ]; + if (typeof files == "string") { + code.push(""); + code.push("//-------------------------------------------------------------") + code.push("// INPUT CODE", files); + } else for (var name in files) { + code.push(""); + code.push("//-------------------------------------------------------------") + code.push(to_comment(name), files[name]); } - if (options.output) { - UglifyJS.merge(output, options.output); - } - var stream = UglifyJS.OutputStream(output); - toplevel.print(stream); - - if(options.outSourceMap){ - stream += "\n//# sourceMappingURL=" + options.outSourceMap; + if (options.sourceMap && options.sourceMap.url) { + code.push(""); + code.push("//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IiJ9"); } - - return { - code : stream + "", - map : output.source_map + "" - }; + var result = { code: code.join("\n") }; + if (options.sourceMap) result.map = '{"version":3,"sources":[],"names":[],"mappings":""}'; + return result; }; -// exports.describe_ast = function() { -// function doitem(ctor) { -// var sub = {}; -// ctor.SUBCLASSES.forEach(function(ctor){ -// sub[ctor.TYPE] = doitem(ctor); -// }); -// var ret = {}; -// if (ctor.SELF_PROPS.length > 0) ret.props = ctor.SELF_PROPS; -// if (ctor.SUBCLASSES.length > 0) ret.sub = sub; -// return ret; -// } -// return doitem(UglifyJS.AST_Node).sub; -// } +function describe_ast() { + var out = OutputStream({ beautify: true }); + doitem(AST_Node); + return out.get() + "\n"; -exports.describe_ast = function() { - var out = UglifyJS.OutputStream({ beautify: true }); function doitem(ctor) { out.print("AST_" + ctor.TYPE); - var props = ctor.SELF_PROPS.filter(function(prop){ + var props = ctor.SELF_PROPS.filter(function(prop) { return !/^\$/.test(prop); }); if (props.length > 0) { out.space(); - out.with_parens(function(){ - props.forEach(function(prop, i){ + out.with_parens(function() { + props.forEach(function(prop, i) { if (i) out.space(); out.print(prop); }); @@ -176,15 +80,32 @@ exports.describe_ast = function() { } if (ctor.SUBCLASSES.length > 0) { out.space(); - out.with_block(function(){ - ctor.SUBCLASSES.forEach(function(ctor, i){ + out.with_block(function() { + ctor.SUBCLASSES.sort(function(a, b) { + return a.TYPE < b.TYPE ? -1 : 1; + }).forEach(function(ctor, i) { out.indent(); doitem(ctor); out.newline(); }); }); } - }; - doitem(UglifyJS.AST_Node); - return out + ""; + } +} + +function infer_options(options) { + var result = exports.minify("", options); + return result.error && result.error.defs; +} + +exports.default_options = function() { + var defs = infer_options({ 0: 0 }); + Object.keys(defs).forEach(function(component) { + var options = { module: false }; + options[component] = { 0: 0 }; + if (options = infer_options(options)) { + defs[component] = options; + } + }); + return defs; }; diff --git a/tools/tty.js b/tools/tty.js new file mode 100644 index 00000000000..d219581c8ca --- /dev/null +++ b/tools/tty.js @@ -0,0 +1,22 @@ +// workaround for tty output truncation on Node.js +try { + // prevent buffer overflow and other asynchronous bugs + process.stdout._handle.setBlocking(true); + process.stderr._handle.setBlocking(true); +} catch (e) { + // ensure output buffers are flushed before process termination + var exit = process.exit; + if ("bufferSize" in process.stdout) process.exit = function() { + var args = [].slice.call(arguments); + process.once("uncaughtException", function() { + (function callback() { + if (process.stdout.bufferSize || process.stderr.bufferSize) { + setTimeout(callback, 1); + } else { + exit.apply(process, args); + } + })(); + }); + throw exit; + }; +}