Compare commits

..

14 Commits

Author SHA1 Message Date
Alex Lam S.L
dfe4f6c6de v3.1.4 2017-10-16 02:44:17 +08:00
Alex Lam S.L
a09c8ad666 update dependency (#2362)
- source-map@0.6.1
2017-10-16 02:41:22 +08:00
Alex Lam S.L
ec598c351b fix-ups for #2356 (#2360) 2017-10-15 22:33:55 +08:00
Alex Lam S.L
eba0f93bc0 more tests for #2351 (#2357) 2017-10-12 02:58:25 +08:00
Roger Peppe
99800d4aa9 update README to include defaults (#2356)
fixes #2353
2017-10-12 02:56:02 +08:00
Tim Malone
70d56c951a Update README.md - sourceMappingURL directive note (#2355)
Moves this README note to underneath the 'url' rather than 'root' option.
2017-10-11 19:48:43 +08:00
Alex Lam S.L
b810e2f8da perform reduce_vars on safe literals (#2351)
- constant expression
- single reference
- same scope
- not across loop body
2017-10-09 12:25:06 +08:00
Alex Lam S.L
1abe14296e collapse a.b whenever safe (#2350) 2017-10-08 13:17:48 +08:00
Alex Lam S.L
6920e898d1 v3.1.3 2017-10-01 12:36:07 +08:00
Alex Lam S.L
dd71639264 enhance reduce_vars for AST_Accessor (#2339)
fixes #2336
2017-10-01 03:01:50 +08:00
Alex Lam S.L
2dcc552ce0 trap invalid use of reserved words (#2338)
fixes #2337
2017-10-01 02:10:41 +08:00
Alex Lam S.L
55387e8fd0 v3.1.2 2017-09-24 02:02:04 +08:00
kzc
7e3e9da860 fix "use asm" numeric output (#2328)
fixes #2324
2017-09-21 04:42:40 +08:00
Alex Lam S.L
00f509405b suppress collapse_vars of this into "use strict" (#2326)
fixes #2319
2017-09-20 05:23:20 +08:00
12 changed files with 761 additions and 171 deletions

249
README.md
View File

@@ -153,10 +153,10 @@ Additional options:
- `--source-map "filename='<NAME>'"` to specify the name of the source map.
- `--source-map "root='<URL>'"` 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='<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:
@@ -203,11 +203,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
@@ -510,6 +508,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
@@ -590,111 +591,78 @@ 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
- `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.
- `booleans` (default: `true`) -- various optimizations for boolean context, for example `!!a
? b : c → a ? b : c`
- `properties` -- rewrite property access using the dot notation, for
example `foo["bar"] → foo.bar`
- `cascade` (default: `true`) -- small optimization for sequences, transform `x, x` into `x`
and `x = something(), x` into `x = something()`
- `dead_code` -- remove unreachable code
- `collapse_vars` (default: `true`) -- Collapse single-use non-constant variables - side
effects permitting.
- `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_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:
- `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.
- `evaluate` -- attempt to evaluate constant expressions
- `conditionals` (default: `true`) -- apply optimizations for `if`-s and conditional
expressions
- `booleans` -- various optimizations for boolean context, for example `!!a
? b : c → a ? b : c`
- `dead_code` (default: `true`) -- remove unreachable code
- `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.
- `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.
- `loops` -- optimizations for `do`, `while` and `for` loops when we can
statically determine the condition
- `drop_debugger` (default: `true`) -- remove `debugger;` statements
- `unused` -- drop unreferenced functions and variables (simple direct variable
assignments do not count as references unless set to `"keep_assign"`)
- `evaluate` (default: `true`) -- attempt to evaluate constant expressions
- `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)
- `expression` (default: `false`) -- default `false`. Pass `true` to preserve completion values
from terminal statements without `return`, e.g. in bookmarklets.
- `top_retain` -- prevent specific toplevel functions and variables from `unused`
removal (can be array, comma-separated, RegExp or function. Implies `toplevel`)
- `global_defs` (default: `{}`) -- see [conditional compilation](#conditional-compilation)
- `hoist_funs` -- hoist function declarations
- `hoist_funs` (default: `true`) -- hoist function declarations
- `hoist_vars` (default: false) -- hoist `var` declarations (this is `false`
- `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
- `if_return` (default: `true`) -- optimizations for if/return and if/continue
- `inline` -- embed simple functions
- `inline` (default: `true`) -- embed simple functions
- `join_vars` -- join consecutive `var` statements
- `join_vars` (default: `true`) -- join consecutive `var` statements
- `cascade` -- small optimization for sequences, transform `x, x` into `x`
and `x = something(), x` into `x = something()`
- `keep_fargs` (default: `true`) -- default `true`. Prevents the
compressor from discarding unused function arguments. You need this
for code which relies on `Function.length`.
- `collapse_vars` -- Collapse single-use non-constant variables - side
effects permitting.
- `keep_infinity` (default: `false`) -- default `false`. Pass `true` to prevent `Infinity` from
being compressed into `1/0`, which may cause performance issues on Chrome.
- `reduce_vars` -- Improve optimization on variables assigned with and
used as constant values.
- `loops` (default: `true`) -- optimizations for `do`, `while` and `for` loops when we can
statically determine the condition
- `warnings` -- display warnings when dropping unreachable code or unused
declarations etc.
- `negate_iife` -- negate "Immediately-Called Function Expressions"
- `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
@@ -705,34 +673,69 @@ 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();`
- `switches` (default: `true`) -- de-duplicate and remove unreachable `switch` branches
- `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_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
- `reserved` (default `[]`). Pass an array of identifiers that should be
@@ -741,10 +744,6 @@ If you're using the `X-SourceMap` header instead, you can just omit `sourceMap.u
- `toplevel` (default `false`). Pass `true` to mangle names declared in the
top level scope.
- `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.
@@ -772,16 +771,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
@@ -790,31 +793,43 @@ 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.
- `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
`</script` in strings
- `keep_quoted_props` (default `false`) -- when turned on, prevents stripping
quotes from property names in object literals.
- `max_line_len` (default `false`) -- maximum line length (for uglified code)
- `preamble` (default `null`) -- when passed it must be a string and
it will be prepended to the output literally. The source map will
adjust for this text. Can be used to insert a comment containing
licensing information, for example.
- `preserve_line` (default `false`) -- pass `true` to preserve lines, but it
only works if `beautify` is set to `false`.
- `quote_keys` (default `false`) -- pass `true` to quote all keys in literal
objects
- `quote_style` (default `0`) -- preferred quote style for strings (affects
quoted property names and directives as well):
- `0` -- prefers double quotes, switches to single quotes when there are
@@ -822,16 +837,20 @@ can pass additional arguments that control the code output:
- `1` -- always use single quotes
- `2` -- always use double quotes
- `3` -- always use the original quotes
- `semicolons` (default `true`) -- separate statements with semicolons. If
you pass `false` then whenever possible we will use a newline instead of a
semicolon, leading to more readable output of uglified code (size before
gzip could be smaller; size after gzip insignificantly larger).
- `shebang` (default `true`) -- preserve shebang `#!` in preamble (bash scripts)
- `width` (default 80) -- only takes effect when beautification is on, this
- `width` (default `80`) -- only takes effect when beautification is on, this
specifies an (orientative) line width that the beautifier will try to
obey. It refers to the width of the line text (excluding indentation).
It doesn't work very well currently, but it does make the code generated
by UglifyJS more readable.
- `wrap_iife` (default `false`) -- pass `true` to wrap immediately invoked
function expressions. See
[#640](https://github.com/mishoo/UglifyJS2/issues/640) for more details.

View File

@@ -134,11 +134,10 @@ var AST_Debugger = DEFNODE("Debugger", null, {
$documentation: "Represents a debugger statement",
}, AST_Statement);
var AST_Directive = DEFNODE("Directive", "value scope quote", {
var AST_Directive = DEFNODE("Directive", "value quote", {
$documentation: "Represents a directive, like \"use strict\";",
$propdoc: {
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",
quote: "[string] the original quote character"
},
}, AST_Statement);
@@ -299,10 +298,9 @@ var AST_With = DEFNODE("With", "expression", {
/* -----[ scope and functions ]----- */
var AST_Scope = DEFNODE("Scope", "directives variables functions uses_with uses_eval parent_scope enclosed cname", {
var AST_Scope = DEFNODE("Scope", "variables functions uses_with uses_eval parent_scope enclosed cname", {
$documentation: "Base class for all statements introducing a lexical 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",

View File

@@ -285,6 +285,10 @@ merge(Compressor.prototype, {
AST_Node.DEFMETHOD("reset_opt_flags", function(compressor, rescan) {
var reduce_vars = rescan && compressor.option("reduce_vars");
// 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
var safe_ids = Object.create(null);
var suppressor = new TreeWalker(function(node) {
if (!(node instanceof AST_Symbol)) return;
@@ -293,6 +297,8 @@ merge(Compressor.prototype, {
if (node instanceof AST_SymbolRef) d.references.push(node);
d.fixed = false;
});
var in_loop = null;
var loop_ids = Object.create(null);
var tw = new TreeWalker(function(node, descend) {
node._squeezed = false;
node._optimized = false;
@@ -303,7 +309,7 @@ merge(Compressor.prototype, {
var d = node.definition();
d.references.push(node);
if (d.fixed === undefined || !safe_to_read(d)
|| is_modified(node, 0, is_immutable(node.fixed_value()))) {
|| is_modified(node, 0, is_immutable(node))) {
d.fixed = false;
} else {
var parent = tw.parent();
@@ -325,6 +331,7 @@ merge(Compressor.prototype, {
d.fixed = function() {
return node.value;
};
loop_ids[d.id] = in_loop;
mark(d, false);
descend();
} else {
@@ -380,6 +387,7 @@ merge(Compressor.prototype, {
d.fixed = function() {
return iife.args[i] || make_node(AST_Undefined, iife);
};
loop_ids[d.id] = in_loop;
mark(d, true);
} else {
d.fixed = false;
@@ -391,10 +399,9 @@ merge(Compressor.prototype, {
return true;
}
if (node instanceof AST_Accessor) {
var save_ids = safe_ids;
safe_ids = Object.create(null);
push();
descend();
safe_ids = save_ids;
pop();
return true;
}
if (node instanceof AST_Binary
@@ -428,10 +435,13 @@ merge(Compressor.prototype, {
return true;
}
if (node instanceof AST_DWLoop) {
var saved_loop = in_loop;
in_loop = node;
push();
node.condition.walk(tw);
node.body.walk(tw);
pop();
in_loop = saved_loop;
return true;
}
if (node instanceof AST_LabeledStatement) {
@@ -442,6 +452,8 @@ merge(Compressor.prototype, {
}
if (node instanceof AST_For) {
if (node.init) node.init.walk(tw);
var saved_loop = in_loop;
in_loop = node;
if (node.condition) {
push();
node.condition.walk(tw);
@@ -455,14 +467,18 @@ merge(Compressor.prototype, {
node.step.walk(tw);
pop();
}
in_loop = saved_loop;
return true;
}
if (node instanceof AST_ForIn) {
node.init.walk(suppressor);
node.object.walk(tw);
var saved_loop = in_loop;
in_loop = node;
push();
node.body.walk(tw);
pop();
in_loop = saved_loop;
return true;
}
if (node instanceof AST_Try) {
@@ -532,10 +548,23 @@ merge(Compressor.prototype, {
}
def.references = [];
def.should_replace = undefined;
def.single_use = undefined;
}
function is_immutable(value) {
return value && value.is_constant() || value instanceof AST_Lambda;
function is_immutable(node) {
var value = node.fixed_value();
if (!value) return false;
if (value.is_constant()) return true;
if (compressor.option("unused")) {
var d = node.definition();
if (d.single_use === undefined) {
d.single_use = loop_ids[d.id] === in_loop
&& d.scope === node.scope
&& value.is_constant_expression();
}
if (d.references.length == 1 && d.single_use) return true;
}
return value instanceof AST_Lambda;
}
function is_modified(node, level, immutable) {
@@ -809,7 +838,7 @@ merge(Compressor.prototype, {
var sym;
if (node instanceof AST_Call
|| node instanceof AST_Exit
|| node instanceof AST_PropAccess
|| node instanceof AST_PropAccess && node.has_side_effects(compressor)
|| node instanceof AST_SymbolRef
&& (lvalues[node.name]
|| side_effects && !references_in_scope(node.definition()))
@@ -842,6 +871,8 @@ merge(Compressor.prototype, {
&& !fn.uses_eval
&& (iife = compressor.parent()) instanceof AST_Call
&& iife.expression === fn) {
var fn_strict = compressor.has_directive("use strict");
if (fn_strict && fn.body.indexOf(fn_strict) < 0) fn_strict = false;
var names = Object.create(null);
for (var i = fn.argnames.length; --i >= 0;) {
var sym = fn.argnames[i];
@@ -859,7 +890,7 @@ merge(Compressor.prototype, {
}
arg = null;
}
if (node instanceof AST_This && !tw.find_parent(AST_Scope)) {
if (node instanceof AST_This && (fn_strict || !tw.find_parent(AST_Scope))) {
arg = null;
return true;
}
@@ -2110,6 +2141,22 @@ merge(Compressor.prototype, {
}
def(AST_Node, return_false);
def(AST_Constant, return_true);
def(AST_Function, function(){
var self = this;
var result = true;
self.walk(new TreeWalker(function(node) {
if (!result) return true;
if (node instanceof AST_SymbolRef) {
var def = node.definition();
if (self.enclosed.indexOf(def) >= 0
&& self.variables.get(def.name) !== def) {
result = false;
return true;
}
}
}));
return result;
});
def(AST_Unary, function(){
return this.expression.is_constant_expression();
});
@@ -4073,12 +4120,13 @@ merge(Compressor.prototype, {
d.fixed = fixed = make_node(AST_Function, fixed, fixed);
}
if (compressor.option("unused")
&& fixed instanceof AST_Function
&& 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 || fixed instanceof AST_Function
&& !(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) {

View File

@@ -482,13 +482,17 @@ function OutputStream(options) {
nodetype.DEFMETHOD("_codegen", generator);
};
var use_asm = false;
var in_directive = false;
var active_scope = null;
var use_asm = null;
AST_Node.DEFMETHOD("print", function(stream, force_parens){
var self = this, generator = self._codegen, prev_use_asm = use_asm;
if (self instanceof AST_Directive && self.value == "use asm" && stream.parent() instanceof AST_Scope) {
use_asm = true;
var self = this, generator = self._codegen;
if (self instanceof AST_Scope) {
active_scope = self;
}
else if (!use_asm && self instanceof AST_Directive && self.value == "use asm") {
use_asm = active_scope;
}
function doit() {
self.add_comments(stream);
@@ -502,8 +506,8 @@ function OutputStream(options) {
doit();
}
stream.pop_node();
if (self instanceof AST_Scope) {
use_asm = prev_use_asm;
if (self === use_asm) {
use_asm = null;
}
});
AST_Node.DEFMETHOD("_print", AST_Node.prototype.print);

View File

@@ -1054,6 +1054,8 @@ function parse($TEXT, options) {
var name = is("name") ? as_symbol(in_statement ? AST_SymbolDefun : AST_SymbolLambda) : null;
if (in_statement && !name)
unexpected();
if (name && ctor !== AST_Accessor && !(name instanceof AST_SymbolDeclaration))
unexpected(prev());
expect("(");
var argnames = [];
for (var first = true; !is("punc", ")");) {

View File

@@ -4,7 +4,7 @@
"homepage": "http://lisperator.net/uglifyjs",
"author": "Mihai Bazon <mihai.bazon@gmail.com> (http://lisperator.net/)",
"license": "BSD-2-Clause",
"version": "3.1.1",
"version": "3.1.4",
"engines": {
"node": ">=0.8.0"
},
@@ -30,7 +30,7 @@
],
"dependencies": {
"commander": "~2.11.0",
"source-map": "~0.5.1"
"source-map": "~0.6.1"
},
"devDependencies": {
"acorn": "~5.1.1",

View File

@@ -104,3 +104,65 @@ asm_mixed: {
}
}
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;'
}

View File

@@ -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
}
@@ -1834,9 +1846,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"
}
@@ -2451,3 +2463,140 @@ issue_2313_2: {
}
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"
}
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",
]
}

View File

@@ -751,12 +751,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) {};
})();
}();
})();
}
}
}

View File

@@ -1021,6 +1021,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());
@@ -1028,11 +1029,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: {
@@ -1045,17 +1050,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: {

View File

@@ -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"
}
@@ -2549,7 +2547,7 @@ issue_1922_2: {
expect_stdout: "1"
}
accessor: {
accessor_1: {
options = {
evaluate: true,
reduce_vars: true,
@@ -2578,6 +2576,33 @@ accessor: {
expect_stdout: "1 1"
}
accessor_2: {
options = {
collapse_vars: true,
evaluate: 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_vars: true,
@@ -2602,3 +2627,270 @@ 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: {
for (var i = { a: 1 }.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: {
for (var b in [ 1, 2, 3 ])
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"
}

View File

@@ -73,6 +73,12 @@ describe("minify", function() {
assert.strictEqual(run_code(compressed), run_code(original));
});
it("should not parse invalid use of reserved words", function() {
assert.strictEqual(Uglify.minify("function enum(){}").error, undefined);
assert.strictEqual(Uglify.minify("function static(){}").error, undefined);
assert.strictEqual(Uglify.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};';