Merge branch 'master' into harmony-v3.2.0

This commit is contained in:
alexlamsl
2017-11-26 04:23:57 +08:00
12 changed files with 967 additions and 103 deletions

View File

@@ -617,17 +617,17 @@ If you're using the `X-SourceMap` header instead, you can just omit `sourceMap.u
the resultant code is shorter: `m(){return x}` becomes `m:()=>x`. 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. This transform requires that the `ecma` compress option is set to `6` or greater.
- `booleans` (default: `true`) -- various optimizations for boolean context, for example `!!a - `booleans` (default: `true`) -- various optimizations for boolean context,
? b : c → a ? b : c` for example `!!a ? b : c → a ? b : c`
- `cascade` (default: `true`) -- small optimization for sequences, transform `x, x` into `x` - `cascade` (default: `true`) -- small optimization for sequences, transform
and `x = something(), x` into `x = something()` `x, x` into `x` and `x = something(), x` into `x = something()`
- `collapse_vars` (default: `true`) -- Collapse single-use non-constant variables - side - `collapse_vars` (default: `true`) -- Collapse single-use non-constant variables,
effects permitting. side effects permitting.
- `comparisons` (default: `true`) -- apply certain optimizations to binary nodes, for example: - `comparisons` (default: `true`) -- apply certain optimizations to binary nodes,
`!(a <= b) → a > b` (only when `unsafe_comps`), attempts to negate binary e.g. `!(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. nodes, e.g. `a = !b && !c && !d && !e → a=!(b||c||d||e)` etc.
- `computed_props` (default: `true`) -- Transforms constant computed properties - `computed_props` (default: `true`) -- Transforms constant computed properties
@@ -638,7 +638,7 @@ If you're using the `X-SourceMap` header instead, you can just omit `sourceMap.u
- `dead_code` (default: `true`) -- remove unreachable code - `dead_code` (default: `true`) -- remove unreachable code
- `drop_console` (default: `false`) -- default `false`. Pass `true` to discard calls to - `drop_console` (default: `false`) -- Pass `true` to discard calls to
`console.*` functions. If you wish to drop a specific function call `console.*` functions. If you wish to drop a specific function call
such as `console.info` and/or retain side effects from function arguments such as `console.info` and/or retain side effects from function arguments
after dropping the function call then use `pure_funcs` instead. after dropping the function call then use `pure_funcs` instead.
@@ -650,14 +650,14 @@ If you're using the `X-SourceMap` header instead, you can just omit `sourceMap.u
- `evaluate` (default: `true`) -- attempt to evaluate constant expressions - `evaluate` (default: `true`) -- attempt to evaluate constant expressions
- `expression` (default: `false`) -- default `false`. Pass `true` to preserve completion values - `expression` (default: `false`) -- Pass `true` to preserve completion values
from terminal statements without `return`, e.g. in bookmarklets. from terminal statements without `return`, e.g. in bookmarklets.
- `global_defs` (default: `{}`) -- see [conditional compilation](#conditional-compilation) - `global_defs` (default: `{}`) -- see [conditional compilation](#conditional-compilation)
- `hoist_funs` (default: `true`) -- hoist function declarations - `hoist_funs` (default: `true`) -- hoist function declarations
- `hoist_props` (default: `false`) -- hoist properties from constant object and - `hoist_props` (default: `true`) -- hoist properties from constant object and
array literals into regular variables subject to a set of constraints. For example: array literals into regular variables subject to a set of constraints. For example:
`var o={p:1, q:2}; f(o.p, o.q);` is converted to `f(1, 2);`. Note: `hoist_props` `var o={p:1, q:2}; f(o.p, o.q);` is converted to `f(1, 2);`. Note: `hoist_props`
works best with `mangle` enabled, the `compress` option `passes` set to `2` or higher, works best with `mangle` enabled, the `compress` option `passes` set to `2` or higher,
@@ -676,19 +676,18 @@ If you're using the `X-SourceMap` header instead, you can just omit `sourceMap.u
compressor from discarding class names. See also: the `keep_classnames` compressor from discarding class names. See also: the `keep_classnames`
[mangle option](#mangle). [mangle option](#mangle).
- `keep_fargs` (default: `true`) -- default `true`. Prevents the - `keep_fargs` (default: `true`) -- Prevents the compressor from discarding unused
compressor from discarding unused function arguments. You need this function arguments. You need this for code which relies on `Function.length`.
for code which relies on `Function.length`.
- `keep_fnames` (default: `false`) -- Pass `true` to prevent the - `keep_fnames` (default: `false`) -- Pass `true` to prevent the
compressor from discarding function names. Useful for code relying on compressor from discarding function names. Useful for code relying on
`Function.prototype.name`. See also: the `keep_fnames` [mangle option](#mangle). `Function.prototype.name`. See also: the `keep_fnames` [mangle option](#mangle).
- `keep_infinity` (default: `false`) -- default `false`. Pass `true` to prevent `Infinity` from - `keep_infinity` (default: `false`) -- Pass `true` to prevent `Infinity` from
being compressed into `1/0`, which may cause performance issues on Chrome. 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 - `loops` (default: `true`) -- optimizations for `do`, `while` and `for` loops
statically determine the condition when we can statically determine the condition.
- `negate_iife` (default: `true`) -- 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 where the return value is discarded, to avoid the parens that the
@@ -719,11 +718,11 @@ If you're using the `X-SourceMap` header instead, you can just omit `sourceMap.u
`foo` is certain to not throw, i.e. not `null` or `undefined`. `foo` is certain to not throw, i.e. not `null` or `undefined`.
- `reduce_funcs` (default: `true`) -- Allows single-use functions to be - `reduce_funcs` (default: `true`) -- Allows single-use functions to be
inlined as function expressions when permissible allowing further inlined as function expressions when permissible allowing further
optimization. Enabled by default. Option depends on `reduce_vars` optimization. Enabled by default. Option depends on `reduce_vars`
being enabled. Some code runs faster in the Chrome V8 engine if this being enabled. Some code runs faster in the Chrome V8 engine if this
option is disabled. Does not negatively impact other major browsers. option is disabled. Does not negatively impact other major browsers.
- `reduce_vars` (default: `true`) -- Improve optimization on variables assigned with and - `reduce_vars` (default: `true`) -- Improve optimization on variables assigned with and
used as constant values. used as constant values.
@@ -736,21 +735,22 @@ If you're using the `X-SourceMap` header instead, you can just omit `sourceMap.u
occasions the default sequences limit leads to very slow compress times in which occasions the default sequences limit leads to very slow compress times in which
case a value of `20` or less is recommended. case a value of `20` or less is recommended.
- `side_effects` (default: `true`) -- default `true`. Pass `false` to disable potentially dropping - `side_effects` (default: `true`) -- Pass `false` to disable potentially dropping
functions marked as "pure". A function call is marked as "pure" if a comment functions marked as "pure". A function call is marked as "pure" if a comment
annotation `/*@__PURE__*/` or `/*#__PURE__*/` immediately precedes the call. For annotation `/*@__PURE__*/` or `/*#__PURE__*/` immediately precedes the call. For
example: `/*@__PURE__*/foo();` example: `/*@__PURE__*/foo();`
- `switches` (default: `true`) -- de-duplicate and remove unreachable `switch` branches - `switches` (default: `true`) -- de-duplicate and remove unreachable `switch` branches
- `toplevel` (default: `false`) -- drop unreferenced functions (`"funcs"`) and/or variables (`"vars"`) - `toplevel` (default: `false`) -- drop unreferenced functions (`"funcs"`) and/or
in the top level scope (`false` by default, `true` to drop both unreferenced variables (`"vars"`) in the top level scope (`false` by default, `true` to drop
functions and variables) both unreferenced functions and variables)
- `top_retain` (default: `null`) -- prevent specific toplevel functions and variables from `unused` - `top_retain` (default: `null`) -- prevent specific toplevel functions and
removal (can be array, comma-separated, RegExp or function. Implies `toplevel`) variables from `unused` removal (can be array, comma-separated, RegExp or
function. Implies `toplevel`)
- `typeofs` (default: `true`) -- default `true`. Transforms `typeof foo == "undefined"` into - `typeofs` (default: `true`) -- Transforms `typeof foo == "undefined"` into
`foo === void 0`. Note: recommend to set this value to `false` for IE10 and `foo === void 0`. Note: recommend to set this value to `false` for IE10 and
earlier versions due to known issues. earlier versions due to known issues.
@@ -788,31 +788,31 @@ If you're using the `X-SourceMap` header instead, you can just omit `sourceMap.u
- `unsafe_regexp` (default: `false`) -- enable substitutions of variables with - `unsafe_regexp` (default: `false`) -- enable substitutions of variables with
`RegExp` values the same way as if they are constants. `RegExp` values the same way as if they are constants.
- `unused` (default: `true`) -- drop unreferenced functions and variables (simple direct variable - `unused` (default: `true`) -- drop unreferenced functions and variables (simple
assignments do not count as references unless set to `"keep_assign"`) direct variable assignments do not count as references unless set to `"keep_assign"`)
- `warnings` (default: `false`) -- display warnings when dropping unreachable code or unused - `warnings` (default: `false`) -- display warnings when dropping unreachable
declarations etc. code or unused declarations etc.
## Mangle options ## Mangle options
- `eval` (default `false`). Pass `true` to mangle names visible in scopes - `eval` (default `false`) -- Pass `true` to mangle names visible in scopes
where `eval` or `with` are used. where `eval` or `with` are used.
- `keep_classnames` (default `false`). Pass `true` to not mangle class names. - `keep_classnames` (default `false`) -- Pass `true` to not mangle class names.
See also: the `keep_classnames` [compress option](#compress-options). See also: the `keep_classnames` [compress option](#compress-options).
- `keep_fnames` (default `false`). Pass `true` to not mangle function 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` Useful for code relying on `Function.prototype.name`. See also: the `keep_fnames`
[compress option](#compress-options). [compress option](#compress-options).
- `reserved` (default `[]`). Pass an array of identifiers that should be - `reserved` (default `[]`) -- Pass an array of identifiers that should be
excluded from mangling. Example: `["foo", "bar"]`. excluded from mangling. Example: `["foo", "bar"]`.
- `toplevel` (default `false`). Pass `true` to mangle names declared in the - `toplevel` (default `false`) -- Pass `true` to mangle names declared in the
top level scope. top level scope.
- `safari10` (default `false`). Pass `true` to work around the Safari 10 loop - `safari10` (default `false`) -- Pass `true` to work around the Safari 10 loop
iterator [bug](https://bugs.webkit.org/show_bug.cgi?id=171041) iterator [bug](https://bugs.webkit.org/show_bug.cgi?id=171041)
"Cannot declare a let variable twice". "Cannot declare a let variable twice".

View File

@@ -49,6 +49,7 @@ program.option("--ie8", "Support non-standard Internet Explorer 8.");
program.option("--keep-classnames", "Do not mangle/drop class names."); program.option("--keep-classnames", "Do not mangle/drop class names.");
program.option("--keep-fnames", "Do not mangle/drop function names. Useful for code relying on Function.prototype.name."); program.option("--keep-fnames", "Do not mangle/drop function names. Useful for code relying on Function.prototype.name.");
program.option("--name-cache <file>", "File to hold mangled name mappings."); program.option("--name-cache <file>", "File to hold mangled name mappings.");
program.option("--no-rename", "Disable symbol expansion.");
program.option("--self", "Build UglifyJS as a library (implies --wrap UglifyJS)"); program.option("--self", "Build UglifyJS as a library (implies --wrap UglifyJS)");
program.option("--source-map [options]", "Enable source map/specify source map options.", parse_source_map()); program.option("--source-map [options]", "Enable source map/specify source map options.", parse_source_map());
program.option("--timings", "Display operations run time on STDERR.") program.option("--timings", "Display operations run time on STDERR.")
@@ -67,11 +68,13 @@ if (!program.output && program.sourceMap && program.sourceMap.url != "inline") {
"compress", "compress",
"ie8", "ie8",
"mangle", "mangle",
"rename",
"sourceMap", "sourceMap",
"toplevel", "toplevel",
"wrap" "wrap"
].forEach(function(name) { ].forEach(function(name) {
if (name in program) { if (name in program) {
if (name == "rename" && program[name]) return;
options[name] = program[name]; options[name] = program[name];
} }
}); });

View File

@@ -63,7 +63,7 @@ function Compressor(options, false_by_default) {
expression : false, expression : false,
global_defs : {}, global_defs : {},
hoist_funs : !false_by_default, hoist_funs : !false_by_default,
hoist_props : false, hoist_props : !false_by_default,
hoist_vars : false, hoist_vars : false,
ie8 : false, ie8 : false,
if_return : !false_by_default, if_return : !false_by_default,
@@ -341,7 +341,7 @@ merge(Compressor.prototype, {
} }
} }
} }
mark_escaped(d, node, value, 0); mark_escaped(d, node.scope, node, value, 0);
} }
if (node instanceof AST_SymbolCatch) { if (node instanceof AST_SymbolCatch) {
node.definition().fixed = false; node.definition().fixed = false;
@@ -627,7 +627,8 @@ merge(Compressor.prototype, {
|| !immutable || !immutable
&& parent instanceof AST_Call && parent instanceof AST_Call
&& parent.expression === node && parent.expression === node
&& (!(value instanceof AST_Function) || value.contains_this(parent))) { && (!(value instanceof AST_Function)
|| !(parent instanceof AST_New) && value.contains_this())) {
return true; return true;
} else if (parent instanceof AST_Array) { } else if (parent instanceof AST_Array) {
return is_modified(parent, parent, level + 1); return is_modified(parent, parent, level + 1);
@@ -639,10 +640,13 @@ merge(Compressor.prototype, {
} }
} }
function mark_escaped(d, node, value, level) { function mark_escaped(d, scope, node, value, level) {
var parent = tw.parent(level); var parent = tw.parent(level);
if (value instanceof AST_Constant || value instanceof AST_ClassExpression) return; if (value) {
if (level > 0 && value instanceof AST_Function) return; if (value.is_constant()) return;
if (value instanceof AST_ClassExpression) return;
if (level > 0 && value.is_constant_expression(scope)) return;
}
if (parent instanceof AST_Assign && parent.operator == "=" && node === parent.right if (parent instanceof AST_Assign && parent.operator == "=" && node === parent.right
|| parent instanceof AST_Call && node !== parent.expression || parent instanceof AST_Call && node !== parent.expression
|| parent instanceof AST_Return && node === parent.value && node.scope !== d.scope || parent instanceof AST_Return && node === parent.value && node.scope !== d.scope
@@ -650,13 +654,13 @@ merge(Compressor.prototype, {
d.escaped = true; d.escaped = true;
return; return;
} else if (parent instanceof AST_Array) { } else if (parent instanceof AST_Array) {
mark_escaped(d, parent, parent, level + 1); mark_escaped(d, scope, parent, parent, level + 1);
} else if (parent instanceof AST_ObjectKeyVal && node === parent.value) { } else if (parent instanceof AST_ObjectKeyVal && node === parent.value) {
var obj = tw.parent(level + 1); var obj = tw.parent(level + 1);
mark_escaped(d, obj, obj, level + 2); mark_escaped(d, scope, obj, obj, level + 2);
} else if (parent instanceof AST_PropAccess && node === parent.expression) { } else if (parent instanceof AST_PropAccess && node === parent.expression) {
value = read_property(value, parent.property); value = read_property(value, parent.property);
mark_escaped(d, parent, value, level + 1); mark_escaped(d, scope, parent, value, level + 1);
if (value) return; if (value) return;
} }
if (level == 0) d.direct_access = true; if (level == 0) d.direct_access = true;
@@ -845,6 +849,12 @@ merge(Compressor.prototype, {
} }
} }
function is_identifier_atom(node) {
return node instanceof AST_Infinity
|| node instanceof AST_NaN
|| node instanceof AST_Undefined;
}
function tighten_body(statements, compressor) { function tighten_body(statements, compressor) {
var CHANGED, max_iter = 10; var CHANGED, max_iter = 10;
do { do {
@@ -911,7 +921,7 @@ merge(Compressor.prototype, {
&& !(node instanceof AST_SymbolDeclaration) && !(node instanceof AST_SymbolDeclaration)
&& lhs.equivalent_to(node)) { && lhs.equivalent_to(node)) {
if (is_lhs(node, parent)) { if (is_lhs(node, parent)) {
if (candidate.multiple) replaced++; if (value_def) replaced++;
return node; return node;
} }
CHANGED = abort = true; CHANGED = abort = true;
@@ -926,19 +936,24 @@ merge(Compressor.prototype, {
return make_node(AST_UnaryPrefix, candidate, candidate); return make_node(AST_UnaryPrefix, candidate, candidate);
} }
if (candidate instanceof AST_VarDef) { if (candidate instanceof AST_VarDef) {
if (candidate.multiple) { if (value_def) {
abort = false; abort = false;
return node; return node;
} }
var def = candidate.name.definition(); var def = candidate.name.definition();
var value = candidate.value;
if (def.references.length - def.replaced == 1 && !compressor.exposed(def)) { if (def.references.length - def.replaced == 1 && !compressor.exposed(def)) {
def.replaced++; def.replaced++;
return maintain_this_binding(parent, node, candidate.value); if (funarg && is_identifier_atom(value)) {
return value.transform(compressor);
} else {
return maintain_this_binding(parent, node, value);
}
} }
return make_node(AST_Assign, candidate, { return make_node(AST_Assign, candidate, {
operator: "=", operator: "=",
left: make_node(AST_SymbolRef, candidate.name, candidate.name), left: make_node(AST_SymbolRef, candidate.name, candidate.name),
right: candidate.value right: value
}); });
} }
candidate.write_only = false; candidate.write_only = false;
@@ -1000,18 +1015,20 @@ merge(Compressor.prototype, {
extract_candidates(statements[stat_index]); extract_candidates(statements[stat_index]);
while (candidates.length > 0) { while (candidates.length > 0) {
var candidate = candidates.pop(); var candidate = candidates.pop();
var value_def = null;
var lhs = get_lhs(candidate); var lhs = get_lhs(candidate);
if (!lhs || is_lhs_read_only(lhs) || lhs.has_side_effects(compressor)) continue; if (!lhs || is_lhs_read_only(lhs) || lhs.has_side_effects(compressor)) continue;
// Locate symbols which may execute code outside of scanning range // Locate symbols which may execute code outside of scanning range
var lvalues = get_lvalues(candidate); var lvalues = get_lvalues(candidate);
if (lhs instanceof AST_SymbolRef) lvalues[lhs.name] = false; if (lhs instanceof AST_SymbolRef) lvalues[lhs.name] = false;
var replace_all = candidate.multiple; var replace_all = value_def;
if (!replace_all && lhs instanceof AST_SymbolRef) { if (!replace_all && lhs instanceof AST_SymbolRef) {
var def = lhs.definition(); var def = lhs.definition();
replace_all = def.references.length - def.replaced == 1; replace_all = def.references.length - def.replaced == 1;
} }
var side_effects = value_has_side_effects(candidate); var side_effects = value_has_side_effects(candidate);
var hit = candidate.name instanceof AST_SymbolFunarg; var funarg = candidate.name instanceof AST_SymbolFunarg;
var hit = funarg;
var abort = false, replaced = 0, can_replace = !args || !hit; var abort = false, replaced = 0, can_replace = !args || !hit;
if (!can_replace) { if (!can_replace) {
for (var j = compressor.self().argnames.lastIndexOf(candidate.name) + 1; !abort && j < args.length; j++) { for (var j = compressor.self().argnames.lastIndexOf(candidate.name) + 1; !abort && j < args.length; j++) {
@@ -1022,13 +1039,12 @@ merge(Compressor.prototype, {
for (var i = stat_index; !abort && i < statements.length; i++) { for (var i = stat_index; !abort && i < statements.length; i++) {
statements[i].transform(scanner); statements[i].transform(scanner);
} }
if (candidate.multiple) { if (value_def) {
var def = candidate.name.definition(); var def = candidate.name.definition();
if (abort && def.references.length - def.replaced > replaced) replaced = false; if (abort && def.references.length - def.replaced > replaced) replaced = false;
else { else {
abort = false; abort = false;
hit = candidate.name instanceof AST_SymbolFunarg; hit = funarg;
var value_def = candidate.value.definition();
for (var i = stat_index; !abort && i < statements.length; i++) { for (var i = stat_index; !abort && i < statements.length; i++) {
statements[i].transform(multi_replacer); statements[i].transform(multi_replacer);
} }
@@ -1131,13 +1147,13 @@ merge(Compressor.prototype, {
} }
} }
function mangleable_var(expr) { function mangleable_var(var_def) {
var value = expr.value; var value = var_def.value;
if (!(value instanceof AST_SymbolRef)) return false; if (!(value instanceof AST_SymbolRef)) return;
if (value.name == "arguments") return false; if (value.name == "arguments") return;
if (value.definition().undeclared) return false; var def = value.definition();
expr.multiple = true; if (def.undeclared) return;
return true; return value_def = def;
} }
function get_lhs(expr) { function get_lhs(expr) {
@@ -4062,9 +4078,7 @@ merge(Compressor.prototype, {
if (self.operator == "delete" if (self.operator == "delete"
&& !(e instanceof AST_SymbolRef && !(e instanceof AST_SymbolRef
|| e instanceof AST_PropAccess || e instanceof AST_PropAccess
|| e instanceof AST_NaN || is_identifier_atom(e))) {
|| e instanceof AST_Infinity
|| e instanceof AST_Undefined)) {
if (e instanceof AST_Sequence) { if (e instanceof AST_Sequence) {
e = e.expressions.slice(); e = e.expressions.slice();
e.push(make_node(AST_True, self)); e.push(make_node(AST_True, self));
@@ -4972,6 +4986,17 @@ merge(Compressor.prototype, {
if (compressor.option("properties")) { if (compressor.option("properties")) {
var key = prop.evaluate(compressor); var key = prop.evaluate(compressor);
if (key !== prop) { if (key !== prop) {
if (typeof key == "string") {
if (key == "undefined") {
key = undefined;
} else {
var value = parseFloat(key);
if (value.toString() == key) {
key = value;
}
}
}
prop = self.property = best_of_expression(prop, make_node_from_constant(key, prop).transform(compressor));
var property = "" + key; var property = "" + key;
if (is_identifier_string(property) if (is_identifier_string(property)
&& property.length <= prop.print_to_string().length + 1) { && property.length <= prop.print_to_string().length + 1) {
@@ -4980,14 +5005,6 @@ merge(Compressor.prototype, {
property: property property: property
}).optimize(compressor); }).optimize(compressor);
} }
if (!(prop instanceof AST_Number)) {
var value = parseFloat(property);
if (value.toString() == property) {
prop = self.property = make_node(AST_Number, prop, {
value: value
});
}
}
} }
} }
if (is_lhs(self, compressor.parent())) return self; if (is_lhs(self, compressor.parent())) return self;
@@ -5044,8 +5061,7 @@ merge(Compressor.prototype, {
return self; return self;
}); });
AST_Lambda.DEFMETHOD("contains_this", function(grandparent) { AST_Lambda.DEFMETHOD("contains_this", function() {
if (grandparent instanceof AST_New) return false;
var result; var result;
var self = this; var self = this;
self.walk(new TreeWalker(function(node) { self.walk(new TreeWalker(function(node) {
@@ -5071,7 +5087,8 @@ merge(Compressor.prototype, {
})) break; })) break;
var value = prop.value; var value = prop.value;
if ((value instanceof AST_Accessor || value instanceof AST_Function) if ((value instanceof AST_Accessor || value instanceof AST_Function)
&& value.contains_this(compressor.parent())) break; && !(compressor.parent() instanceof AST_New)
&& value.contains_this()) break;
return make_node(AST_Sub, this, { return make_node(AST_Sub, this, {
expression: make_node(AST_Array, expr, { expression: make_node(AST_Array, expr, {
elements: props.map(function(prop) { elements: props.map(function(prop) {

View File

@@ -57,6 +57,7 @@ function minify(files, options) {
nameCache: null, nameCache: null,
output: {}, output: {},
parse: {}, parse: {},
rename: undefined,
sourceMap: false, sourceMap: false,
timings: false, timings: false,
toplevel: false, toplevel: false,
@@ -69,6 +70,9 @@ function minify(files, options) {
if (options.keep_classnames === undefined) { if (options.keep_classnames === undefined) {
options.keep_classnames = options.keep_fnames; options.keep_classnames = options.keep_fnames;
} }
if (options.rename === undefined) {
options.rename = options.compress && options.mangle;
}
set_shorthand("ecma", options, [ "parse", "compress", "output" ]); set_shorthand("ecma", options, [ "parse", "compress", "output" ]);
set_shorthand("ie8", options, [ "compress", "mangle", "output" ]); set_shorthand("ie8", options, [ "compress", "mangle", "output" ]);
set_shorthand("keep_classnames", options, [ "compress", "mangle" ]); set_shorthand("keep_classnames", options, [ "compress", "mangle" ]);
@@ -146,6 +150,11 @@ function minify(files, options) {
if (options.wrap) { if (options.wrap) {
toplevel = toplevel.wrap_commonjs(options.wrap); toplevel = toplevel.wrap_commonjs(options.wrap);
} }
if (timings) timings.rename = Date.now();
if (options.rename) {
toplevel.figure_out_scope(options.mangle);
toplevel.expand_names(options.mangle);
}
if (timings) timings.compress = Date.now(); if (timings) timings.compress = Date.now();
if (options.compress) toplevel = new Compressor(options.compress).compress(toplevel); if (options.compress) toplevel = new Compressor(options.compress).compress(toplevel);
if (timings) timings.scope = Date.now(); if (timings) timings.scope = Date.now();
@@ -206,7 +215,8 @@ function minify(files, options) {
if (timings) { if (timings) {
timings.end = Date.now(); timings.end = Date.now();
result.timings = { result.timings = {
parse: 1e-3 * (timings.compress - timings.parse), parse: 1e-3 * (timings.rename - timings.parse),
rename: 1e-3 * (timings.compress - timings.rename),
compress: 1e-3 * (timings.scope - timings.compress), compress: 1e-3 * (timings.scope - timings.compress),
scope: 1e-3 * (timings.mangle - timings.scope), scope: 1e-3 * (timings.mangle - timings.scope),
mangle: 1e-3 * (timings.properties - timings.mangle), mangle: 1e-3 * (timings.properties - timings.mangle),

View File

@@ -43,7 +43,7 @@
"use strict"; "use strict";
function SymbolDef(scope, index, orig) { function SymbolDef(scope, orig) {
this.name = orig.name; this.name = orig.name;
this.orig = [ orig ]; this.orig = [ orig ];
this.eliminated = 0; this.eliminated = 0;
@@ -54,7 +54,6 @@ function SymbolDef(scope, index, orig) {
this.export = false; this.export = false;
this.mangled_name = null; this.mangled_name = null;
this.undeclared = false; this.undeclared = false;
this.index = index;
this.id = SymbolDef.next_id++; this.id = SymbolDef.next_id++;
}; };
@@ -346,7 +345,7 @@ AST_Toplevel.DEFMETHOD("def_global", function(node){
if (globals.has(name)) { if (globals.has(name)) {
return globals.get(name); return globals.get(name);
} else { } else {
var g = new SymbolDef(this, globals.size(), node); var g = new SymbolDef(this, node);
g.undeclared = true; g.undeclared = true;
g.global = true; g.global = true;
globals.set(name, g); globals.set(name, g);
@@ -417,7 +416,7 @@ AST_Scope.DEFMETHOD("def_function", function(symbol){
AST_Scope.DEFMETHOD("def_variable", function(symbol){ AST_Scope.DEFMETHOD("def_variable", function(symbol){
var def; var def;
if (!this.variables.has(symbol.name)) { if (!this.variables.has(symbol.name)) {
def = new SymbolDef(this, this.variables.size(), symbol); def = new SymbolDef(this, symbol);
this.variables.set(symbol.name, def); this.variables.set(symbol.name, def);
def.global = !this.parent_scope; def.global = !this.parent_scope;
} else { } else {
@@ -435,7 +434,7 @@ AST_Scope.DEFMETHOD("next_mangled", function(options){
// https://github.com/mishoo/UglifyJS2/issues/242 -- do not // https://github.com/mishoo/UglifyJS2/issues/242 -- do not
// shadow a name reserved from mangling. // shadow a name reserved from mangling.
if (options.reserved.indexOf(m) >= 0) continue; if (member(m, options.reserved)) continue;
// we must ensure that the mangled name does not shadow a name // we must ensure that the mangled name does not shadow a name
// from some parent scope that is referenced in this or in // from some parent scope that is referenced in this or in
@@ -487,7 +486,7 @@ AST_Symbol.DEFMETHOD("global", function(){
return this.definition().global; return this.definition().global;
}); });
AST_Toplevel.DEFMETHOD("_default_mangler_options", function(options){ AST_Toplevel.DEFMETHOD("_default_mangler_options", function(options) {
options = defaults(options, { options = defaults(options, {
eval : false, eval : false,
ie8 : false, ie8 : false,
@@ -497,15 +496,14 @@ AST_Toplevel.DEFMETHOD("_default_mangler_options", function(options){
toplevel : false, toplevel : false,
}); });
if (!Array.isArray(options.reserved)) options.reserved = []; if (!Array.isArray(options.reserved)) options.reserved = [];
// Never mangle arguments
push_uniq(options.reserved, "arguments");
return options; return options;
}); });
AST_Toplevel.DEFMETHOD("mangle_names", function(options){ AST_Toplevel.DEFMETHOD("mangle_names", function(options){
options = this._default_mangler_options(options); options = this._default_mangler_options(options);
// Never mangle arguments
options.reserved.push('arguments');
// We only need to mangle declaration nodes. Special logic wired // We only need to mangle declaration nodes. Special logic wired
// into the code generator will display the mangled name if it's // 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 // present (and for AST_SymbolRef-s it'll use the mangled name of
@@ -514,11 +512,7 @@ AST_Toplevel.DEFMETHOD("mangle_names", function(options){
var to_mangle = []; var to_mangle = [];
if (options.cache) { if (options.cache) {
this.globals.each(function(symbol){ this.globals.each(collect);
if (options.reserved.indexOf(symbol.name) < 0) {
to_mangle.push(symbol);
}
});
} }
var tw = new TreeWalker(function(node, descend){ var tw = new TreeWalker(function(node, descend){
@@ -530,13 +524,7 @@ AST_Toplevel.DEFMETHOD("mangle_names", function(options){
return true; // don't descend again in TreeWalker return true; // don't descend again in TreeWalker
} }
if (node instanceof AST_Scope) { if (node instanceof AST_Scope) {
var p = tw.parent(), a = []; node.variables.each(collect);
node.variables.each(function(symbol){
if (options.reserved.indexOf(symbol.name) < 0) {
a.push(symbol);
}
});
to_mangle.push.apply(to_mangle, a);
return; return;
} }
if (node instanceof AST_Label) { if (node instanceof AST_Label) {
@@ -561,6 +549,74 @@ AST_Toplevel.DEFMETHOD("mangle_names", function(options){
if (options.cache) { if (options.cache) {
options.cache.cname = this.cname; options.cache.cname = this.cname;
} }
function collect(symbol) {
if (!member(symbol.name, options.reserved)) {
to_mangle.push(symbol);
}
}
});
AST_Toplevel.DEFMETHOD("find_unique_prefix", function(options) {
var letters = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ$_";
var cache = options.cache && options.cache.props;
var prefixes = Object.create(null);
options.reserved.forEach(add_prefix);
this.globals.each(add_def);
this.walk(new TreeWalker(function(node) {
if (node instanceof AST_Scope) node.variables.each(add_def);
if (node instanceof AST_SymbolCatch) add_def(node.definition());
}));
var prefix, i = 0;
do {
prefix = create_name(i++);
} while (prefixes[prefix]);
return prefix;
function add_prefix(name) {
if (/[0-9]$/.test(name)) {
prefixes[name.replace(/[0-9]+$/, "")] = 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;
add_prefix(name);
}
function create_name(num) {
var name = "";
do {
name += letters[num % letters.length];
num = Math.floor(num / letters.length);
} while (num);
return name;
}
});
AST_Toplevel.DEFMETHOD("expand_names", function(options) {
options = this._default_mangler_options(options);
var prefix = this.find_unique_prefix(options);
this.globals.each(rename);
this.walk(new TreeWalker(function(node) {
if (node instanceof AST_Scope) node.variables.each(rename);
if (node instanceof AST_SymbolCatch) rename(node.definition());
}));
function rename(def) {
if (def.global || def.unmangleable(options)) return;
if (member(def.name, options.reserved)) return;
var d = def.redefined();
def.name = d ? d.name : prefix + def.id;
def.orig.forEach(function(sym) {
sym.name = def.name;
});
def.references.forEach(function(sym) {
sym.name = def.name;
});
}
}); });
AST_Toplevel.DEFMETHOD("compute_char_frequency", function(options){ AST_Toplevel.DEFMETHOD("compute_char_frequency", function(options){

View File

@@ -4,7 +4,7 @@
"homepage": "https://github.com/mishoo/UglifyJS2/tree/harmony", "homepage": "https://github.com/mishoo/UglifyJS2/tree/harmony",
"author": "Mihai Bazon <mihai.bazon@gmail.com> (http://lisperator.net/)", "author": "Mihai Bazon <mihai.bazon@gmail.com> (http://lisperator.net/)",
"license": "BSD-2-Clause", "license": "BSD-2-Clause",
"version": "3.1.10", "version": "3.2.0",
"engines": { "engines": {
"node": ">=0.8.0" "node": ">=0.8.0"
}, },
@@ -26,11 +26,11 @@
"LICENSE" "LICENSE"
], ],
"dependencies": { "dependencies": {
"commander": "~2.11.0", "commander": "~2.12.1",
"source-map": "~0.6.1" "source-map": "~0.6.1"
}, },
"devDependencies": { "devDependencies": {
"acorn": "~5.1.1", "acorn": "~5.2.1",
"mocha": "~3.5.1", "mocha": "~3.5.1",
"semver": "~5.4.1" "semver": "~5.4.1"
}, },

View File

@@ -3880,3 +3880,81 @@ issue_2436_14: {
} }
expect_stdout: true 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 (i = 0; i < 1; ++i)
for (k = 0; k < 1; ++k) {
value = 1;
value = value ? value + 1 : 0;
}
else
for (var i = 0; i < 1; ++i)
for (var k = 0; k < 1; ++k)
var value=1;
}
}
}
issue_2506: {
options = {
collapse_vars: true,
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"
}

View File

@@ -639,3 +639,136 @@ issue_2473_4: {
} }
expect_stdout: "1 2" expect_stdout: "1 2"
} }
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
}

View File

@@ -1305,3 +1305,29 @@ new_this: {
}(42); }(42);
} }
} }
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",
]
}

536
test/compress/rename.js Normal file
View File

@@ -0,0 +1,536 @@
mangle_catch: {
rename = true
options = {
ie8: false,
toplevel: false,
}
mangle = {
ie8: 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 = {
ie8: true,
toplevel: false,
}
mangle = {
ie8: 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 = {
ie8: false,
toplevel: false,
}
mangle = {
ie8: 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 = {
ie8: true,
toplevel: false,
}
mangle = {
ie8: 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 = {
ie8: false,
toplevel: true,
}
mangle = {
ie8: false,
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_ie8_toplevel: {
rename = true
options = {
ie8: true,
toplevel: true,
}
mangle = {
ie8: 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 = {
ie8: false,
toplevel: true,
}
mangle = {
ie8: false,
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_var_ie8_toplevel: {
rename = true
options = {
ie8: true,
toplevel: true,
}
mangle = {
ie8: 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 = {
ie8: false,
toplevel: false,
}
mangle = {
ie8: 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 = {
ie8: true,
toplevel: false,
}
mangle = {
ie8: 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 = {
ie8: false,
toplevel: true,
}
mangle = {
ie8: 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 = {
ie8: true,
toplevel: true,
}
mangle = {
ie8: 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 = {
ie8: false,
toplevel: false,
}
mangle = {
ie8: 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 = {
ie8: true,
toplevel: false,
}
mangle = {
ie8: 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 = {
ie8: false,
toplevel: true,
}
mangle = {
ie8: 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 = {
ie8: true,
toplevel: true,
}
mangle = {
ie8: 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 = {
ie8: 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 = {
ie8: 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 = {
ie8: 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(c){var o=1;console.log(c,o)}}()}f();"
expect_stdout: "0 1"
}
function_iife_catch_ie8: {
rename = true
mangle = {
ie8: true,
}
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_catch_catch: {
rename = true
mangle = {
ie8: 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(c){try{throw 2}catch(o){var o=3;console.log(o)}}console.log(o)}f();"
expect_stdout: [
"3",
"undefined",
]
}
function_catch_catch_ie8: {
rename = true
mangle = {
ie8: 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",
]
}

View File

@@ -354,7 +354,8 @@ describe("minify", function() {
Uglify.minify(ast, { Uglify.minify(ast, {
compress: { compress: {
sequences: false sequences: false
} },
mangle: false
}); });
assert.ok(stat.body); assert.ok(stat.body);
assert.strictEqual(stat.print_to_string(), "a=x()"); assert.strictEqual(stat.print_to_string(), "a=x()");

View File

@@ -117,6 +117,10 @@ function run_compress_tests() {
test.mangle.properties.reserved = quoted_props; test.mangle.properties.reserved = quoted_props;
U.reserve_quoted_keys(input, 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(options, true); var cmp = new U.Compressor(options, true);
var output = cmp.compress(input); var output = cmp.compress(input);
output.figure_out_scope(test.mangle); output.figure_out_scope(test.mangle);