diff --git a/README.md b/README.md index 64a33c4e..931ae2e0 100644 --- a/README.md +++ b/README.md @@ -151,10 +151,10 @@ Additional options: - `--source-map "filename=''"` to specify the name of the source map. - `--source-map "root=''"` to pass the URL where the original files can be found. - Otherwise UglifyJS assumes HTTP `X-SourceMap` is being used and will omit the - `//# sourceMappingURL=` directive. - `--source-map "url=''"` to specify the URL where the source map can be found. + Otherwise UglifyJS assumes HTTP `X-SourceMap` is being used and will omit the + `//# sourceMappingURL=` directive. For example: @@ -201,11 +201,9 @@ Example: To enable the mangler you need to pass `--mangle` (`-m`). The following (comma-separated) options are supported: -- `toplevel` — mangle names declared in the top level scope (disabled by - default). +- `toplevel` (default `false`) -- mangle names declared in the top level scope. -- `eval` — mangle names visible in scopes where `eval` or `with` are used - (disabled by default). +- `eval` (default `false`) -- mangle names visible in scopes where `eval` or `with` are used. When mangling is enabled but you want to prevent certain names from being mangled, you can declare those names with `--mangle reserved` — pass a @@ -511,6 +509,9 @@ if (result.error) throw result.error; - `ie8` (default `false`) - set to `true` to support IE8. +- `keep_fnames` (default: `false`) - pass `true` to prevent discarding or mangling + of function names. Useful for code relying on `Function.prototype.name`. + ## Minify options structure ```javascript @@ -592,132 +593,93 @@ If you're using the `X-SourceMap` header instead, you can just omit `sourceMap.u ## Parse options - `bare_returns` (default `false`) -- support top level `return` statements + - `ecma` (default: `8`) -- specify one of `5`, `6`, `7` or `8`. Note: this setting is not presently enforced except for ES8 optional trailing commas in function parameter lists and calls with `ecma` `8`. + - `html5_comments` (default `true`) + - `shebang` (default `true`) -- support `#!command` as the first line ## Compress options -- `sequences` (default: true) -- join consecutive simple statements using the - comma operator. May be set to a positive integer to specify the maximum number - of consecutive comma sequences that will be generated. If this option is set to - `true` then the default `sequences` limit is `200`. Set option to `false` or `0` - to disable. The smallest `sequences` length is `2`. A `sequences` value of `1` - is grandfathered to be equivalent to `true` and as such means `200`. On rare - occasions the default sequences limit leads to very slow compress times in which - case a value of `20` or less is recommended. - -- `properties` -- rewrite property access using the dot notation, for - example `foo["bar"] → foo.bar` - -- `dead_code` -- remove unreachable code - -- `drop_debugger` -- remove `debugger;` statements - -- `unsafe` (default: false) -- apply "unsafe" transformations (discussion below) - -- `unsafe_comps` (default: false) -- Reverse `<` and `<=` to `>` and `>=` to - allow improved compression. This might be unsafe when an at least one of two - operands is an object with computed values due the use of methods like `get`, - or `valueOf`. This could cause change in execution order after operands in the - comparison are switching. Compression only works if both `comparisons` and - `unsafe_comps` are both set to true. - -- `unsafe_Func` (default: false) -- compress and mangle `Function(args, code)` - when both `args` and `code` are string literals. - -- `unsafe_math` (default: false) -- optimize numerical expressions like - `2 * x * 3` into `6 * x`, which may give imprecise floating point results. - -- `unsafe_methods` (default: false) -- Converts `{ m: function(){} }` to - `{ m(){} }`. `ecma` must be set to `6` or greater to enable this transform. - If `unsafe_methods` is a RegExp then key/value pairs with keys matching the - RegExp will be converted to concise methods. - Note: if enabled there is a risk of getting a "`` is not a - constructor" TypeError should any code try to `new` the former function. - -- `unsafe_proto` (default: false) -- optimize expressions like - `Array.prototype.slice.call(a)` into `[].slice.call(a)` - -- `unsafe_regexp` (default: false) -- enable substitutions of variables with - `RegExp` values the same way as if they are constants. - -- `conditionals` -- apply optimizations for `if`-s and conditional - expressions - -- `comparisons` -- apply certain optimizations to binary nodes, for example: - `!(a <= b) → a > b` (only when `unsafe_comps`), attempts to negate binary - nodes, e.g. `a = !b && !c && !d && !e → a=!(b||c||d||e)` etc. - -- `evaluate` -- attempt to evaluate constant expressions - - `arrows` (default `true`) -- Converts `()=>{return x}` to `()=>x`. Class and object literal methods will also be converted to arrow expressions if the resultant code is shorter: `m(){return x}` becomes `m:()=>x`. This transform requires that the `ecma` compress option is set to `6` or greater. -- `unsafe_arrows` (default `false`) -- Convert ES5 style anonymous function - expressions to arrow functions if the function body does not reference `this`. - Note: it is not always safe to perform this conversion if code relies on the - the function having a `prototype`, which arrow functions lack. - This transform requires that the `ecma` compress option is set to `6` or greater. - -- `booleans` -- various optimizations for boolean context, for example `!!a +- `booleans` (default: `true`) -- various optimizations for boolean context, for example `!!a ? b : c → a ? b : c` -- `typeofs` -- default `true`. Transforms `typeof foo == "undefined"` into - `foo === void 0`. Note: recommend to set this value to `false` for IE10 and - earlier versions due to known issues. - -- `loops` -- optimizations for `do`, `while` and `for` loops when we can - statically determine the condition - -- `unused` -- drop unreferenced functions and variables (simple direct variable - assignments do not count as references unless set to `"keep_assign"`) - -- `toplevel` -- drop unreferenced functions (`"funcs"`) and/or variables (`"vars"`) - in the top level scope (`false` by default, `true` to drop both unreferenced - functions and variables) - -- `top_retain` -- prevent specific toplevel functions and variables from `unused` - removal (can be array, comma-separated, RegExp or function. Implies `toplevel`) - -- `hoist_funs` -- hoist function declarations - -- `hoist_vars` (default: false) -- hoist `var` declarations (this is `false` - by default because it seems to increase the size of the output in general) - -- `if_return` -- optimizations for if/return and if/continue - -- `inline` -- embed simple functions - -- `join_vars` -- join consecutive `var` statements - -- `cascade` -- small optimization for sequences, transform `x, x` into `x` +- `cascade` (default: `true`) -- small optimization for sequences, transform `x, x` into `x` and `x = something(), x` into `x = something()` -- `collapse_vars` -- Collapse single-use non-constant variables - side +- `collapse_vars` (default: `true`) -- Collapse single-use non-constant variables - side effects permitting. -- `reduce_vars` -- Improve optimization on variables assigned with and - used as constant values. +- `comparisons` (default: `true`) -- apply certain optimizations to binary nodes, for example: + `!(a <= b) → a > b` (only when `unsafe_comps`), attempts to negate binary + nodes, e.g. `a = !b && !c && !d && !e → a=!(b||c||d||e)` etc. -- `warnings` -- display warnings when dropping unreachable code or unused - declarations etc. +- `computed_props` -- default `true`. Transforms constant computed properties + into regular ones: `{["computed"]: 1}` is converted into `{computed: 1}`. -- `negate_iife` -- negate "Immediately-Called Function Expressions" +- `conditionals` (default: `true`) -- apply optimizations for `if`-s and conditional + expressions + +- `dead_code` (default: `true`) -- remove unreachable code + +- `drop_console` (default: `false`) -- default `false`. Pass `true` to discard calls to + `console.*` functions. If you wish to drop a specific function call + such as `console.info` and/or retain side effects from function arguments + after dropping the function call then use `pure_funcs` instead. + +- `drop_debugger` (default: `true`) -- remove `debugger;` statements + +- `ecma` -- default `5`. Pass `6` or greater to enable `compress` options that + will transform ES5 code into smaller ES6+ equivalent forms. + +- `evaluate` (default: `true`) -- attempt to evaluate constant expressions + +- `expression` (default: `false`) -- default `false`. Pass `true` to preserve completion values + from terminal statements without `return`, e.g. in bookmarklets. + +- `global_defs` (default: `{}`) -- see [conditional compilation](#conditional-compilation) + +- `hoist_funs` (default: `true`) -- hoist function declarations + +- `hoist_vars` (default: `false`) -- hoist `var` declarations (this is `false` + by default because it seems to increase the size of the output in general) + +- `if_return` (default: `true`) -- optimizations for if/return and if/continue + +- `inline` (default: `true`) -- embed simple functions + +- `join_vars` (default: `true`) -- join consecutive `var` statements + +- `keep_fargs` (default: `true`) -- default `true`. Prevents the + compressor from discarding unused function arguments. You need this + for code which relies on `Function.length`. + +- `keep_infinity` (default: `false`) -- default `false`. Pass `true` to prevent `Infinity` from + being compressed into `1/0`, which may cause performance issues on Chrome. + +- `loops` (default: `true`) -- optimizations for `do`, `while` and `for` loops when we can + statically determine the condition + +- `negate_iife` (default: `true`) -- negate "Immediately-Called Function Expressions" where the return value is discarded, to avoid the parens that the code generator would insert. -- `pure_getters` -- the default is `false`. If you pass `true` for - this, UglifyJS will assume that object property access - (e.g. `foo.bar` or `foo["bar"]`) doesn't have any side effects. - Specify `"strict"` to treat `foo.bar` as side-effect-free only when - `foo` is certain to not throw, i.e. not `null` or `undefined`. +- `passes` (default: `1`) -- The maximum number of times to run compress. + In some cases more than one pass leads to further compressed code. Keep in + mind more passes will take more time. -- `pure_funcs` -- default `null`. You can pass an array of names and +- `properties` (default: `true`) -- rewrite property access using the dot notation, for + example `foo["bar"] → foo.bar` + +- `pure_funcs` (default: `null`) -- You can pass an array of names and UglifyJS will assume that those functions do not produce side effects. DANGER: will not check if the name is redefined in scope. An example case here, for instance `var q = Math.floor(a/b)`. If @@ -728,54 +690,85 @@ If you're using the `X-SourceMap` header instead, you can just omit `sourceMap.u statement would get discarded. The current implementation adds some overhead (compression will be slower). -- `drop_console` -- default `false`. Pass `true` to discard calls to - `console.*` functions. If you wish to drop a specific function call - such as `console.info` and/or retain side effects from function arguments - after dropping the function call then use `pure_funcs` instead. +- `pure_getters` (default: `"strict"`) -- If you pass `true` for + this, UglifyJS will assume that object property access + (e.g. `foo.bar` or `foo["bar"]`) doesn't have any side effects. + Specify `"strict"` to treat `foo.bar` as side-effect-free only when + `foo` is certain to not throw, i.e. not `null` or `undefined`. -- `expression` -- default `false`. Pass `true` to preserve completion values - from terminal statements without `return`, e.g. in bookmarklets. +- `reduce_vars` (default: `true`) -- Improve optimization on variables assigned with and + used as constant values. -- `keep_fargs` -- default `true`. Prevents the - compressor from discarding unused function arguments. You need this - for code which relies on `Function.length`. +- `sequences` (default: `true`) -- join consecutive simple statements using the + comma operator. May be set to a positive integer to specify the maximum number + of consecutive comma sequences that will be generated. If this option is set to + `true` then the default `sequences` limit is `200`. Set option to `false` or `0` + to disable. The smallest `sequences` length is `2`. A `sequences` value of `1` + is grandfathered to be equivalent to `true` and as such means `200`. On rare + occasions the default sequences limit leads to very slow compress times in which + case a value of `20` or less is recommended. -- `keep_fnames` -- default `false`. Pass `true` to prevent the - compressor from discarding function names. Useful for code relying on - `Function.prototype.name`. See also: the `keep_fnames` [mangle option](#mangle). - -- `passes` -- default `1`. The maximum number of times to run compress. - In some cases more than one pass leads to further compressed code. Keep in - mind more passes will take more time. - -- `keep_infinity` -- default `false`. Pass `true` to prevent `Infinity` from - being compressed into `1/0`, which may cause performance issues on Chrome. - -- `side_effects` -- default `true`. Pass `false` to disable potentially dropping +- `side_effects` (default: `true`) -- default `true`. Pass `false` to disable potentially dropping functions marked as "pure". A function call is marked as "pure" if a comment annotation `/*@__PURE__*/` or `/*#__PURE__*/` immediately precedes the call. For example: `/*@__PURE__*/foo();` -- `ecma` -- default `5`. Pass `6` or greater to enable `compress` options that - will transform ES5 code into smaller ES6+ equivalent forms. +- `switches` (default: `true`) -- de-duplicate and remove unreachable `switch` branches -- `computed_props` -- default `true`. Transforms constant computed properties - into regular ones: `{["computed"]: 1}` is converted into `{computed: 1}`. +- `toplevel` (default: `false`) -- drop unreferenced functions (`"funcs"`) and/or variables (`"vars"`) + in the top level scope (`false` by default, `true` to drop both unreferenced + functions and variables) + +- `top_retain` (default: `null`) -- prevent specific toplevel functions and variables from `unused` + removal (can be array, comma-separated, RegExp or function. Implies `toplevel`) + +- `typeofs` (default: `true`) -- default `true`. Transforms `typeof foo == "undefined"` into + `foo === void 0`. Note: recommend to set this value to `false` for IE10 and + earlier versions due to known issues. + +- `unsafe_arrows` (default `false`) -- Convert ES5 style anonymous function + expressions to arrow functions if the function body does not reference `this`. + Note: it is not always safe to perform this conversion if code relies on the + the function having a `prototype`, which arrow functions lack. + This transform requires that the `ecma` compress option is set to `6` or greater. + +- `unsafe_comps` (default: `false`) -- Reverse `<` and `<=` to `>` and `>=` to + allow improved compression. This might be unsafe when an at least one of two + operands is an object with computed values due the use of methods like `get`, + or `valueOf`. This could cause change in execution order after operands in the + comparison are switching. Compression only works if both `comparisons` and + `unsafe_comps` are both set to true. + +- `unsafe` (default: `false`) -- apply "unsafe" transformations (discussion below) + +- `unsafe_Func` (default: `false`) -- compress and mangle `Function(args, code)` + when both `args` and `code` are string literals. + +- `unsafe_math` (default: `false`) -- optimize numerical expressions like + `2 * x * 3` into `6 * x`, which may give imprecise floating point results. + +- `unsafe_proto` (default: `false`) -- optimize expressions like + `Array.prototype.slice.call(a)` into `[].slice.call(a)` + +- `unsafe_regexp` (default: `false`) -- enable substitutions of variables with + `RegExp` values the same way as if they are constants. + +- `unused` (default: `true`) -- drop unreferenced functions and variables (simple direct variable + assignments do not count as references unless set to `"keep_assign"`) + +- `warnings` (default: `false`) -- display warnings when dropping unreachable code or unused + declarations etc. ## Mangle options +- `keep_classnames` (default `false`). Pass `true` to not mangle class names. + - `reserved` (default `[]`). Pass an array of identifiers that should be excluded from mangling. Example: `["foo", "bar"]`. - `toplevel` (default `false`). Pass `true` to mangle names declared in the top level scope. -- `keep_classnames` (default `false`). Pass `true` to not mangle class names. - -- `keep_fnames` (default `false`). Pass `true` to not mangle function names. - Useful for code relying on `Function.prototype.name`. See also: the `keep_fnames` - [compress option](#compress-options). - - `eval` (default `false`). Pass `true` to mangle names visible in scopes where `eval` or `with` are used. @@ -807,16 +800,20 @@ UglifyJS.minify(code, { mangle: { toplevel: true } }).code; ### Mangle properties options -- `reserved` (default: `[]`) -- Do not mangle property names listed in the - `reserved` array. -- `regex` (default: `null`) -— Pass a RegExp literal to only mangle property - names matching the regular expression. -- `keep_quoted` (default: `false`) -— Only mangle unquoted property names. -- `debug` (default: `false`) -— Mangle names with the original name still present. - Pass an empty string `""` to enable, or a non-empty string to set the debug suffix. - `builtins` (default: `false`) -- Use `true` to allow the mangling of builtin DOM properties. Not recommended to override this setting. +- `debug` (default: `false`) -— Mangle names with the original name still present. + Pass an empty string `""` to enable, or a non-empty string to set the debug suffix. + +- `keep_quoted` (default: `false`) -— Only mangle unquoted property names. + +- `regex` (default: `null`) -— Pass a RegExp literal to only mangle property + names matching the regular expression. + +- `reserved` (default: `[]`) -- Do not mangle property names listed in the + `reserved` array. + ## Output options The code generator tries to output shortest code possible by default. In @@ -825,37 +822,50 @@ can pass additional arguments that control the code output: - `ascii_only` (default `false`) -- escape Unicode characters in strings and regexps (affects directives with non-ascii characters becoming invalid) + - `beautify` (default `true`) -- whether to actually beautify the output. Passing `-b` will set this to true, but you might need to pass `-b` even when you want to generate minified code, in order to specify additional arguments, so you can use `-b beautify=false` to override it. + - `bracketize` (default `false`) -- always insert brackets in `if`, `for`, `do`, `while` or `with` statements, even if their body is a single statement. + - `comments` (default `false`) -- pass `true` or `"all"` to preserve all comments, `"some"` to preserve some comments, a regular expression string (e.g. `/^!/`) or a function. + - `ecma` (default `5`) -- set output printing mode. Set `ecma` to `6` or greater to emit shorthand object properties - i.e.: `{a}` instead of `{a: a}`. The `ecma` option will only change the output in direct control of the beautifier. Non-compatible features in the abstract syntax tree will still be output as is. For example: an `ecma` setting of `5` will **not** convert ES6+ code to ES5. -- `indent_level` (default 4) -- `indent_start` (default 0) -- prefix all lines by that many spaces + +- `indent_level` (default `4`) + +- `indent_start` (default `0`) -- prefix all lines by that many spaces + - `inline_script` (default `false`) -- escape the slash in occurrences of `= 0 + && self.variables.get(def.name) !== def) { + result = false; + return true; + } + } + })); + return result; + }); def(AST_Unary, function(){ return this.expression.is_constant_expression(); }); @@ -4306,12 +4348,13 @@ merge(Compressor.prototype, { d.fixed = fixed = make_node(AST_Function, fixed, fixed); } if (compressor.option("unused") - && is_func_expr(fixed) && d.references.length == 1 - && !(d.scope.uses_arguments && d.orig[0] instanceof AST_SymbolFunarg) - && !d.scope.uses_eval - && compressor.find_parent(AST_Scope) === fixed.parent_scope) { - return fixed.clone(true); + && (d.single_use || is_func_expr(fixed) + && !(d.scope.uses_arguments && d.orig[0] instanceof AST_SymbolFunarg) + && !d.scope.uses_eval + && compressor.find_parent(AST_Scope) === fixed.parent_scope)) { + var value = fixed.optimize(compressor); + return value === fixed ? fixed.clone(true) : value; } if (compressor.option("evaluate") && fixed) { if (d.should_replace === undefined) { diff --git a/package.json b/package.json index bd69d53e..b9eb72ad 100644 --- a/package.json +++ b/package.json @@ -4,7 +4,7 @@ "homepage": "https://github.com/mishoo/UglifyJS2/tree/harmony", "author": "Mihai Bazon (http://lisperator.net/)", "license": "BSD-2-Clause", - "version": "3.1.3", + "version": "3.1.4", "engines": { "node": ">=0.8.0" }, @@ -27,7 +27,7 @@ ], "dependencies": { "commander": "~2.11.0", - "source-map": "~0.5.1" + "source-map": "~0.6.1" }, "devDependencies": { "acorn": "~5.1.1", diff --git a/test/compress/collapse_vars.js b/test/compress/collapse_vars.js index 6639d9ff..f11579d6 100644 --- a/test/compress/collapse_vars.js +++ b/test/compress/collapse_vars.js @@ -1268,22 +1268,21 @@ collapse_vars_short_circuited_conditions: { collapse_vars_regexp: { options = { - collapse_vars: true, - loops: false, - sequences: true, - dead_code: true, - conditionals: true, - comparisons: true, - evaluate: true, booleans: true, - unused: true, - hoist_funs: true, - keep_fargs: true, + cascade: true, + collapse_vars: true, + comparisons: true, + conditionals: true, + dead_code: true, + evaluate: true, if_return: true, join_vars: true, - cascade: true, - side_effects: true, + hoist_funs: true, + keep_fargs: true, + loops: false, reduce_vars: true, + side_effects: true, + unused: true, } input: { function f1() { @@ -1292,12 +1291,12 @@ collapse_vars_regexp: { return [rx, k]; } function f2() { - var rx = /[abc123]+/; + var rx = /ab*/g; return function(s) { return rx.exec(s); }; } - (function(){ + (function() { var result; var s = 'acdabcdeabbb'; var rx = /ab*/g; @@ -1305,22 +1304,35 @@ collapse_vars_regexp: { console.log(result[0]); } })(); + (function() { + var result; + var s = 'acdabcdeabbb'; + var rx = f2(); + while (result = rx(s)) { + console.log(result[0]); + } + })(); } expect: { function f1() { return [/[A-Z]+/, 9]; } function f2() { - var rx = /[abc123]+/; + var rx = /ab*/g; return function(s) { return rx.exec(s); }; } - (function(){ + (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]); + })(); } expect_stdout: true } @@ -1933,9 +1945,9 @@ issue_1858: { } expect: { console.log(function(x) { - var a = {}, b = a.b = x; + var a = {}, b = a.b = 1; return a.b + b; - }(1)); + }()); } expect_stdout: "2" } @@ -2795,3 +2807,70 @@ issue_2319_3: { } expect_stdout: "true" } + +prop_side_effects_1: { + options = { + collapse_vars: true, + evaluate: true, + pure_getters: "strict", + reduce_vars: true, + toplevel: true, + unsafe: 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); + console.log({ + bar: function() { + return 2; + } + }.bar()); + } + expect_stdout: [ + "1", + "2", + ] +} + +prop_side_effects_2: { + options = { + collapse_vars: true, + evaluate: true, + inline: true, + passes: 2, + pure_getters: "strict", + reduce_vars: true, + side_effects: true, + toplevel: true, + unsafe: 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); + console.log(2); + } + expect_stdout: [ + "1", + "2", + ] +} diff --git a/test/compress/drop-unused.js b/test/compress/drop-unused.js index 763250fc..f849d69d 100644 --- a/test/compress/drop-unused.js +++ b/test/compress/drop-unused.js @@ -863,12 +863,12 @@ issue_1583: { expect: { function m(t) { (function(e) { - t = e(); - })(function() { - return (function(a) { - return a; - })(function(a) {}); - }); + t = function() { + return (function(a) { + return function(a) {}; + })(); + }(); + })(); } } } diff --git a/test/compress/evaluate.js b/test/compress/evaluate.js index f015c601..29027768 100644 --- a/test/compress/evaluate.js +++ b/test/compress/evaluate.js @@ -1114,6 +1114,7 @@ issue_1964_1: { 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()); @@ -1121,11 +1122,15 @@ issue_1964_1: { 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: "b" + expect_stdout: [ + "\\s", + "b", + ] } issue_1964_2: { @@ -1138,17 +1143,22 @@ issue_1964_2: { 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: "b" + expect_stdout: [ + "\\s", + "b", + ] } array_slice_index: { diff --git a/test/compress/reduce_vars.js b/test/compress/reduce_vars.js index 994c0aee..0c758c0b 100644 --- a/test/compress/reduce_vars.js +++ b/test/compress/reduce_vars.js @@ -172,6 +172,7 @@ unsafe_evaluate: { options = { evaluate : true, reduce_vars : true, + side_effects : true, unsafe : true, unused : true } @@ -1898,10 +1899,7 @@ redefine_farg_3: { console.log(f([]), g([]), h([])); } expect: { - console.log(function(a) { - var a; - return typeof a; - }([]), "number", "undefined"); + console.log(typeof [], "number", "undefined"); } expect_stdout: "object number undefined" } @@ -2735,3 +2733,321 @@ for_in_prop: { } expect_stdout: "1" } + +obj_var_1: { + options = { + evaluate: true, + passes: 2, + 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, + 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 = { + evaluate: true, + inline: true, + passes: 2, + 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 = { + evaluate: true, + inline: true, + passes: 2, + reduce_vars: true, + side_effects: true, + toplevel: true, + unsafe: 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_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_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_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_vars: true, + toplevel: true, + unused: true, + } + input: { + var o = { a: 1 }; + for (var i = o.a--; i; i--) + console.log(i); + } + expect: { + // TODO make this work on harmony + var o = { a: 1 }; + for (var i = o.a--; i; i--) + console.log(i); + } + expect_stdout: "1" +} + +obj_for_2: { + options = { + 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_vars: true, + toplevel: true, + unused: true, + } + input: { + var a = [ 1, 2, 3 ]; + for (var b in a) + console.log(b); + } + expect: { + // TODO make this work on harmony + var a = [ 1, 2, 3 ]; + for (var b in a) + console.log(b); + } + expect_stdout: [ + "0", + "1", + "2", + ] +} + +array_forin_2: { + options = { + 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" +} + +array_forof_1: { + options = { + reduce_vars: true, + toplevel: true, + unused: true, + } + input: { + var a = [ 1, 2, 3 ]; + for (var b of a) + console.log(b); + } + expect: { + // TODO make this work on harmony + var a = [ 1, 2, 3 ]; + for (var b of a) + console.log(b); + } + expect_stdout: [ + "1", + "2", + "3", + ] + node_version: ">=0.12" +} + +array_forof_2: { + options = { + reduce_vars: true, + toplevel: true, + unused: true, + } + input: { + var a = []; + for (var b of [ 1, 2, 3 ]) + a.push(b); + console.log(a.length); + } + expect: { + var a = []; + for (var b of [ 1, 2, 3 ]) + a.push(b); + console.log(a.length); + } + expect_stdout: "3" + node_version: ">=0.12" +}