Compare commits

..

62 Commits

Author SHA1 Message Date
Alex Lam S.L
4377e932ca v3.0.15 2017-06-01 18:12:38 +08:00
Alex Lam S.L
bac14ba881 fix non-identifier getter/setter name (#2041)
fixes #2040
2017-06-01 18:11:16 +08:00
Alex Lam S.L
ec095ed647 whitelist unsafe evaluate candidates (#2039)
- all arguments may accept constant values
- return constant value
- free of side effects
- available & identical across locales and runtime environments
2017-06-01 04:33:05 +08:00
Alex Lam S.L
17e73121fa enhance unsafe evaluate (#2037) 2017-06-01 00:56:28 +08:00
kzc
f71e8fd948 reformat mangle options section of README (#2036) 2017-05-31 21:52:43 +08:00
Alex Lam S.L
3e62faa64f v3.0.14 2017-05-31 11:34:51 +08:00
Alex Lam S.L
e9645e017f introduce unsafe_Func (#2033)
Separate flag for #203 functionality.
2017-05-31 03:38:00 +08:00
Alex Lam S.L
55b5f2a8aa widen CLI parse error code fragment displayed (#2032)
fixes #2030
2017-05-31 01:56:52 +08:00
Alex Lam S.L
4e0a22e5c8 v3.0.13 2017-05-29 10:52:13 +08:00
Alex Lam S.L
1aa38051fb better fix for #512 & #2010 (#2019)
- remove duplicated functionalities
- fix similar issue with `else`
2017-05-29 10:51:41 +08:00
Alex Lam S.L
e62b879b48 display default values in --help options (#2018) 2017-05-28 22:57:20 +08:00
Alex Lam S.L
c6c9f4f5a8 implement --help options (#2017) 2017-05-28 18:21:44 +08:00
Alex Lam S.L
fec14379f6 improve CLI usability (#2016)
Report supported options upon invalid option syntax.

fixes #1883
2017-05-28 04:09:40 +08:00
Alex Lam S.L
79131cd647 extend node_version range on applicable tests (#2015) 2017-05-27 22:18:28 +08:00
Alex Lam S.L
c3f14a1481 v3.0.12 2017-05-27 18:08:09 +08:00
Alex Lam S.L
7b13159cda fix hoist_funs on block-scoped function under "use strict" (#2013)
Technically not part of ES5, but commonly used code exists in the wild.
2017-05-27 17:44:59 +08:00
Alex Lam S.L
95094b9c22 fix if_return on AST_Defun (#2010)
Previous fiix for #1052 perturbs declaration order of functions which leads to incorrect behaviour under "use strict".
2017-05-27 13:41:49 +08:00
kzc
1ff8e9dd38 clarify what --mangle-props does (#2012) 2017-05-27 13:17:30 +08:00
kzc
78309a293d better document mangle properties options (#2009) 2017-05-27 02:28:43 +08:00
kzc
695e182d59 fix and expand --mangle-props documentation (#2008)
fixes #2007
2017-05-27 01:25:51 +08:00
Alex Lam S.L
dc33facfcb fix dead_code on block-scoped function under "use strict" (#2006)
Technically not part of ES5, but commonly used code exists in the wild.
2017-05-26 16:08:51 +08:00
Alex Lam S.L
c70fb60384 clean up lib/scope.js (#2003)
fixes #2004
2017-05-26 03:58:35 +08:00
Alex Lam S.L
793d61499b report timing breakdown (#2000)
fix corner cases with `sourceMap`

fixes #1998
2017-05-25 07:15:55 +08:00
Alex Lam S.L
a277fe168d ensure new line after describe_ast() (#1999) 2017-05-25 02:32:36 +08:00
Alex Lam S.L
7d3b941e6e reinstate describe_ast() on CLI (#1996)
fixes #1995
2017-05-24 02:30:09 +08:00
Alex Lam S.L
e95052a423 v3.0.11 2017-05-23 22:26:59 +08:00
Alex Lam S.L
e667f0acb8 fix source map offset (#1993)
Account for whitespace insertions.

fixes #505
fixes #890
2017-05-23 20:25:48 +08:00
kzc
69ac794bc8 add another minify() options example (#1988) 2017-05-22 12:19:07 +08:00
Alex Lam S.L
efdb65913b improve usability of global_defs in minify() (#1987)
Use `@key` to `parse()` string value as `AST_Node`.

fixes #1986
2017-05-22 01:38:43 +08:00
kzc
a1dedeb3ce more refinement of minify() documentation (#1983) 2017-05-21 04:55:03 +08:00
Alex Lam S.L
d3c4a8e9e7 v3.0.10 2017-05-21 01:30:17 +08:00
kzc
7e164aba8f add "es5" to package.json keywords (#1980) 2017-05-20 22:09:50 +08:00
kzc
22aedef849 document minify() option toplevel (#1979) 2017-05-20 22:09:21 +08:00
Alex Lam S.L
58fae7dc07 enhance if_return to handle return void... (#1977)
fixes #512
2017-05-20 15:58:46 +08:00
kzc
5bf8d7e949 document 3.x minify() does not throw errors (#1975) 2017-05-20 10:49:35 +08:00
kzc
1df9d06f4a document minify warnings and add an error example (#1973) 2017-05-19 17:20:21 +08:00
Alex Lam S.L
3408fc9d32 v3.0.9 2017-05-19 09:35:26 +08:00
Alex Lam S.L
eae26756f1 introduce unsafe_regexp (#1970)
fixes #1964
2017-05-19 09:06:29 +08:00
Alex Lam S.L
43add9416b v3.0.8 2017-05-18 14:49:40 +08:00
Alex Lam S.L
efcf167e5e make expect_stdout node version specific (#1963)
... via semver string on `node_version` label.
2017-05-18 11:28:35 +08:00
Kara
6ed90913ca fix docs for side_effects flag to reflect current behavior (#1966) 2017-05-18 10:51:49 +08:00
Alex Lam S.L
569c21e952 improve RegExp handling (#1959)
- remove `options.output.unescape_regexps`
- preserve original pattern whenever possible

fixes #54
fixes #1929
2017-05-17 20:10:50 +08:00
Alex Lam S.L
87c3a2c0ce remove space_colon (#1960)
Always emit space after colon when `options.output.beautify` is enabled.
2017-05-17 14:07:34 +08:00
Rob Garrison
baef8bf050 update output options in readme (#1958) 2017-05-17 11:54:46 +08:00
alexlamsl
0813c5316f remove Travis CI badge 2017-05-17 10:32:59 +08:00
Alex Lam S.L
c88139492d v3.0.7 2017-05-16 19:59:40 +08:00
Alex Lam S.L
cb45886512 export TreeTransformer (#1950)
- link to existing documentation on `TreeWalker` & `TreeTransformer`
- fix Travis build failures

fixes #1949
2017-05-16 19:59:05 +08:00
Alex Lam S.L
050474ab44 v3.0.6 2017-05-16 06:38:58 +08:00
Alex Lam S.L
f6c805ae1d print package name alongside version in CLI (#1946)
fixes #1945
2017-05-16 06:34:32 +08:00
Alex Lam S.L
9464d3c20f fix parsing of property access after new line (#1944)
Account for comments when detecting property access in `tokenizer`.

fixes #1943
2017-05-16 05:40:49 +08:00
alexlamsl
f18abd1b9c minor fixes to README.md 2017-05-16 01:33:01 +08:00
kzc
3be06ad085 reorg README for 3.x (#1942) 2017-05-16 01:12:00 +08:00
Alex Lam S.L
265008c948 improve keyword-related parser errors (#1941)
fixes #1937
2017-05-15 23:02:55 +08:00
Alex Lam S.L
ff526be61d v3.0.5 2017-05-15 11:37:14 +08:00
Alex Lam S.L
e005099fb1 fix & improve coverage of estree (#1935)
- fix `estree` conversion of getter/setter
- fix non-directive literal in `to_mozilla_ast()`
- revamp `test/mozilla-ast.js`
  - reuse `test/ufuzz.js` for code generation
  - use `acorn.parse()` for creating `estree`
- extend `test/ufuzz.js` for `acorn` workaround
  - catch variable redefinition
  - non-trivial literal as directive
  - adjust options for tolerance

Miscellaneous
- optional semi-colon when parsing directives

fixes #1914
closes #1915
2017-05-15 02:37:53 +08:00
kzc
504a436e9d Tweak README Notes (#1934) 2017-05-14 02:12:14 +08:00
Alex Lam S.L
3ca902258c fix bugs with getter/setter (#1926)
- `reduce_vars`
- `side_effects`
- property access for object
- `AST_SymbolAccessor` as key names

enhance `test/ufuzz.js`
- add object getter & setter
  - property assignment to setter
  - avoid infinite recursion in setter
- fix & adjust assignment operators
  - 50% `=`
  - 25% `+=`
  - 2.5% each for the rest
- avoid "Invalid array length"
- fix `console.log()`
  - bypass getter
  - curb recursive reference
- deprecate `-E`, always report runtime errors
2017-05-14 02:10:34 +08:00
olsonpm
fd0951231c document 3 max passes (#1928) 2017-05-13 12:54:32 +08:00
olsonpm
9e29b6dad2 clarify wording (#1931) 2017-05-13 12:54:01 +08:00
Alex Lam S.L
c391576d52 remove support for const (#1910)
As this is not part of ES5.
2017-05-12 14:57:41 +08:00
Alex Lam S.L
ac73c5d421 avoid arguments and eval in reduce_vars (#1924)
fixes #1922
2017-05-12 12:34:55 +08:00
olsonpm
547f41beba add documentation for side_effects & [#@]__PURE__ (#1925) 2017-05-12 12:29:55 +08:00
99 changed files with 2756 additions and 9739 deletions

View File

@@ -1,5 +1,4 @@
language: node_js language: node_js
before_install: "npm install -g npm"
node_js: node_js:
- "0.10" - "0.10"
- "0.12" - "0.12"

1018
README.md

File diff suppressed because it is too large Load Diff

View File

@@ -21,10 +21,27 @@ var options = {
compress: false, compress: false,
mangle: false mangle: false
}; };
program._name = info.name; program.version(info.name + " " + info.version);
program.version(info.version);
program.parseArgv = program.parse; program.parseArgv = program.parse;
program.parse = undefined; program.parse = undefined;
if (process.argv.indexOf("ast") >= 0) program.helpInformation = UglifyJS.describe_ast;
else if (process.argv.indexOf("options") >= 0) program.helpInformation = function() {
var text = [];
var options = UglifyJS.default_options();
for (var option in options) {
text.push("--" + (option == "output" ? "beautify" : option == "sourceMap" ? "source-map" : option) + " options:");
var defs = options[option];
var padding = "";
Object.keys(defs).map(function(name) {
if (padding.length < name.length) padding = Array(name.length + 1).join(" ");
return [ name, JSON.stringify(defs[name]) ];
}).forEach(function(tokens) {
text.push(" " + tokens[0] + padding.slice(tokens[0].length - 2) + tokens[1]);
});
text.push("");
}
return text.join("\n");
};
program.option("-p, --parse <options>", "Specify parser options.", parse_js("parse", true)); program.option("-p, --parse <options>", "Specify parser options.", parse_js("parse", true));
program.option("-c, --compress [options]", "Enable compressor/specify compressor options.", parse_js("compress", true)); program.option("-c, --compress [options]", "Enable compressor/specify compressor options.", parse_js("compress", true));
program.option("-m, --mangle [options]", "Mangle names/specify mangler options.", parse_js("mangle", true)); program.option("-m, --mangle [options]", "Mangle names/specify mangler options.", parse_js("mangle", true));
@@ -39,7 +56,7 @@ program.option("--keep-fnames", "Do not mangle/drop function names. Useful for c
program.option("--name-cache <file>", "File to hold mangled name mappings."); program.option("--name-cache <file>", "File to hold mangled name mappings.");
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("--stats", "Display operations run time on STDERR.") program.option("--timings", "Display operations run time on STDERR.")
program.option("--toplevel", "Compress and/or mangle variables in toplevel scope."); program.option("--toplevel", "Compress and/or mangle variables in toplevel scope.");
program.option("--verbose", "Print diagnostic messages."); program.option("--verbose", "Print diagnostic messages.");
program.option("--warn", "Print warning messages."); program.option("--warn", "Print warning messages.");
@@ -115,10 +132,10 @@ if (program.output == "ast") {
}; };
} }
if (program.parse) { if (program.parse) {
if (program.parse.acorn || program.parse.spidermonkey) { if (!program.parse.acorn && !program.parse.spidermonkey) {
if (program.sourceMap) fatal("ERROR: inline source map only works with built-in parser");
} else {
options.parse = program.parse; options.parse = program.parse;
} else if (program.sourceMap && program.sourceMap.content == "inline") {
fatal("ERROR: inline source map only works with built-in parser");
} }
} }
var convert_path = function(name) { var convert_path = function(name) {
@@ -172,7 +189,7 @@ function run() {
UglifyJS.AST_Node.warn_function = function(msg) { UglifyJS.AST_Node.warn_function = function(msg) {
console.error("WARN:", msg); console.error("WARN:", msg);
}; };
if (program.stats) program.stats = Date.now(); if (program.timings) options.timings = true;
try { try {
if (program.parse) { if (program.parse) {
if (program.parse.acorn) { if (program.parse.acorn) {
@@ -208,9 +225,10 @@ function run() {
col = line.length; col = line.length;
} }
if (line) { if (line) {
if (col > 40) { var limit = 70;
line = line.slice(col - 40); if (col > limit) {
col = 40; line = line.slice(col - limit);
col = limit;
} }
console.error(line.slice(0, 80)); console.error(line.slice(0, 80));
console.error(line.slice(0, col).replace(/\S/g, " ") + "^"); console.error(line.slice(0, col).replace(/\S/g, " ") + "^");
@@ -259,7 +277,9 @@ function run() {
return value instanceof UglifyJS.Dictionary ? value.toObject() : value; return value instanceof UglifyJS.Dictionary ? value.toObject() : value;
})); }));
} }
if (program.stats) console.error("Elapsed:", Date.now() - program.stats); if (result.timings) for (var phase in result.timings) {
console.error("- " + phase + ": " + result.timings[phase].toFixed(3) + "s");
}
} }
function fatal(message) { function fatal(message) {
@@ -349,7 +369,7 @@ function parse_js(flag, constants) {
} }
})); }));
} catch(ex) { } catch(ex) {
fatal("Error parsing arguments for '" + flag + "': " + value); options[value] = null;
} }
return options; return options;
} }

View File

@@ -157,7 +157,7 @@ var AST_SimpleStatement = DEFNODE("SimpleStatement", "body", {
function walk_body(node, visitor) { function walk_body(node, visitor) {
var body = node.body; var body = node.body;
if (body instanceof AST_Node) { if (body instanceof AST_Statement) {
body._walk(visitor); body._walk(visitor);
} }
else for (var i = 0, len = body.length; i < len; i++) { else for (var i = 0, len = body.length; i < len; i++) {
@@ -284,10 +284,6 @@ var AST_ForIn = DEFNODE("ForIn", "init name object", {
} }
}, AST_IterationStatement); }, AST_IterationStatement);
var AST_ForOf = DEFNODE("ForOf", null, {
$documentation: "A `for ... of` statement",
}, AST_ForIn);
var AST_With = DEFNODE("With", "expression", { var AST_With = DEFNODE("With", "expression", {
$documentation: "A `with` statement", $documentation: "A `with` statement",
$propdoc: { $propdoc: {
@@ -315,13 +311,6 @@ var AST_Scope = DEFNODE("Scope", "directives variables functions uses_with uses_
enclosed: "[SymbolDef*/S] a list of all symbol definitions that are accessed from this scope or any subscopes", enclosed: "[SymbolDef*/S] a list of all symbol definitions that are accessed from this scope or any subscopes",
cname: "[integer/S] current index for mangling variables (used internally by the mangler)", cname: "[integer/S] current index for mangling variables (used internally by the mangler)",
}, },
get_defun_scope: function () {
var self = this;
while (self.is_block_scope() && self.parent_scope) {
self = self.parent_scope;
}
return self;
}
}, AST_Block); }, AST_Block);
var AST_Toplevel = DEFNODE("Toplevel", "globals", { var AST_Toplevel = DEFNODE("Toplevel", "globals", {
@@ -342,111 +331,13 @@ var AST_Toplevel = DEFNODE("Toplevel", "globals", {
} }
}, AST_Scope); }, AST_Scope);
var AST_Expansion = DEFNODE("Expansion", "expression", { var AST_Lambda = DEFNODE("Lambda", "name argnames uses_arguments", {
$documentation: "An expandible argument, such as ...rest, a splat, such as [1,2,...all], or an expansion in a variable declaration, such as var [first, ...rest] = list",
$propdoc: {
expression: "AST_Symbol the thing to be expanded"
},
_walk: function(visitor) {
var self = this;
return visitor._visit(this, function(){
self.expression.walk(visitor);
});
}
});
var AST_ArrowParametersOrSeq = DEFNODE("ArrowParametersOrSeq", "expressions", {
$documentation: "A set of arrow function parameters or a sequence expression. This is used because when the parser sees a \"(\" it could be the start of a seq, or the start of a parameter list of an arrow function.",
$propdoc: {
expressions: "[AST_Expression|AST_Destructuring|AST_Expansion*] array of expressions or argument names or destructurings."
},
as_params: function (croak) {
// We don't want anything which doesn't belong in a destructuring
var root = this;
return this.expressions.map(function to_fun_args(ex, _, __, default_seen_above) {
var insert_default = function(ex, default_value) {
if (default_value) {
return new AST_DefaultAssign({
start: ex.start,
left: ex,
operator: "=",
right: default_value,
end: default_value.end
});
}
return ex;
}
if (ex instanceof AST_Object) {
return insert_default(new AST_Destructuring({
start: ex.start,
end: ex.end,
is_array: false,
names: ex.properties.map(to_fun_args)
}), default_seen_above);
} else if (ex instanceof AST_ObjectKeyVal) {
if (ex.key instanceof AST_SymbolRef) {
ex.key = to_fun_args(ex.key, 0, [ex.key]);
}
ex.value = to_fun_args(ex.value, 0, [ex.key]);
return insert_default(ex, default_seen_above);
} else if (ex instanceof AST_Hole) {
return ex;
} else if (ex instanceof AST_Destructuring) {
ex.names = ex.names.map(to_fun_args);
return insert_default(ex, default_seen_above);
} else if (ex instanceof AST_SymbolRef) {
return insert_default(new AST_SymbolFunarg({
name: ex.name,
start: ex.start,
end: ex.end
}), default_seen_above);
} else if (ex instanceof AST_Expansion) {
ex.expression = to_fun_args(ex.expression);
return insert_default(ex, default_seen_above);
} else if (ex instanceof AST_Array) {
return insert_default(new AST_Destructuring({
start: ex.start,
end: ex.end,
is_array: true,
names: ex.elements.map(to_fun_args)
}), default_seen_above);
} else if (ex instanceof AST_Assign) {
return insert_default(to_fun_args(ex.left, undefined, undefined, ex.right), default_seen_above);
} else if (ex instanceof AST_DefaultAssign) {
ex.left = to_fun_args(ex.left, 0, [ex.left]);
return ex;
} else {
croak("Invalid function parameter", ex.start.line, ex.start.col);
}
});
},
as_expr: function() {
var exprs = this.expressions;
return exprs.length == 1 ? exprs[0] : new AST_Sequence({
expressions: exprs
});
}
});
var AST_Lambda = DEFNODE("Lambda", "name argnames uses_arguments is_generator", {
$documentation: "Base class for functions", $documentation: "Base class for functions",
$propdoc: { $propdoc: {
is_generator: "[boolean] is generatorFn or not",
name: "[AST_SymbolDeclaration?] the name of this function", name: "[AST_SymbolDeclaration?] the name of this function",
argnames: "[AST_SymbolFunarg|AST_Destructuring|AST_Expansion|AST_DefaultAssign*] array of function arguments, destructurings, or expanding arguments", argnames: "[AST_SymbolFunarg*] array of function arguments",
uses_arguments: "[boolean/S] tells whether this function accesses the arguments array" uses_arguments: "[boolean/S] tells whether this function accesses the arguments array"
}, },
args_as_names: function () {
var out = [];
for (var i = 0; i < this.argnames.length; i++) {
if (this.argnames[i] instanceof AST_Destructuring) {
out = out.concat(this.argnames[i].all_symbols());
} else {
out.push(this.argnames[i]);
}
}
return out;
},
_walk: function(visitor) { _walk: function(visitor) {
return visitor._visit(this, function(){ return visitor._visit(this, function(){
if (this.name) this.name._walk(visitor); if (this.name) this.name._walk(visitor);
@@ -467,78 +358,10 @@ var AST_Function = DEFNODE("Function", null, {
$documentation: "A function expression" $documentation: "A function expression"
}, AST_Lambda); }, AST_Lambda);
var AST_Arrow = DEFNODE("Arrow", null, {
$documentation: "An ES6 Arrow function ((a) => b)"
}, AST_Lambda);
var AST_Defun = DEFNODE("Defun", null, { var AST_Defun = DEFNODE("Defun", null, {
$documentation: "A function definition" $documentation: "A function definition"
}, AST_Lambda); }, AST_Lambda);
/* -----[ DESTRUCTURING ]----- */
var AST_Destructuring = DEFNODE("Destructuring", "names is_array", {
$documentation: "A destructuring of several names. Used in destructuring assignment and with destructuring function argument names",
$propdoc: {
"names": "[AST_Node*] Array of properties or elements",
"is_array": "[Boolean] Whether the destructuring represents an object or array"
},
_walk: function(visitor) {
return visitor._visit(this, function(){
this.names.forEach(function(name){
name._walk(visitor);
});
});
},
all_symbols: function() {
var out = [];
this.walk(new TreeWalker(function (node) {
if (node instanceof AST_Symbol) {
out.push(node);
}
if (node instanceof AST_Expansion) {
out.push(node.expression);
}
}));
return out;
}
});
var AST_PrefixedTemplateString = DEFNODE("PrefixedTemplateString", "template_string prefix", {
$documentation: "A templatestring with a prefix, such as String.raw`foobarbaz`",
$propdoc: {
template_string: "[AST_TemplateString] The template string",
prefix: "[AST_SymbolRef|AST_PropAccess] The prefix, which can be a symbol such as `foo` or a dotted expression such as `String.raw`."
},
_walk: function(visitor) {
this.prefix._walk(visitor);
this.template_string._walk(visitor);
}
})
var AST_TemplateString = DEFNODE("TemplateString", "segments", {
$documentation: "A template string literal",
$propdoc: {
segments: "[AST_TemplateSegment|AST_Expression]* One or more segments, starting with AST_TemplateSegment. AST_Expression may follow AST_TemplateSegment, but each AST_Expression must be followed by AST_TemplateSegment."
},
_walk: function(visitor) {
return visitor._visit(this, function(){
this.segments.forEach(function(seg, i){
if (i % 2 !== 0) {
seg._walk(visitor);
}
});
});
}
});
var AST_TemplateSegment = DEFNODE("TemplateSegment", "value raw", {
$documentation: "A segment of a template string literal",
$propdoc: {
value: "Content of the segment",
raw: "Raw content of the segment"
}
});
/* -----[ JUMPS ]----- */ /* -----[ JUMPS ]----- */
var AST_Jump = DEFNODE("Jump", null, { var AST_Jump = DEFNODE("Jump", null, {
@@ -658,7 +481,7 @@ var AST_Try = DEFNODE("Try", "bcatch bfinally", {
var AST_Catch = DEFNODE("Catch", "argname", { var AST_Catch = DEFNODE("Catch", "argname", {
$documentation: "A `catch` node; only makes sense as part of a `try` statement", $documentation: "A `catch` node; only makes sense as part of a `try` statement",
$propdoc: { $propdoc: {
argname: "[AST_SymbolCatch|AST_Destructuring|AST_Expansion|AST_DefaultAssign] symbol for the exception" argname: "[AST_SymbolCatch] symbol for the exception"
}, },
_walk: function(visitor) { _walk: function(visitor) {
return visitor._visit(this, function(){ return visitor._visit(this, function(){
@@ -672,10 +495,10 @@ var AST_Finally = DEFNODE("Finally", null, {
$documentation: "A `finally` node; only makes sense as part of a `try` statement" $documentation: "A `finally` node; only makes sense as part of a `try` statement"
}, AST_Block); }, AST_Block);
/* -----[ VAR/CONST ]----- */ /* -----[ VAR ]----- */
var AST_Definitions = DEFNODE("Definitions", "definitions", { var AST_Definitions = DEFNODE("Definitions", "definitions", {
$documentation: "Base class for `var` or `const` nodes (variable declarations/initializations)", $documentation: "Base class for `var` nodes (variable declarations/initializations)",
$propdoc: { $propdoc: {
definitions: "[AST_VarDef*] array of variable definitions" definitions: "[AST_VarDef*] array of variable definitions"
}, },
@@ -693,75 +516,10 @@ var AST_Var = DEFNODE("Var", null, {
$documentation: "A `var` statement" $documentation: "A `var` statement"
}, AST_Definitions); }, AST_Definitions);
var AST_Let = DEFNODE("Let", null, {
$documentation: "A `let` statement"
}, AST_Definitions);
var AST_Const = DEFNODE("Const", null, {
$documentation: "A `const` statement"
}, AST_Definitions);
var AST_NameImport = DEFNODE("NameImport", "foreign_name name", {
$documentation: "The part of the import statement that imports names from a module.",
$propdoc: {
foreign_name: "[AST_SymbolImportForeign] The name being imported (as specified in the module)",
name: "[AST_SymbolImport] The name as it becomes available to this module."
},
_walk: function (visitor) {
return visitor._visit(this, function() {
this.foreign_name._walk(visitor);
this.name._walk(visitor);
});
}
})
var AST_Import = DEFNODE("Import", "imported_name imported_names module_name", {
$documentation: "An `import` statement",
$propdoc: {
imported_name: "[AST_SymbolImport] The name of the variable holding the module's default export.",
imported_names: "[AST_NameImport*] The names of non-default imported variables",
module_name: "[AST_String] String literal describing where this module came from",
},
_walk: function(visitor) {
return visitor._visit(this, function() {
if (this.imported_name) {
this.imported_name._walk(visitor);
}
if (this.imported_names) {
this.imported_names.forEach(function (name_import) {
name_import._walk(visitor);
});
}
this.module_name._walk(visitor);
});
}
});
var AST_Export = DEFNODE("Export", "exported_definition exported_value is_default exported_names module_name", {
$documentation: "An `export` statement",
$propdoc: {
exported_definition: "[AST_Defun|AST_Definitions|AST_DefClass?] An exported definition",
exported_value: "[AST_Node?] An exported value",
exported_names: "[AST_NameImport*?] List of exported names",
module_name: "[AST_String?] Name of the file to load exports from",
is_default: "[Boolean] Whether this is the default exported value of this module"
},
_walk: function (visitor) {
visitor._visit(this, function () {
if (this.exported_definition) {
this.exported_definition._walk(visitor);
}
if (this.exported_value) {
this.exported_value._walk(visitor);
}
});
}
}, AST_Statement);
var AST_VarDef = DEFNODE("VarDef", "name value", { var AST_VarDef = DEFNODE("VarDef", "name value", {
$documentation: "A variable declaration; only appears in a AST_Definitions node", $documentation: "A variable declaration; only appears in a AST_Definitions node",
$propdoc: { $propdoc: {
name: "[AST_SymbolVar|AST_SymbolConst|AST_Destructuring] name of the variable", name: "[AST_SymbolVar] name of the variable",
value: "[AST_Node?] initializer, or null of there's no initializer" value: "[AST_Node?] initializer, or null of there's no initializer"
}, },
_walk: function(visitor) { _walk: function(visitor) {
@@ -892,10 +650,6 @@ var AST_Assign = DEFNODE("Assign", null, {
$documentation: "An assignment expression — `a = b + 5`", $documentation: "An assignment expression — `a = b + 5`",
}, AST_Binary); }, AST_Binary);
var AST_DefaultAssign = DEFNODE("DefaultAssign", null, {
$documentation: "A default assignment expression like in `(a = 3) => a`"
}, AST_Binary);
/* -----[ LITERALS ]----- */ /* -----[ LITERALS ]----- */
var AST_Array = DEFNODE("Array", "elements", { var AST_Array = DEFNODE("Array", "elements", {
@@ -931,13 +685,11 @@ var AST_Object = DEFNODE("Object", "properties", {
var AST_ObjectProperty = DEFNODE("ObjectProperty", "key value", { var AST_ObjectProperty = DEFNODE("ObjectProperty", "key value", {
$documentation: "Base class for literal object properties", $documentation: "Base class for literal object properties",
$propdoc: { $propdoc: {
key: "[string|AST_Node] the property name converted to a string for ObjectKeyVal. For setters, getters and computed property this is an arbitrary AST_Node", key: "[string] the property name converted to a string for ObjectKeyVal. For setters and getters this is an AST_SymbolAccessor.",
value: "[AST_Node] property value. For setters and getters this is an AST_Function." value: "[AST_Node] property value. For setters and getters this is an AST_Accessor."
}, },
_walk: function(visitor) { _walk: function(visitor) {
return visitor._visit(this, function(){ return visitor._visit(this, function(){
if (this.key instanceof AST_Node)
this.key._walk(visitor);
this.value._walk(visitor); this.value._walk(visitor);
}); });
} }
@@ -950,72 +702,21 @@ var AST_ObjectKeyVal = DEFNODE("ObjectKeyVal", "quote", {
} }
}, AST_ObjectProperty); }, AST_ObjectProperty);
var AST_ObjectSetter = DEFNODE("ObjectSetter", "quote static", { var AST_ObjectSetter = DEFNODE("ObjectSetter", null, {
$propdoc: {
quote: "[string|undefined] the original quote character, if any",
static: "[boolean] whether this is a static setter (classes only)"
},
$documentation: "An object setter property", $documentation: "An object setter property",
}, AST_ObjectProperty); }, AST_ObjectProperty);
var AST_ObjectGetter = DEFNODE("ObjectGetter", "quote static", { var AST_ObjectGetter = DEFNODE("ObjectGetter", null, {
$propdoc: {
quote: "[string|undefined] the original quote character, if any",
static: "[boolean] whether this is a static getter (classes only)"
},
$documentation: "An object getter property", $documentation: "An object getter property",
}, AST_ObjectProperty); }, AST_ObjectProperty);
var AST_ConciseMethod = DEFNODE("ConciseMethod", "quote static is_generator", {
$propdoc: {
quote: "[string|undefined] the original quote character, if any",
static: "[boolean] whether this method is static (classes only)",
is_generator: "[boolean] is generatorFn or not",
},
$documentation: "An ES6 concise method inside an object or class"
}, AST_ObjectProperty);
var AST_Class = DEFNODE("Class", "name extends properties", {
$propdoc: {
name: "[AST_SymbolClass|AST_SymbolDefClass?] optional class name.",
extends: "[AST_Node]? optional parent class",
properties: "[AST_ObjectProperty*] array of properties"
},
$documentation: "An ES6 class",
_walk: function(visitor) {
return visitor._visit(this, function(){
if (this.name) {
this.name._walk(visitor);
}
if (this.extends) {
this.extends._walk(visitor);
}
this.properties.forEach(function(prop){
prop._walk(visitor);
});
});
},
}, AST_Scope);
var AST_DefClass = DEFNODE("DefClass", null, {
$documentation: "A class definition",
}, AST_Class);
var AST_ClassExpression = DEFNODE("ClassExpression", null, {
$documentation: "A class expression."
}, AST_Class);
var AST_Symbol = DEFNODE("Symbol", "scope name thedef", { var AST_Symbol = DEFNODE("Symbol", "scope name thedef", {
$propdoc: { $propdoc: {
name: "[string] name of this symbol", name: "[string] name of this symbol",
scope: "[AST_Scope/S] the current scope (not necessarily the definition scope)", scope: "[AST_Scope/S] the current scope (not necessarily the definition scope)",
thedef: "[SymbolDef/S] the definition of this symbol" thedef: "[SymbolDef/S] the definition of this symbol"
}, },
$documentation: "Base class for all symbols" $documentation: "Base class for all symbols",
});
var AST_NewTarget = DEFNODE("NewTarget", null, {
$documentation: "A reference to new.target"
}); });
var AST_SymbolAccessor = DEFNODE("SymbolAccessor", null, { var AST_SymbolAccessor = DEFNODE("SymbolAccessor", null, {
@@ -1023,25 +724,13 @@ var AST_SymbolAccessor = DEFNODE("SymbolAccessor", null, {
}, AST_Symbol); }, AST_Symbol);
var AST_SymbolDeclaration = DEFNODE("SymbolDeclaration", "init", { var AST_SymbolDeclaration = DEFNODE("SymbolDeclaration", "init", {
$documentation: "A declaration symbol (symbol in var/const, function name or argument, symbol in catch)", $documentation: "A declaration symbol (symbol in var, function name or argument, symbol in catch)",
}, AST_Symbol); }, AST_Symbol);
var AST_SymbolVar = DEFNODE("SymbolVar", null, { var AST_SymbolVar = DEFNODE("SymbolVar", null, {
$documentation: "Symbol defining a variable", $documentation: "Symbol defining a variable",
}, AST_SymbolDeclaration); }, AST_SymbolDeclaration);
var AST_SymbolBlockDeclaration = DEFNODE("SymbolBlockDeclaration", null, {
$documentation: "Base class for block-scoped declaration symbols"
}, AST_SymbolDeclaration);
var AST_SymbolConst = DEFNODE("SymbolConst", null, {
$documentation: "A constant declaration"
}, AST_SymbolBlockDeclaration);
var AST_SymbolLet = DEFNODE("SymbolLet", null, {
$documentation: "A block-scoped `let` declaration"
}, AST_SymbolBlockDeclaration);
var AST_SymbolFunarg = DEFNODE("SymbolFunarg", null, { var AST_SymbolFunarg = DEFNODE("SymbolFunarg", null, {
$documentation: "Symbol naming a function argument", $documentation: "Symbol naming a function argument",
}, AST_SymbolVar); }, AST_SymbolVar);
@@ -1050,33 +739,13 @@ var AST_SymbolDefun = DEFNODE("SymbolDefun", null, {
$documentation: "Symbol defining a function", $documentation: "Symbol defining a function",
}, AST_SymbolDeclaration); }, AST_SymbolDeclaration);
var AST_SymbolMethod = DEFNODE("SymbolMethod", null, {
$documentation: "Symbol in an object defining a method",
}, AST_Symbol);
var AST_SymbolLambda = DEFNODE("SymbolLambda", null, { var AST_SymbolLambda = DEFNODE("SymbolLambda", null, {
$documentation: "Symbol naming a function expression", $documentation: "Symbol naming a function expression",
}, AST_SymbolDeclaration); }, AST_SymbolDeclaration);
var AST_SymbolDefClass = DEFNODE("SymbolDefClass", null, {
$documentation: "Symbol naming a class's name in a class declaration. Lexically scoped to its containing scope, and accessible within the class."
}, AST_SymbolBlockDeclaration);
var AST_SymbolClass = DEFNODE("SymbolClass", null, {
$documentation: "Symbol naming a class's name. Lexically scoped to the class."
}, AST_SymbolDeclaration);
var AST_SymbolCatch = DEFNODE("SymbolCatch", null, { var AST_SymbolCatch = DEFNODE("SymbolCatch", null, {
$documentation: "Symbol naming the exception in catch", $documentation: "Symbol naming the exception in catch",
}, AST_SymbolBlockDeclaration); }, AST_SymbolDeclaration);
var AST_SymbolImport = DEFNODE("SymbolImport", null, {
$documentation: "Symbol refering to an imported name",
}, AST_SymbolBlockDeclaration);
var AST_SymbolImportForeign = DEFNODE("SymbolImportForeign", null, {
$documentation: "A symbol imported from a module, but it is defined in the other module, and its real name is irrelevant for this module's purposes",
}, AST_Symbol);
var AST_Label = DEFNODE("Label", "references", { var AST_Label = DEFNODE("Label", "references", {
$documentation: "Symbol naming a label (declaration)", $documentation: "Symbol naming a label (declaration)",
@@ -1101,10 +770,6 @@ var AST_This = DEFNODE("This", null, {
$documentation: "The `this` symbol", $documentation: "The `this` symbol",
}, AST_Symbol); }, AST_Symbol);
var AST_Super = DEFNODE("Super", null, {
$documentation: "The `super` symbol",
}, AST_Symbol);
var AST_Constant = DEFNODE("Constant", null, { var AST_Constant = DEFNODE("Constant", null, {
$documentation: "Base class for all constants", $documentation: "Base class for all constants",
getValue: function() { getValue: function() {
@@ -1178,21 +843,6 @@ var AST_True = DEFNODE("True", null, {
value: true value: true
}, AST_Boolean); }, AST_Boolean);
/* -----[ Yield ]----- */
var AST_Yield = DEFNODE("Yield", "expression is_star", {
$documentation: "A `yield` statement",
$propdoc: {
expression: "[AST_Node?] the value returned or thrown by this statement; could be null (representing undefined) but only when is_star is set to false",
is_star: "[Boolean] Whether this is a yield or yield* statement"
},
_walk: function(visitor) {
return visitor._visit(this, this.expression && function(){
this.expression._walk(visitor);
});
}
});
/* -----[ TreeWalker ]----- */ /* -----[ TreeWalker ]----- */
function TreeWalker(callback) { function TreeWalker(callback) {
@@ -1220,17 +870,12 @@ TreeWalker.prototype = {
this.directives = Object.create(this.directives); this.directives = Object.create(this.directives);
} else if (node instanceof AST_Directive && !this.directives[node.value]) { } else if (node instanceof AST_Directive && !this.directives[node.value]) {
this.directives[node.value] = node; this.directives[node.value] = node;
} else if (node instanceof AST_Class) {
this.directives = Object.create(this.directives);
if (!this.directives["use strict"]) {
this.directives["use strict"] = node;
}
} }
this.stack.push(node); this.stack.push(node);
}, },
pop: function(node) { pop: function(node) {
this.stack.pop(); this.stack.pop();
if (node instanceof AST_Lambda || node instanceof AST_Class) { if (node instanceof AST_Lambda) {
this.directives = Object.getPrototypeOf(this.directives); this.directives = Object.getPrototypeOf(this.directives);
} }
}, },
@@ -1248,7 +893,7 @@ TreeWalker.prototype = {
var dir = this.directives[type]; var dir = this.directives[type];
if (dir) return dir; if (dir) return dir;
var node = this.stack[this.stack.length - 1]; var node = this.stack[this.stack.length - 1];
if (node instanceof AST_Scope && node.body) { if (node instanceof AST_Scope) {
for (var i = 0; i < node.body.length; ++i) { for (var i = 0; i < node.body.length; ++i) {
var st = node.body[i]; var st = node.body[i];
if (!(st instanceof AST_Directive)) break; if (!(st instanceof AST_Directive)) break;

File diff suppressed because it is too large Load Diff

View File

@@ -30,9 +30,6 @@ function set_shorthand(name, options, keys) {
function minify(files, options) { function minify(files, options) {
var warn_function = AST_Node.warn_function; var warn_function = AST_Node.warn_function;
try { try {
if (typeof files == "string") {
files = [ files ];
}
options = defaults(options, { options = defaults(options, {
compress: {}, compress: {},
ie8: false, ie8: false,
@@ -41,10 +38,14 @@ function minify(files, options) {
output: {}, output: {},
parse: {}, parse: {},
sourceMap: false, sourceMap: false,
timings: false,
toplevel: false, toplevel: false,
warnings: false, warnings: false,
wrap: false, wrap: false,
}, true); }, true);
var timings = options.timings && {
start: Date.now()
};
set_shorthand("ie8", options, [ "compress", "mangle", "output" ]); set_shorthand("ie8", options, [ "compress", "mangle", "output" ]);
set_shorthand("keep_fnames", options, [ "compress", "mangle" ]); set_shorthand("keep_fnames", options, [ "compress", "mangle" ]);
set_shorthand("toplevel", options, [ "compress", "mangle" ]); set_shorthand("toplevel", options, [ "compress", "mangle" ]);
@@ -57,7 +58,6 @@ function minify(files, options) {
keep_fnames: false, keep_fnames: false,
properties: false, properties: false,
reserved: [], reserved: [],
safari10: false,
toplevel: false, toplevel: false,
}, true); }, true);
} }
@@ -76,10 +76,14 @@ function minify(files, options) {
warnings.push(warning); warnings.push(warning);
}; };
} }
if (timings) timings.parse = Date.now();
var toplevel; var toplevel;
if (files instanceof AST_Toplevel) { if (files instanceof AST_Toplevel) {
toplevel = files; toplevel = files;
} else { } else {
if (typeof files == "string") {
files = [ files ];
}
options.parse = options.parse || {}; options.parse = options.parse || {};
options.parse.toplevel = null; options.parse.toplevel = null;
for (var name in files) { for (var name in files) {
@@ -96,19 +100,23 @@ function minify(files, options) {
if (options.wrap) { if (options.wrap) {
toplevel = toplevel.wrap_commonjs(options.wrap); toplevel = toplevel.wrap_commonjs(options.wrap);
} }
if (options.compress) { if (timings) timings.scope1 = Date.now();
toplevel.figure_out_scope(options.mangle); if (options.compress) toplevel.figure_out_scope(options.mangle);
toplevel = new Compressor(options.compress).compress(toplevel); if (timings) timings.compress = Date.now();
} if (options.compress) toplevel = new Compressor(options.compress).compress(toplevel);
if (timings) timings.scope2 = Date.now();
if (options.mangle) toplevel.figure_out_scope(options.mangle);
if (timings) timings.mangle = Date.now();
if (options.mangle) { if (options.mangle) {
toplevel.figure_out_scope(options.mangle);
base54.reset(); base54.reset();
toplevel.compute_char_frequency(options.mangle); toplevel.compute_char_frequency(options.mangle);
toplevel.mangle_names(options.mangle); toplevel.mangle_names(options.mangle);
if (options.mangle.properties) { }
if (timings) timings.properties = Date.now();
if (options.mangle && options.mangle.properties) {
toplevel = mangle_properties(toplevel, options.mangle.properties); toplevel = mangle_properties(toplevel, options.mangle.properties);
} }
} if (timings) timings.output = Date.now();
var result = {}; var result = {};
if (options.output.ast) { if (options.output.ast) {
result.ast = toplevel; result.ast = toplevel;
@@ -124,7 +132,9 @@ function minify(files, options) {
root: options.sourceMap.root root: options.sourceMap.root
}); });
if (options.sourceMap.includeSources) { if (options.sourceMap.includeSources) {
for (var name in files) { if (files instanceof AST_Toplevel) {
throw new Error("original source content unavailable");
} else for (var name in files) {
options.output.source_map.get().setSourceContent(name, files[name]); options.output.source_map.get().setSourceContent(name, files[name]);
} }
} }
@@ -143,6 +153,18 @@ function minify(files, options) {
} }
} }
} }
if (timings) {
timings.end = Date.now();
result.timings = {
parse: 1e-3 * (timings.scope1 - timings.parse),
scope: 1e-3 * (timings.compress - timings.scope1 + timings.mangle - timings.scope2),
compress: 1e-3 * (timings.scope2 - timings.compress),
mangle: 1e-3 * (timings.properties - timings.mangle),
properties: 1e-3 * (timings.output - timings.properties),
output: 1e-3 * (timings.end - timings.output),
total: 1e-3 * (timings.end - timings.start)
}
}
if (warnings.length) { if (warnings.length) {
result.warnings = warnings; result.warnings = warnings;
} }

View File

@@ -111,23 +111,19 @@
}, },
Property: function(M) { Property: function(M) {
var key = M.key; var key = M.key;
var name = key.type == "Identifier" ? key.name : key.value;
var args = { var args = {
start : my_start_token(key), start : my_start_token(key),
end : my_end_token(M.value), end : my_end_token(M.value),
key : name, key : key.type == "Identifier" ? key.name : key.value,
value : from_moz(M.value) value : from_moz(M.value)
}; };
switch (M.kind) { if (M.kind == "init") return new AST_ObjectKeyVal(args);
case "init": args.key = new AST_SymbolAccessor({
return new AST_ObjectKeyVal(args); name: args.key
case "set": });
args.value.name = from_moz(key); args.value = new AST_Accessor(args.value);
return new AST_ObjectSetter(args); if (M.kind == "get") return new AST_ObjectGetter(args);
case "get": if (M.kind == "set") return new AST_ObjectSetter(args);
args.value.name = from_moz(key);
return new AST_ObjectGetter(args);
}
}, },
ArrayExpression: function(M) { ArrayExpression: function(M) {
return new AST_Array({ return new AST_Array({
@@ -172,7 +168,7 @@
}); });
}, },
VariableDeclaration: function(M) { VariableDeclaration: function(M) {
return new (M.kind === "const" ? AST_Const : AST_Var)({ return new AST_Var({
start : my_start_token(M), start : my_start_token(M),
end : my_end_token(M), end : my_end_token(M),
definitions : M.declarations.map(from_moz) definitions : M.declarations.map(from_moz)
@@ -208,7 +204,7 @@
Identifier: function(M) { Identifier: function(M) {
var p = FROM_MOZ_STACK[FROM_MOZ_STACK.length - 2]; var p = FROM_MOZ_STACK[FROM_MOZ_STACK.length - 2];
return new ( p.type == "LabeledStatement" ? AST_Label return new ( p.type == "LabeledStatement" ? AST_Label
: p.type == "VariableDeclarator" && p.id === M ? (p.kind == "const" ? AST_SymbolConst : AST_SymbolVar) : p.type == "VariableDeclarator" && p.id === M ? AST_SymbolVar
: p.type == "FunctionExpression" ? (p.id === M ? AST_SymbolLambda : AST_SymbolFunarg) : p.type == "FunctionExpression" ? (p.id === M ? AST_SymbolLambda : AST_SymbolFunarg)
: p.type == "FunctionDeclaration" ? (p.id === M ? AST_SymbolDefun : AST_SymbolFunarg) : p.type == "FunctionDeclaration" ? (p.id === M ? AST_SymbolDefun : AST_SymbolFunarg)
: p.type == "CatchClause" ? AST_SymbolCatch : p.type == "CatchClause" ? AST_SymbolCatch
@@ -260,10 +256,7 @@
map("CallExpression", AST_Call, "callee>expression, arguments@args"); map("CallExpression", AST_Call, "callee>expression, arguments@args");
def_to_moz(AST_Toplevel, function To_Moz_Program(M) { def_to_moz(AST_Toplevel, function To_Moz_Program(M) {
return { return to_moz_scope("Program", M);
type: "Program",
body: M.body.map(to_moz)
};
}); });
def_to_moz(AST_Defun, function To_Moz_FunctionDeclaration(M) { def_to_moz(AST_Defun, function To_Moz_FunctionDeclaration(M) {
@@ -271,7 +264,7 @@
type: "FunctionDeclaration", type: "FunctionDeclaration",
id: to_moz(M.name), id: to_moz(M.name),
params: M.argnames.map(to_moz), params: M.argnames.map(to_moz),
body: to_moz_block(M) body: to_moz_scope("BlockStatement", M)
} }
}); });
@@ -280,7 +273,7 @@
type: "FunctionExpression", type: "FunctionExpression",
id: to_moz(M.name), id: to_moz(M.name),
params: M.argnames.map(to_moz), params: M.argnames.map(to_moz),
body: to_moz_block(M) body: to_moz_scope("BlockStatement", M)
} }
}); });
@@ -331,7 +324,7 @@
def_to_moz(AST_Definitions, function To_Moz_VariableDeclaration(M) { def_to_moz(AST_Definitions, function To_Moz_VariableDeclaration(M) {
return { return {
type: "VariableDeclaration", type: "VariableDeclaration",
kind: M instanceof AST_Const ? "const" : "var", kind: "var",
declarations: M.definitions.map(to_moz) declarations: M.definitions.map(to_moz)
}; };
}); });
@@ -386,11 +379,10 @@
}); });
def_to_moz(AST_ObjectProperty, function To_Moz_Property(M) { def_to_moz(AST_ObjectProperty, function To_Moz_Property(M) {
var key = ( var key = {
is_identifier(M.key) type: "Literal",
? {type: "Identifier", name: M.key} value: M.key instanceof AST_SymbolAccessor ? M.key.name : M.key
: {type: "Literal", value: M.key} };
);
var kind; var kind;
if (M instanceof AST_ObjectKeyVal) { if (M instanceof AST_ObjectKeyVal) {
kind = "init"; kind = "init";
@@ -551,8 +543,8 @@
moz_to_me = new Function("U2", "my_start_token", "my_end_token", "from_moz", "return(" + moz_to_me + ")")( moz_to_me = new Function("U2", "my_start_token", "my_end_token", "from_moz", "return(" + moz_to_me + ")")(
exports, my_start_token, my_end_token, from_moz exports, my_start_token, my_end_token, from_moz
); );
me_to_moz = new Function("to_moz", "to_moz_block", "return(" + me_to_moz + ")")( me_to_moz = new Function("to_moz", "to_moz_block", "to_moz_scope", "return(" + me_to_moz + ")")(
to_moz, to_moz_block to_moz, to_moz_block, to_moz_scope
); );
MOZ_TO_ME[moztype] = moz_to_me; MOZ_TO_ME[moztype] = moz_to_me;
def_to_moz(mytype, me_to_moz); def_to_moz(mytype, me_to_moz);
@@ -610,4 +602,14 @@
}; };
}; };
function to_moz_scope(type, node) {
var body = node.body.map(to_moz);
if (node.body[0] instanceof AST_SimpleStatement && node.body[0].body instanceof AST_String) {
body.unshift(to_moz(new AST_EmptyStatement(node.body[0])));
}
return {
type: type,
body: body
};
};
})(); })();

View File

@@ -54,11 +54,9 @@ function OutputStream(options) {
options = defaults(options, { options = defaults(options, {
ascii_only : false, ascii_only : false,
ascii_identifiers: undefined,
beautify : false, beautify : false,
bracketize : false, bracketize : false,
comments : false, comments : false,
ecma : 5,
ie8 : false, ie8 : false,
indent_level : 4, indent_level : 4,
indent_start : 0, indent_start : 0,
@@ -71,20 +69,11 @@ function OutputStream(options) {
quote_style : 0, quote_style : 0,
semicolons : true, semicolons : true,
shebang : true, shebang : true,
shorthand : undefined,
source_map : null, source_map : null,
space_colon : true,
unescape_regexps : false,
width : 80, width : 80,
wrap_iife : false, wrap_iife : false,
}, true); }, true);
if (typeof options.ascii_identifiers === 'undefined')
options.ascii_identifiers = options.ascii_only;
if (options.shorthand === undefined)
options.shorthand = options.ecma > 5;
// Convert comment option to RegExp if neccessary and set up comments filter // Convert comment option to RegExp if neccessary and set up comments filter
var comment_filter = return_false; // Default case, throw all comments away var comment_filter = return_false; // Default case, throw all comments away
if (options.comments) { if (options.comments) {
@@ -120,19 +109,9 @@ function OutputStream(options) {
var OUTPUT = ""; var OUTPUT = "";
function to_ascii(str, identifier) { function to_ascii(str, identifier) {
return str.replace(/[\ud800-\udbff][\udc00-\udfff]|[\u0000-\u001f\u007f-\uffff]/g, function(ch) { return str.replace(/[\u0000-\u001f\u007f-\uffff]/g, function(ch) {
var code = get_full_char_code(ch, 0).toString(16); var code = ch.charCodeAt(0).toString(16);
if (code.length <= 2 && !identifier) {
if ((identifier && code.length === 1 && options.ecma >= 6) || code.length > 4) {
if (options.ecma < 6) {
if (identifier) {
return ch; // no \u{} support
}
return "\\u" + ch.charCodeAt(0).toString(16) + "\\u"
+ ch.charCodeAt(1).toString(16);
}
return "\\u{" + code + "}";
} else if (code.length <= 2 && !identifier) {
while (code.length < 2) code = "0" + code; while (code.length < 2) code = "0" + code;
return "\\x" + code; return "\\x" + code;
} else { } else {
@@ -160,7 +139,7 @@ function OutputStream(options) {
case "\u2029": return "\\u2029"; case "\u2029": return "\\u2029";
case "\ufeff": return "\\ufeff"; case "\ufeff": return "\\ufeff";
case "\0": case "\0":
return /[0-7]/.test(get_full_char(str, i+1)) ? "\\x00" : "\\0"; return /[0-7]/.test(str.charAt(i+1)) ? "\\x00" : "\\0";
} }
return s; return s;
}); });
@@ -170,11 +149,7 @@ function OutputStream(options) {
function quote_double() { function quote_double() {
return '"' + str.replace(/\x22/g, '\\"') + '"'; return '"' + str.replace(/\x22/g, '\\"') + '"';
} }
function quote_template() {
return '`' + str.replace(/`/g, '\\`') + '`';
}
if (options.ascii_only) str = to_ascii(str); if (options.ascii_only) str = to_ascii(str);
if (quote === "`") return quote_template();
switch (options.quote_style) { switch (options.quote_style) {
case 1: case 1:
return quote_single(); return quote_single();
@@ -199,7 +174,7 @@ function OutputStream(options) {
function make_name(name) { function make_name(name) {
name = name.toString(); name = name.toString();
if (options.ascii_identifiers) if (options.ascii_only)
name = to_ascii(name, true); name = to_ascii(name, true);
return name; return name;
}; };
@@ -214,12 +189,43 @@ function OutputStream(options) {
var might_need_semicolon = false; var might_need_semicolon = false;
var might_add_newline = 0; var might_add_newline = 0;
var last = ""; var last = "";
var mapping_token, mapping_name, mappings = options.source_map && [];
var do_add_mapping = mappings ? function() {
mappings.forEach(function(mapping) {
try {
options.source_map.add(
mapping.token.file,
mapping.line, mapping.col,
mapping.token.line, mapping.token.col,
!mapping.name && mapping.token.type == "name" ? mapping.token.value : mapping.name
);
} catch(ex) {
AST_Node.warn("Couldn't figure out mapping for {file}:{line},{col} → {cline},{ccol} [{name}]", {
file: mapping.token.file,
line: mapping.token.line,
col: mapping.token.col,
cline: mapping.line,
ccol: mapping.col,
name: mapping.name || ""
})
}
});
mappings = [];
} : noop;
var ensure_line_len = options.max_line_len ? function() { var ensure_line_len = options.max_line_len ? function() {
if (current_col > options.max_line_len) { if (current_col > options.max_line_len) {
if (might_add_newline) { if (might_add_newline) {
var left = OUTPUT.slice(0, might_add_newline); var left = OUTPUT.slice(0, might_add_newline);
var right = OUTPUT.slice(might_add_newline); var right = OUTPUT.slice(might_add_newline);
if (mappings) {
var delta = right.length - current_col;
mappings.forEach(function(mapping) {
mapping.line++;
mapping.col += delta;
});
}
OUTPUT = left + "\n" + right; OUTPUT = left + "\n" + right;
current_line++; current_line++;
current_pos++; current_pos++;
@@ -229,15 +235,18 @@ function OutputStream(options) {
AST_Node.warn("Output exceeds {max_line_len} characters", options); AST_Node.warn("Output exceeds {max_line_len} characters", options);
} }
} }
if (might_add_newline) {
might_add_newline = 0; might_add_newline = 0;
do_add_mapping();
}
} : noop; } : noop;
var requireSemicolonChars = makePredicate("( [ + * / - , ."); var requireSemicolonChars = makePredicate("( [ + * / - , .");
function print(str) { function print(str) {
str = String(str); str = String(str);
var ch = get_full_char(str, 0); var ch = str.charAt(0);
var prev = get_full_char(last, last.length - 1); var prev = last.charAt(last.length - 1);
if (might_need_semicolon) { if (might_need_semicolon) {
might_need_semicolon = false; might_need_semicolon = false;
@@ -289,6 +298,18 @@ function OutputStream(options) {
} }
might_need_space = false; might_need_space = false;
} }
if (mapping_token) {
mappings.push({
token: mapping_token,
name: mapping_name,
line: current_line,
col: current_col
});
mapping_token = false;
if (!might_add_newline) do_add_mapping();
}
OUTPUT += str; OUTPUT += str;
current_pos += str.length; current_pos += str.length;
var a = str.split(/\r?\n/), n = a.length - 1; var a = str.split(/\r?\n/), n = a.length - 1;
@@ -301,10 +322,6 @@ function OutputStream(options) {
last = str; last = str;
}; };
var star = function(){
print("*");
}
var space = options.beautify ? function() { var space = options.beautify ? function() {
print(" "); print(" ");
} : function() { } : function() {
@@ -384,27 +401,12 @@ function OutputStream(options) {
function colon() { function colon() {
print(":"); print(":");
if (options.space_colon) space(); space();
}; };
var add_mapping = options.source_map ? function(token, name) { var add_mapping = mappings ? function(token, name) {
try { mapping_token = token;
if (token) options.source_map.add( mapping_name = name;
token.file || "?",
current_line, current_col,
token.line, token.col,
(!name && token.type == "name") ? token.value : name
);
} catch(ex) {
AST_Node.warn("Couldn't figure out mapping for {file}:{line},{col} → {cline},{ccol} [{name}]", {
file: token.file,
line: token.line,
col: token.col,
cline: current_line,
ccol: current_col,
name: name || ""
})
}
} : noop; } : noop;
function get() { function get() {
@@ -424,7 +426,6 @@ function OutputStream(options) {
should_break : function() { return options.width && this.current_width() >= options.width }, should_break : function() { return options.width && this.current_width() >= options.width },
newline : newline, newline : newline,
print : print, print : print,
star : star,
space : space, space : space,
comma : comma, comma : comma,
colon : colon, colon : colon,
@@ -444,10 +445,6 @@ function OutputStream(options) {
} }
print(encoded); print(encoded);
}, },
print_template_string_chars: function(str) {
var encoded = encode_string(str, '`').replace(/\${/g, "\\${");
return print(encoded.substr(1, encoded.length - 2));
},
encode_string : encode_string, encode_string : encode_string,
next_indent : next_indent, next_indent : next_indent,
with_indent : with_indent, with_indent : with_indent,
@@ -608,11 +605,6 @@ function OutputStream(options) {
return false; return false;
}); });
PARENS(AST_Arrow, function(output){
var p = output.parent();
return p instanceof AST_PropAccess && p.expression === this;
});
// same goes for an object literal, because otherwise it would be // same goes for an object literal, because otherwise it would be
// interpreted as a block of code. // interpreted as a block of code.
PARENS(AST_Object, function(output){ PARENS(AST_Object, function(output){
@@ -622,13 +614,7 @@ function OutputStream(options) {
PARENS(AST_Unary, function(output){ PARENS(AST_Unary, function(output){
var p = output.parent(); var p = output.parent();
return p instanceof AST_PropAccess && p.expression === this return p instanceof AST_PropAccess && p.expression === this
|| p instanceof AST_Call && p.expression === this || p instanceof AST_Call && p.expression === this;
|| p instanceof AST_Binary
&& p.operator === "**"
&& this instanceof AST_UnaryPrefix
&& p.left === this
&& this.operator !== "++"
&& this.operator !== "--";
}); });
PARENS(AST_Sequence, function(output){ PARENS(AST_Sequence, function(output){
@@ -642,9 +628,6 @@ function OutputStream(options) {
|| p instanceof AST_ObjectProperty // { foo: (1, 2) }.foo ==> 2 || p instanceof AST_ObjectProperty // { foo: (1, 2) }.foo ==> 2
|| p instanceof AST_Conditional /* (false, true) ? (a = 10, b = 20) : (c = 30) || p instanceof AST_Conditional /* (false, true) ? (a = 10, b = 20) : (c = 30)
* ==> 20 (side effect, set a := 10 and b := 20) */ * ==> 20 (side effect, set a := 10 and b := 20) */
|| p instanceof AST_Arrow // x => (x, x)
|| p instanceof AST_DefaultAssign // x => (x = (0, function(){}))
|| (p instanceof AST_Class && p.extends === this) // class D extends (calls++, C) {}
; ;
}); });
@@ -671,24 +654,6 @@ function OutputStream(options) {
} }
}); });
PARENS(AST_Yield, function(output){
var p = output.parent();
// (yield 1) + (yield 2)
// a = yield 3
if (p instanceof AST_Binary && p.operator !== "=")
return true;
// (yield 1) ? yield 2 : yield 3
if (p instanceof AST_Conditional && p.condition === this)
return true;
// -(yield 4)
if (p instanceof AST_Unary)
return true;
// (yield x).foo
// (yield x)['foo']
if (p instanceof AST_PropAccess && p.expression === this)
return true;
});
PARENS(AST_PropAccess, function(output){ PARENS(AST_PropAccess, function(output){
var p = output.parent(); var p = output.parent();
if (p instanceof AST_New && p.expression === this) { if (p instanceof AST_New && p.expression === this) {
@@ -758,9 +723,6 @@ function OutputStream(options) {
// (a = foo)["prop"] —or— (a = foo).prop // (a = foo)["prop"] —or— (a = foo).prop
if (p instanceof AST_PropAccess && p.expression === this) if (p instanceof AST_PropAccess && p.expression === this)
return true; return true;
// ({a, b} = {a: 1, b: 2}), a destructuring assignment
if (this instanceof AST_Assign && this.left instanceof AST_Destructuring && this.left.is_array === false)
return true;
}); });
/* -----[ PRINTERS ]----- */ /* -----[ PRINTERS ]----- */
@@ -769,28 +731,6 @@ function OutputStream(options) {
output.print_string(self.value, self.quote); output.print_string(self.value, self.quote);
output.semicolon(); output.semicolon();
}); });
DEFPRINT(AST_Expansion, function (self, output) {
output.print('...');
self.expression.print(output);
});
DEFPRINT(AST_Destructuring, function (self, output) {
output.print(self.is_array ? "[" : "{");
var first = true;
var len = self.names.length;
self.names.forEach(function (name, i) {
if (first) first = false; else { output.comma(); output.space(); }
name.print(output);
// If the final element is a hole, we need to make sure it
// doesn't look like a trailing comma, by inserting an actual
// trailing comma.
if (i === len - 1 && name instanceof AST_Hole)
output.comma();
})
output.print(self.is_array ? "]" : "}");
});
DEFPRINT(AST_Debugger, function(self, output){ DEFPRINT(AST_Debugger, function(self, output){
output.print("debugger"); output.print("debugger");
output.semicolon(); output.semicolon();
@@ -915,11 +855,7 @@ function OutputStream(options) {
output.with_parens(function(){ output.with_parens(function(){
self.init.print(output); self.init.print(output);
output.space(); output.space();
if (self instanceof AST_ForOf) {
output.print("of");
} else {
output.print("in"); output.print("in");
}
output.space(); output.space();
self.object.print(output); self.object.print(output);
}); });
@@ -941,19 +877,10 @@ function OutputStream(options) {
var self = this; var self = this;
if (!nokeyword) { if (!nokeyword) {
output.print("function"); output.print("function");
if (this.is_generator) {
output.star();
} }
if (self.name) { if (self.name) {
output.space(); output.space();
}
}
if (self.name instanceof AST_Symbol) {
self.name.print(output); self.name.print(output);
} else if (nokeyword && self.name instanceof AST_Node) {
output.with_square(function() {
self.name.print(output); // Computed method name
});
} }
output.with_parens(function(){ output.with_parens(function(){
self.argnames.forEach(function(arg, i){ self.argnames.forEach(function(arg, i){
@@ -968,56 +895,6 @@ function OutputStream(options) {
self._do_print(output); self._do_print(output);
}); });
DEFPRINT(AST_PrefixedTemplateString, function(self, output) {
self.prefix.print(output);
self.template_string.print(output);
});
DEFPRINT(AST_TemplateString, function(self, output) {
var is_tagged = output.parent() instanceof AST_PrefixedTemplateString;
output.print("`");
for (var i = 0; i < self.segments.length; i++) {
if (!(self.segments[i] instanceof AST_TemplateSegment)) {
output.print("${");
self.segments[i].print(output);
output.print("}");
} else if (is_tagged) {
output.print(self.segments[i].raw);
} else {
output.print_template_string_chars(self.segments[i].value);
}
}
output.print("`");
});
AST_Arrow.DEFMETHOD("_do_print", function(output){
var self = this;
var parent = output.parent();
var needs_parens = parent instanceof AST_Binary ||
parent instanceof AST_Unary ||
(parent instanceof AST_Call && self === parent.expression);
if (needs_parens) { output.print("(") }
if (self.argnames.length === 1 && self.argnames[0] instanceof AST_Symbol) {
self.argnames[0].print(output);
} else {
output.with_parens(function(){
self.argnames.forEach(function(arg, i){
if (i) output.comma();
arg.print(output);
});
});
}
output.space();
output.print('=>');
output.space();
if (self.body instanceof AST_Node) {
this.body.print(output);
} else {
print_bracketed(this.body, output);
}
if (needs_parens) { output.print(")") }
});
/* -----[ exits ]----- */ /* -----[ exits ]----- */
AST_Exit.DEFMETHOD("_do_print", function(output, kind){ AST_Exit.DEFMETHOD("_do_print", function(output, kind){
output.print(kind); output.print(kind);
@@ -1034,17 +911,6 @@ function OutputStream(options) {
self._do_print(output, "throw"); self._do_print(output, "throw");
}); });
/* -----[ yield ]----- */
DEFPRINT(AST_Yield, function(self, output){
var star = self.is_star ? "*" : "";
output.print("yield" + star);
if (self.expression) {
output.space();
self.expression.print(output);
}
});
/* -----[ loop control ]----- */ /* -----[ loop control ]----- */
AST_LoopControl.DEFMETHOD("_do_print", function(output, kind){ AST_LoopControl.DEFMETHOD("_do_print", function(output, kind){
output.print(kind); output.print(kind);
@@ -1193,107 +1059,9 @@ function OutputStream(options) {
if (!avoid_semicolon) if (!avoid_semicolon)
output.semicolon(); output.semicolon();
}); });
DEFPRINT(AST_Let, function(self, output){
self._do_print(output, "let");
});
DEFPRINT(AST_Var, function(self, output){ DEFPRINT(AST_Var, function(self, output){
self._do_print(output, "var"); self._do_print(output, "var");
}); });
DEFPRINT(AST_Const, function(self, output){
self._do_print(output, "const");
});
DEFPRINT(AST_Import, function(self, output) {
output.print("import");
output.space();
if (self.imported_name) {
self.imported_name.print(output);
}
if (self.imported_name && self.imported_names) {
output.print(",");
output.space();
}
if (self.imported_names) {
if (self.imported_names.length === 1 && self.imported_names[0].foreign_name.name === "*") {
self.imported_names[0].print(output);
} else {
output.print("{");
self.imported_names.forEach(function (name_import, i) {
output.space();
name_import.print(output);
if (i < self.imported_names.length - 1) {
output.print(",");
output.space();
}
});
output.space();
output.print("}");
}
}
if (self.imported_name || self.imported_names) {
output.space();
output.print("from")
output.space();
}
self.module_name.print(output);
output.semicolon();
});
DEFPRINT(AST_NameImport, function(self, output) {
var definition = self.name.definition();
var names_are_different =
(definition && definition.mangled_name || self.name.name) !==
self.foreign_name.name;
if (names_are_different) {
output.print(self.foreign_name.name);
output.space();
output.print("as");
output.space();
self.name.print(output);
} else {
self.name.print(output);
}
});
DEFPRINT(AST_Export, function(self, output) {
output.print("export");
output.space();
if (self.is_default) {
output.print("default");
output.space();
}
if (self.exported_names) {
output.space();
if (self.exported_names.length === 1 && self.exported_names[0].name.name === "*") {
self.exported_names[0].print(output);
} else {
output.print("{");
self.exported_names.forEach(function (name_import, i) {
output.space();
name_import.print(output);
if (i < self.exported_names.length - 1) {
output.print(",");
output.space();
}
});
output.space();
output.print("}");
}
output.space();
}
else if (self.exported_value) {
self.exported_value.print(output);
} else if (self.exported_definition) {
self.exported_definition.print(output);
}
if (self.module_name) {
output.space();
output.print("from");
output.space();
self.module_name.print(output);
}
output.semicolon();
});
function parenthesize_for_noin(node, output, noin) { function parenthesize_for_noin(node, output, noin) {
if (!noin) node.print(output); if (!noin) node.print(output);
@@ -1466,35 +1234,8 @@ function OutputStream(options) {
}); });
else output.print("{}"); else output.print("{}");
}); });
DEFPRINT(AST_Class, function(self, output){
output.print("class"); function print_property_name(key, quote, output) {
output.space();
if (self.name) {
self.name.print(output);
output.space();
}
if (self.extends) {
output.print("extends");
output.space();
self.extends.print(output);
output.space();
}
if (self.properties.length > 0) output.with_block(function(){
self.properties.forEach(function(prop, i){
if (i) {
output.newline();
}
output.indent();
prop.print(output);
});
output.newline();
});
else output.print("{}");
});
DEFPRINT(AST_NewTarget, function(self, output) {
output.print("new.target");
});
AST_ObjectProperty.DEFMETHOD("print_property_name", function(key, quote, output) {
if (output.option("quote_keys")) { if (output.option("quote_keys")) {
output.print_string(key + ""); output.print_string(key + "");
} else if ((typeof key == "number" } else if ((typeof key == "number"
@@ -1511,98 +1252,33 @@ function OutputStream(options) {
} else { } else {
output.print_string(key, quote); output.print_string(key, quote);
} }
}); }
DEFPRINT(AST_ObjectKeyVal, function(self, output){ DEFPRINT(AST_ObjectKeyVal, function(self, output){
function get_name(self) { print_property_name(self.key, self.quote, output);
var def = self.definition();
return def ? def.mangled_name || def.name : self.name;
}
var allowShortHand = output.option("shorthand");
if (allowShortHand &&
self.value instanceof AST_Symbol &&
is_identifier_string(self.key) &&
get_name(self.value) === self.key
) {
self.print_property_name(self.key, self.quote, output);
} else if (allowShortHand &&
self.value instanceof AST_DefaultAssign &&
self.value.left instanceof AST_Symbol &&
is_identifier_string(self.key) &&
get_name(self.value.left) === self.key
) {
self.print_property_name(self.key, self.quote, output);
output.print("=");
self.value.right.print(output);
} else {
if (!(self.key instanceof AST_Node)) {
self.print_property_name(self.key, self.quote, output);
} else {
output.with_square(function() {
self.key.print(output);
});
}
output.colon(); output.colon();
self.value.print(output); self.value.print(output);
}
}); });
AST_ObjectProperty.DEFMETHOD("_print_getter_setter", function(type, self, output) { AST_ObjectProperty.DEFMETHOD("_print_getter_setter", function(type, output) {
if (self.static) {
output.print("static");
output.space();
}
output.print(type); output.print(type);
output.space(); output.space();
if (self.key instanceof AST_SymbolMethod) { print_property_name(this.key.name, this.quote, output);
self.print_property_name(self.key.name, self.quote, output); this.value._do_print(output, true);
} else {
output.with_square(function() {
self.key.print(output);
});
}
self.value._do_print(output, true);
}); });
DEFPRINT(AST_ObjectSetter, function(self, output){ DEFPRINT(AST_ObjectSetter, function(self, output){
self._print_getter_setter("set", self, output); self._print_getter_setter("set", output);
}); });
DEFPRINT(AST_ObjectGetter, function(self, output){ DEFPRINT(AST_ObjectGetter, function(self, output){
self._print_getter_setter("get", self, output); self._print_getter_setter("get", output);
});
DEFPRINT(AST_ConciseMethod, function(self, output){
if (self.static) {
output.print("static");
output.space();
}
if (self.is_generator) {
output.print("*");
}
if (self.key instanceof AST_SymbolMethod) {
self.print_property_name(self.key.name, self.quote, output);
} else {
output.with_square(function() {
self.key.print(output);
});
}
self.value._do_print(output, true);
});
AST_Symbol.DEFMETHOD("_do_print", function(output){
var def = this.definition();
output.print_name(def ? def.mangled_name || def.name : this.name);
}); });
DEFPRINT(AST_Symbol, function(self, output){ DEFPRINT(AST_Symbol, function(self, output){
self._do_print(output); var def = self.definition();
}); output.print_name(def ? def.mangled_name || def.name : self.name);
DEFPRINT(AST_SymbolDeclaration, function(self, output){
self._do_print(output);
}); });
DEFPRINT(AST_Hole, noop); DEFPRINT(AST_Hole, noop);
DEFPRINT(AST_This, function(self, output){ DEFPRINT(AST_This, function(self, output){
output.print("this"); output.print("this");
}); });
DEFPRINT(AST_Super, function(self, output){
output.print("super");
});
DEFPRINT(AST_Constant, function(self, output){ DEFPRINT(AST_Constant, function(self, output){
output.print(self.getValue()); output.print(self.getValue());
}); });
@@ -1617,45 +1293,14 @@ function OutputStream(options) {
} }
}); });
function regexp_safe_literal(code) {
return [
0x5c , // \
0x2f , // /
0x2e , // .
0x2b , // +
0x2a , // *
0x3f , // ?
0x28 , // (
0x29 , // )
0x5b , // [
0x5d , // ]
0x7b , // {
0x7d , // }
0x24 , // $
0x5e , // ^
0x3a , // :
0x7c , // |
0x21 , // !
0x0a , // \n
0x0d , // \r
0x00 , // \0
0xfeff , // Unicode BOM
0x2028 , // unicode "line separator"
0x2029 , // unicode "paragraph separator"
].indexOf(code) < 0;
};
DEFPRINT(AST_RegExp, function(self, output){ DEFPRINT(AST_RegExp, function(self, output){
var str = self.getValue().toString(); var regexp = self.getValue();
var str = regexp.toString();
if (regexp.raw_source) {
str = "/" + regexp.raw_source + str.slice(str.lastIndexOf("/"));
}
if (output.option("ascii_only")) { if (output.option("ascii_only")) {
str = output.to_ascii(str); str = output.to_ascii(str);
} else if (output.option("unescape_regexps")) {
str = str.split("\\\\").map(function(str){
return str.replace(/\\u[0-9a-fA-F]{4}|\\x[0-9a-fA-F]{2}/g, function(s){
var code = parseInt(s.substr(2), 16);
return regexp_safe_literal(code) ? String.fromCharCode(code) : s;
});
}).join("\\\\");
} }
output.print(str); output.print(str);
var p = output.parent(); var p = output.parent();

File diff suppressed because one or more lines are too long

View File

@@ -44,16 +44,6 @@
"use strict"; "use strict";
function find_builtins(reserved) { function find_builtins(reserved) {
// Compatibility fix for some standard defined globals not defined on every js environment
var new_globals = ["Symbol", "Map", "Promise", "Proxy", "Reflect", "Set", "WeakMap", "WeakSet"];
var objects = {};
var global_ref = typeof global === "object" ? global : self;
new_globals.forEach(function (new_global) {
objects[new_global] = global_ref[new_global] || new Function();
});
// NaN will be included due to Number.NaN // NaN will be included due to Number.NaN
[ [
"null", "null",
@@ -65,15 +55,7 @@ function find_builtins(reserved) {
].forEach(add); ].forEach(add);
[ Object, Array, Function, Number, [ Object, Array, Function, Number,
String, Boolean, Error, Math, String, Boolean, Error, Math,
Date, RegExp, objects.Symbol, ArrayBuffer, Date, RegExp
DataView, decodeURI, decodeURIComponent,
encodeURI, encodeURIComponent, eval, EvalError,
Float32Array, Float64Array, Int8Array, Int16Array,
Int32Array, isFinite, isNaN, JSON, objects.Map, parseFloat,
parseInt, objects.Promise, objects.Proxy, RangeError, ReferenceError,
objects.Reflect, objects.Set, SyntaxError, TypeError, Uint8Array,
Uint8ClampedArray, Uint16Array, Uint32Array, URIError,
objects.WeakMap, objects.WeakSet
].forEach(function(ctor){ ].forEach(function(ctor){
Object.getOwnPropertyNames(ctor).map(add); Object.getOwnPropertyNames(ctor).map(add);
if (ctor.prototype) { if (ctor.prototype) {
@@ -138,9 +120,6 @@ function mangle_properties(ast, options) {
else if (node instanceof AST_Sub) { else if (node instanceof AST_Sub) {
addStrings(node.property, keep_quoted); addStrings(node.property, keep_quoted);
} }
else if (node instanceof AST_ConciseMethod) {
add(node.name.name);
}
})); }));
// step 2: transform the tree, renaming properties // step 2: transform the tree, renaming properties
@@ -160,11 +139,6 @@ function mangle_properties(ast, options) {
if (!keep_quoted) if (!keep_quoted)
node.property = mangleStrings(node.property); node.property = mangleStrings(node.property);
} }
else if (node instanceof AST_ConciseMethod) {
if (should_mangle(node.name.name)) {
node.name.name = mangle(node.name.name);
}
}
// else if (node instanceof AST_String) { // else if (node instanceof AST_String) {
// if (should_mangle(node.value)) { // if (should_mangle(node.value)) {
// AST_Node.warn( // AST_Node.warn(

View File

@@ -49,9 +49,7 @@ function SymbolDef(scope, index, orig) {
this.scope = scope; this.scope = scope;
this.references = []; this.references = [];
this.global = false; this.global = false;
this.export = false;
this.mangled_name = null; this.mangled_name = null;
this.object_destructuring_arg = false;
this.undeclared = false; this.undeclared = false;
this.index = index; this.index = index;
this.id = SymbolDef.next_id++; this.id = SymbolDef.next_id++;
@@ -64,17 +62,11 @@ SymbolDef.prototype = {
if (!options) options = {}; if (!options) options = {};
return (this.global && !options.toplevel) return (this.global && !options.toplevel)
|| this.export
|| this.object_destructuring_arg
|| this.undeclared || this.undeclared
|| (!options.eval && (this.scope.uses_eval || this.scope.uses_with)) || (!options.eval && (this.scope.uses_eval || this.scope.uses_with))
|| (options.keep_fnames || (options.keep_fnames
&& (this.orig[0] instanceof AST_SymbolLambda && (this.orig[0] instanceof AST_SymbolLambda
|| this.orig[0] instanceof AST_SymbolDefun)) || this.orig[0] instanceof AST_SymbolDefun));
|| this.orig[0] instanceof AST_SymbolMethod
|| (options.keep_classnames
&& (this.orig[0] instanceof AST_SymbolClass
|| this.orig[0] instanceof AST_SymbolDefClass));
}, },
mangle: function(options) { mangle: function(options) {
var cache = options.cache && options.cache.props; var cache = options.cache && options.cache.props;
@@ -102,7 +94,6 @@ AST_Toplevel.DEFMETHOD("figure_out_scope", function(options){
options = defaults(options, { options = defaults(options, {
cache: null, cache: null,
ie8: false, ie8: false,
safari10: false,
}); });
// pass 1: setup scope chaining and handle definitions // pass 1: setup scope chaining and handle definitions
@@ -110,35 +101,15 @@ AST_Toplevel.DEFMETHOD("figure_out_scope", function(options){
var scope = self.parent_scope = null; var scope = self.parent_scope = null;
var labels = new Dictionary(); var labels = new Dictionary();
var defun = null; var defun = null;
var in_destructuring = null;
var in_export = false;
var in_block = 0;
var for_scopes = [];
var tw = new TreeWalker(function(node, descend){ var tw = new TreeWalker(function(node, descend){
if (node.is_block_scope()) { if (node instanceof AST_Catch) {
var save_scope = scope; var save_scope = scope;
scope = new AST_Scope(node); scope = new AST_Scope(node);
scope.init_scope_vars(save_scope); scope.init_scope_vars(save_scope);
if (!(node instanceof AST_Scope)) {
scope.uses_with = save_scope.uses_with;
scope.uses_eval = save_scope.uses_eval;
scope.directives = save_scope.directives;
}
if (options.safari10) {
if (node instanceof AST_For || node instanceof AST_ForIn) {
for_scopes.push(scope);
}
}
descend(); descend();
scope = save_scope; scope = save_scope;
return true; return true;
} }
if (node instanceof AST_Destructuring && node.is_array === false) {
in_destructuring = node; // These don't nest
descend();
in_destructuring = null;
return true;
}
if (node instanceof AST_Scope) { if (node instanceof AST_Scope) {
node.init_scope_vars(scope); node.init_scope_vars(scope);
var save_scope = scope; var save_scope = scope;
@@ -152,22 +123,6 @@ AST_Toplevel.DEFMETHOD("figure_out_scope", function(options){
labels = save_labels; labels = save_labels;
return true; // don't descend again in TreeWalker return true; // don't descend again in TreeWalker
} }
if (node instanceof AST_Export) {
in_export = true;
descend();
in_export = false;
return true;
}
if (node instanceof AST_BlockStatement
|| node instanceof AST_Switch
|| node instanceof AST_Try
|| node instanceof AST_Catch
|| node instanceof AST_Finally) {
in_block++;
descend();
in_block--;
return true;
}
if (node instanceof AST_LabeledStatement) { if (node instanceof AST_LabeledStatement) {
var l = node.label; var l = node.label;
if (labels.has(l.name)) { if (labels.has(l.name)) {
@@ -186,15 +141,12 @@ AST_Toplevel.DEFMETHOD("figure_out_scope", function(options){
if (node instanceof AST_Symbol) { if (node instanceof AST_Symbol) {
node.scope = scope; node.scope = scope;
} }
if (node instanceof AST_SymbolFunarg) {
node.object_destructuring_arg = !!in_destructuring;
}
if (node instanceof AST_Label) { if (node instanceof AST_Label) {
node.thedef = node; node.thedef = node;
node.references = []; node.references = [];
} }
if (node instanceof AST_SymbolLambda) { if (node instanceof AST_SymbolLambda) {
defun.def_function(node, in_export, in_block); defun.def_function(node);
} }
else if (node instanceof AST_SymbolDefun) { else if (node instanceof AST_SymbolDefun) {
// Careful here, the scope where this should be defined is // Careful here, the scope where this should be defined is
@@ -202,28 +154,10 @@ AST_Toplevel.DEFMETHOD("figure_out_scope", function(options){
// scope when we encounter the AST_Defun node (which is // scope when we encounter the AST_Defun node (which is
// instanceof AST_Scope) but we get to the symbol a bit // instanceof AST_Scope) but we get to the symbol a bit
// later. // later.
var parent_lambda = defun.parent_scope; (node.scope = defun.parent_scope).def_function(node);
while (parent_lambda.is_block_scope()) {
parent_lambda = parent_lambda.parent_scope;
} }
(node.scope = parent_lambda).def_function(node, in_export, in_block); else if (node instanceof AST_SymbolVar) {
} defun.def_variable(node);
else if (node instanceof AST_SymbolClass) {
defun.def_variable(node, in_export, in_block);
}
else if (node instanceof AST_SymbolImport) {
scope.def_variable(node, in_export, in_block);
}
else if (node instanceof AST_SymbolDefClass) {
// This deals with the name of the class being available
// inside the class.
(node.scope = defun.parent_scope).def_function(node, in_export, in_block);
}
else if (node instanceof AST_SymbolVar
|| node instanceof AST_SymbolLet
|| node instanceof AST_SymbolConst) {
var def = ((node instanceof AST_SymbolBlockDeclaration) ? scope : defun).def_variable(node, in_export, in_block);
def.destructuring = in_destructuring;
if (defun !== scope) { if (defun !== scope) {
node.mark_enclosed(options); node.mark_enclosed(options);
var def = scope.find_variable(node); var def = scope.find_variable(node);
@@ -234,7 +168,7 @@ AST_Toplevel.DEFMETHOD("figure_out_scope", function(options){
} }
} }
else if (node instanceof AST_SymbolCatch) { else if (node instanceof AST_SymbolCatch) {
scope.def_variable(node, in_export, in_block).defun = defun; scope.def_variable(node).defun = defun;
} }
else if (node instanceof AST_LabelRef) { else if (node instanceof AST_LabelRef) {
var sym = labels.get(node.name); var sym = labels.get(node.name);
@@ -249,24 +183,8 @@ AST_Toplevel.DEFMETHOD("figure_out_scope", function(options){
self.walk(tw); self.walk(tw);
// pass 2: find back references and eval // pass 2: find back references and eval
var func = null; self.globals = new Dictionary();
var cls = null;
var globals = self.globals = new Dictionary();
var tw = new TreeWalker(function(node, descend){ var tw = new TreeWalker(function(node, descend){
if (node instanceof AST_Lambda) {
var prev_func = func;
func = node;
descend();
func = prev_func;
return true;
}
if (node instanceof AST_Class) {
var prev_cls = cls;
cls = node;
descend();
cls = prev_cls;
return true;
}
if (node instanceof AST_LoopControl && node.label) { if (node instanceof AST_LoopControl && node.label) {
node.label.thedef.references.push(node); node.label.thedef.references.push(node);
return true; return true;
@@ -310,19 +228,6 @@ AST_Toplevel.DEFMETHOD("figure_out_scope", function(options){
})); }));
} }
// pass 4: add symbol definitions to loop scopes
// Safari/Webkit bug workaround - loop init let variable shadowing argument.
// https://github.com/mishoo/UglifyJS2/issues/1753
// https://bugs.webkit.org/show_bug.cgi?id=171041
if (options.safari10) {
for (var i = 0; i < for_scopes.length; i++) {
var scope = for_scopes[i];
scope.parent_scope.variables.each(function(def) {
push_uniq(scope.enclosed, def);
});
}
}
if (options.cache) { if (options.cache) {
this.cname = options.cache.cname; this.cname = options.cache.cname;
} }
@@ -351,27 +256,10 @@ AST_Scope.DEFMETHOD("init_scope_vars", function(parent_scope){
this.cname = -1; // the current index for mangling functions/variables this.cname = -1; // the current index for mangling functions/variables
}); });
AST_Node.DEFMETHOD("is_block_scope", function(){
return false; // Behaviour will be overridden by AST_Block
});
AST_Block.DEFMETHOD("is_block_scope", function(){
return (
!(this instanceof AST_Lambda) &&
!(this instanceof AST_Toplevel) &&
!(this instanceof AST_Class) &&
!(this instanceof AST_SwitchBranch)
);
});
AST_IterationStatement.DEFMETHOD("is_block_scope", function(){
return true;
});
AST_Lambda.DEFMETHOD("init_scope_vars", function(){ AST_Lambda.DEFMETHOD("init_scope_vars", function(){
AST_Scope.prototype.init_scope_vars.apply(this, arguments); AST_Scope.prototype.init_scope_vars.apply(this, arguments);
this.uses_arguments = false; this.uses_arguments = false;
this.def_variable(new AST_SymbolConst({ this.def_variable(new AST_SymbolFunarg({
name: "arguments", name: "arguments",
start: this.start, start: this.start,
end: this.end end: this.end
@@ -404,24 +292,16 @@ AST_Scope.DEFMETHOD("find_variable", function(name){
|| (this.parent_scope && this.parent_scope.find_variable(name)); || (this.parent_scope && this.parent_scope.find_variable(name));
}); });
AST_Scope.DEFMETHOD("def_function", function(symbol, in_export, in_block){ AST_Scope.DEFMETHOD("def_function", function(symbol){
this.functions.set(symbol.name, this.def_variable(symbol, in_export, in_block)); this.functions.set(symbol.name, this.def_variable(symbol));
}); });
AST_Scope.DEFMETHOD("def_variable", function(symbol, in_export, in_block){ 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, this.variables.size(), symbol);
this.variables.set(symbol.name, def); this.variables.set(symbol.name, def);
def.object_destructuring_arg = symbol.object_destructuring_arg;
if (in_export) {
def.export = true;
}
if (in_block && symbol instanceof AST_SymbolBlockDeclaration) {
def.global = false;
} else {
def.global = !this.parent_scope; def.global = !this.parent_scope;
}
} else { } else {
def = this.variables.get(symbol.name); def = this.variables.get(symbol.name);
def.orig.push(symbol); def.orig.push(symbol);
@@ -469,19 +349,11 @@ AST_Function.DEFMETHOD("next_mangled", function(options, def){
}); });
AST_Symbol.DEFMETHOD("unmangleable", function(options){ AST_Symbol.DEFMETHOD("unmangleable", function(options){
var def = this.definition(); return this.definition().unmangleable(options);
return def && def.unmangleable(options);
});
// property accessors are not mangleable
AST_SymbolAccessor.DEFMETHOD("unmangleable", function(){
return true;
}); });
// labels are always mangleable // labels are always mangleable
AST_Label.DEFMETHOD("unmangleable", function(){ AST_Label.DEFMETHOD("unmangleable", return_false);
return false;
});
AST_Symbol.DEFMETHOD("unreferenced", function(){ AST_Symbol.DEFMETHOD("unreferenced", function(){
return this.definition().references.length == 0 return this.definition().references.length == 0
@@ -492,13 +364,9 @@ AST_Symbol.DEFMETHOD("undeclared", function(){
return this.definition().undeclared; return this.definition().undeclared;
}); });
AST_LabelRef.DEFMETHOD("undeclared", function(){ AST_LabelRef.DEFMETHOD("undeclared", return_false);
return false;
});
AST_Label.DEFMETHOD("undeclared", function(){ AST_Label.DEFMETHOD("undeclared", return_false);
return false;
});
AST_Symbol.DEFMETHOD("definition", function(){ AST_Symbol.DEFMETHOD("definition", function(){
return this.thedef; return this.thedef;
@@ -512,7 +380,6 @@ AST_Toplevel.DEFMETHOD("_default_mangler_options", function(options){
return defaults(options, { return defaults(options, {
eval : false, eval : false,
ie8 : false, ie8 : false,
keep_classnames: false,
keep_fnames : false, keep_fnames : false,
reserved : [], reserved : [],
toplevel : false, toplevel : false,
@@ -564,19 +431,13 @@ AST_Toplevel.DEFMETHOD("mangle_names", function(options){
node.mangled_name = name; node.mangled_name = name;
return true; return true;
} }
var mangle_with_block_scope = if (!options.ie8 && node instanceof AST_SymbolCatch) {
(!options.ie8 && node instanceof AST_SymbolCatch) ||
node instanceof AST_SymbolBlockDeclaration;
if (mangle_with_block_scope) {
to_mangle.push(node.definition()); to_mangle.push(node.definition());
return; return;
} }
}); });
this.walk(tw); this.walk(tw);
to_mangle.forEach(function(def){ to_mangle.forEach(function(def){ def.mangle(options) });
if (def.destructuring && !def.destructuring.is_array) return;
def.mangle(options);
});
if (options.cache) { if (options.cache) {
options.cache.cname = this.cname; options.cache.cname = this.cname;
@@ -610,8 +471,6 @@ AST_Toplevel.DEFMETHOD("compute_char_frequency", function(options){
} }
else if (node instanceof AST_Var) else if (node instanceof AST_Var)
base54.consider("var"); base54.consider("var");
else if (node instanceof AST_Const)
base54.consider("const");
else if (node instanceof AST_Lambda) else if (node instanceof AST_Lambda)
base54.consider("function"); base54.consider("function");
else if (node instanceof AST_For) else if (node instanceof AST_For)
@@ -627,27 +486,21 @@ AST_Toplevel.DEFMETHOD("compute_char_frequency", function(options){
else if (node instanceof AST_With) else if (node instanceof AST_With)
base54.consider("with"); base54.consider("with");
else if (node instanceof AST_ObjectSetter) else if (node instanceof AST_ObjectSetter)
base54.consider("set" + (typeof node.key === "string" ? node.key : "")); base54.consider("set" + node.key);
else if (node instanceof AST_ObjectGetter) else if (node instanceof AST_ObjectGetter)
base54.consider("get" + (typeof node.key === "string" ? node.key : "")); base54.consider("get" + node.key);
else if (node instanceof AST_ObjectKeyVal && typeof node.key === "string") else if (node instanceof AST_ObjectKeyVal)
base54.consider(node.key);
else if (node instanceof AST_ConciseMethod && typeof node.key === "string")
base54.consider(node.key); base54.consider(node.key);
else if (node instanceof AST_New) else if (node instanceof AST_New)
base54.consider("new"); base54.consider("new");
else if (node instanceof AST_This) else if (node instanceof AST_This)
base54.consider("this"); base54.consider("this");
else if (node instanceof AST_Super)
base54.consider("super");
else if (node instanceof AST_Try) else if (node instanceof AST_Try)
base54.consider("try"); base54.consider("try");
else if (node instanceof AST_Catch) else if (node instanceof AST_Catch)
base54.consider("catch"); base54.consider("catch");
else if (node instanceof AST_Finally) else if (node instanceof AST_Finally)
base54.consider("finally"); base54.consider("finally");
else if (node instanceof AST_Yield)
base54.consider("yield");
else if (node instanceof AST_Symbol && node.unmangleable(options)) else if (node instanceof AST_Symbol && node.unmangleable(options))
base54.consider(node.name); base54.consider(node.name);
else if (node instanceof AST_Unary || node instanceof AST_Binary) else if (node instanceof AST_Unary || node instanceof AST_Binary)

View File

@@ -163,18 +163,10 @@ TreeTransformer.prototype = new TreeWalker;
if (self.value) self.value = self.value.transform(tw); if (self.value) self.value = self.value.transform(tw);
}); });
_(AST_Destructuring, function(self, tw) {
self.names = do_list(self.names, tw);
});
_(AST_Lambda, function(self, tw){ _(AST_Lambda, function(self, tw){
if (self.name) self.name = self.name.transform(tw); if (self.name) self.name = self.name.transform(tw);
self.argnames = do_list(self.argnames, tw); self.argnames = do_list(self.argnames, tw);
if (self.body instanceof AST_Node) {
self.body = self.body.transform(tw);
} else {
self.body = do_list(self.body, tw); self.body = do_list(self.body, tw);
}
}); });
_(AST_Call, function(self, tw){ _(AST_Call, function(self, tw){
@@ -195,10 +187,6 @@ TreeTransformer.prototype = new TreeWalker;
self.property = self.property.transform(tw); self.property = self.property.transform(tw);
}); });
_(AST_Yield, function(self, tw){
if (self.expression) self.expression = self.expression.transform(tw);
});
_(AST_Unary, function(self, tw){ _(AST_Unary, function(self, tw){
self.expression = self.expression.transform(tw); self.expression = self.expression.transform(tw);
}); });
@@ -223,32 +211,7 @@ TreeTransformer.prototype = new TreeWalker;
}); });
_(AST_ObjectProperty, function(self, tw){ _(AST_ObjectProperty, function(self, tw){
if (self.key instanceof AST_Node) {
self.key = self.key.transform(tw);
}
self.value = self.value.transform(tw); self.value = self.value.transform(tw);
}); });
_(AST_Class, function(self, tw){
if (self.name) self.name = self.name.transform(tw);
if (self.extends) self.extends = self.extends.transform(tw);
self.properties = do_list(self.properties, tw);
});
_(AST_Expansion, function(self, tw){
self.expression = self.expression.transform(tw);
});
_(AST_TemplateString, function(self, tw) {
for (var i = 0; i < self.segments.length; i++) {
if (!(self.segments[i] instanceof AST_TemplateSegment)) {
self.segments[i] = self.segments[i].transform(tw);
}
}
});
_(AST_PrefixedTemplateString, function(self, tw) {
self.template_string = self.template_string.transform(tw);
});
})(); })();

View File

@@ -1,10 +1,10 @@
{ {
"name": "uglify-es", "name": "uglify-js",
"description": "JavaScript parser, mangler/compressor and beautifier toolkit", "description": "JavaScript parser, mangler/compressor and beautifier toolkit",
"homepage": "http://lisperator.net/uglifyjs", "homepage": "http://lisperator.net/uglifyjs",
"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.0.4", "version": "3.0.15",
"engines": { "engines": {
"node": ">=0.8.0" "node": ">=0.8.0"
}, },
@@ -33,22 +33,12 @@
"source-map": "~0.5.1" "source-map": "~0.5.1"
}, },
"devDependencies": { "devDependencies": {
"acorn": "~0.6.0", "acorn": "~5.0.3",
"escodegen": "~1.3.3", "mocha": "~2.3.4",
"esfuzz": "~0.3.1", "semver": "~5.3.0"
"estraverse": "~1.5.1",
"mocha": "~2.3.4"
},
"optionalDependencies": {
"uglify-to-browserify": "~1.0.0"
},
"browserify": {
"transform": [
"uglify-to-browserify"
]
}, },
"scripts": { "scripts": {
"test": "node test/run-tests.js" "test": "node test/run-tests.js"
}, },
"keywords": ["uglify", "uglify-js", "minify", "minifier"] "keywords": ["uglify", "uglify-js", "minify", "minifier", "es5"]
} }

View File

@@ -9,7 +9,7 @@ var args = process.argv.slice(2);
if (!args.length) { if (!args.length) {
args.push("-mc"); args.push("-mc");
} }
args.push("--stats"); args.push("--timings");
var urls = [ var urls = [
"https://code.jquery.com/jquery-3.2.1.js", "https://code.jquery.com/jquery-3.2.1.js",
"https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.6.4/angular.js", "https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.6.4/angular.js",
@@ -29,12 +29,7 @@ function done() {
var info = results[url]; var info = results[url];
console.log(); console.log();
console.log(url); console.log(url);
var elapsed = 0; console.log(info.log);
console.log(info.log.replace(/Elapsed: ([0-9]+)\s*/g, function(match, time) {
elapsed += 1e-3 * parseInt(time);
return "";
}));
console.log("Run-time:", elapsed.toFixed(3), "s");
console.log("Original:", info.input, "bytes"); console.log("Original:", info.input, "bytes");
console.log("Uglified:", info.output, "bytes"); console.log("Uglified:", info.output, "bytes");
console.log("SHA1 sum:", info.sha1); console.log("SHA1 sum:", info.sha1);

View File

@@ -1,5 +1,3 @@
// NOTE trailing comma doesn't contribute to length of an array
// That also means the array changes length if previous element is a hole too and got cut off
holes_and_undefined: { holes_and_undefined: {
input: { input: {
w = [1,,]; w = [1,,];
@@ -93,79 +91,6 @@ constant_join_2: {
} }
} }
spread_with_variable_as_last_element: {
input: {
var values = [4, 5, 6];
var a = [1, 2, 3, ...values];
}
expect: {
var values = [4, 5, 6];
var a = [1, 2, 3, ...values];
}
}
spread_with_variable_in_middle: {
input: {
var values = [4, 5, 6];
var a = [1, 2, 3, ...values, 7,,,];
}
expect: {
var values = [4, 5, 6];
var a = [1, 2, 3, ...values, 7,,,];
}
}
spread_with_variable_at_front: {
input: {
var values = [1, 2, 3];
var a = [...values, 4, 5, 6];
}
expect: {
var values = [1, 2, 3];
var a = [...values, 4, 5, 6];
}
}
spread_with_variable_at_front_after_elisions: {
input: {
var values = [1, 2, 3];
var a = [,,,...values, 4, 5, 6];
}
expect: {
var values = [1, 2, 3];
var a = [,,,...values, 4, 5, 6];
}
}
spread_with_array_at_end: {
input: {
var a = [1, 2, ...[4, 5, 6]];
}
expect: {
var a = [1, 2, ...[4, 5, 6]];
}
}
spread_with_logical_expression_at_end: {
options = { evaluate: true }
input: {
var a = [1, 2, 3, ...[2+2]]
}
expect: {
var a = [1, 2, 3, ...[4]]
}
}
spread_with_logical_expression_at_middle: {
options = { evaluate: true }
input: {
var a = [1, 1, ...[1+1, 1+2, 2+3], 8]
}
expect: {
var a = [1, 1, ...[2, 3, 5], 8]
}
}
constant_join_3: { constant_join_3: {
options = { options = {
unsafe: true, unsafe: true,

View File

@@ -1,137 +0,0 @@
arrow_functions_without_body: {
input: {
var a1 = () => 42;
var a2 = (p) => p;
var a3 = p => p;
var a4 = (...p) => p;
var a5 = (b, c) => b + c;
var a6 = (b, ...c) => b + c[0];
var a7 = (...b) => b.join();
}
expect: {
var a1 = () => 42;
var a2 = (p) => p;
var a3 = p => p;
var a4 = (...p) => p;
var a5 = (b, c) => b + c;
var a6 = (b, ...c) => b + c[0];
var a7 = (...b) => b.join();
}
}
arrow_functions_with_body: {
input: {
var a1 = () => {
var a = 42 * Math.random();
return a;
};
var a2 = (p) => {
var a = Math.random() * p;
return a;
};
var a3 = p => {
var a = Math.random() * p;
return a;
};
var a4 = (...p) => {
var a = Math.random() * p;
return a;
};
var a5 = (b, c) => {
var result = b * c + b / c;
return result
};
var a6 = (b, ...c) => {
var result = b;
for (var i = 0; i < c.length; i++)
result += c[i];
return result
};
var a7 = (...b) => {
b.join();
}
}
expect: {
var a1 = () => {
var a = 42 * Math.random();
return a;
};
var a2 = (p) => {
var a = Math.random() * p;
return a;
};
var a3 = p => {
var a = Math.random() * p;
return a;
};
var a4 = (...p) => {
var a = Math.random() * p;
return a;
};
var a5 = (b, c) => {
var result = b * c + b / c;
return result
};
var a6 = (b, ...c) => {
var result = b;
for (var i = 0; i < c.length; i++)
result += c[i];
return result
};
var a7 = (...b) => {
b.join();
};
}
}
arrow_function_with_single_parameter_with_default: {
input: {
var foo = (a = 0) => doSomething(a);
}
expect_exact: "var foo=(a=0)=>doSomething(a);"
}
arrow_binding_pattern: {
input: {
var foo = ([]) => "foo";
var bar = ({}) => "bar";
var with_default = (foo = "default") => foo;
var object_with_default = ({foo = "default", bar: baz = "default"}) => foo;
var array_after_spread = (...[foo]) => foo;
var array_after_spread = (...{foo}) => foo;
var computed = ({ [compute()]: x }) => {};
var array_hole = ([, , ...x] = [1, 2]) => {};
var object_trailing_elision = ({foo,}) => {};
var spread_empty_array = (...[]) => "foo";
var spread_empty_object = (...{}) => "foo";
}
expect: {
var foo = ([]) => "foo";
var bar = ({}) => "bar";
var with_default = (foo = "default") => foo;
var object_with_default = ({foo = "default", bar: baz = "default"}) => foo;
var array_after_spread = (...[foo]) => foo;
var array_after_spread = (...{foo}) => foo;
var computed = ({ [compute()]: x }) => {};
var array_hole = ([, , ...x] = [1, 2]) => {};
var object_trailing_elision = ({foo,}) => {};
var spread_empty_array = (...[]) => "foo";
var spread_empty_object = (...{}) => "foo";
}
}
arrow_binding_pattern_strict: {
input: {
var foo = ([,]) => "foo";
}
expect_exact: 'var foo=([,])=>"foo";'
}
arrow_with_regexp: {
input: {
num => /\d{11,14}/.test( num )
}
expect: {
num => /\d{11,14}/.test( num )
}
}

View File

@@ -1,177 +0,0 @@
let_statement: {
input: {
let x = 6;
}
expect_exact: "let x=6;"
}
do_not_hoist_let: {
options = {
hoist_vars: true,
};
input: {
function x() {
if (FOO) {
let let1;
let let2;
var var1;
var var2;
}
}
}
expect: {
function x() {
var var1, var2;
if (FOO) {
let let1;
let let2;
}
}
}
}
do_not_remove_anon_blocks_if_they_have_decls: {
input: {
function x() {
{
let x;
}
{
var x;
}
{
const y;
class Zee {};
}
}
{
let y;
}
{
var y;
}
}
expect: {
function x(){
{
let x
}
var x;
{
const y;
class Zee {}
}
}
{
let y
}
var y;
}
}
remove_unused_in_global_block: {
options = {
unused: true,
}
input: {
{
let x;
const y;
class Zee {};
var w;
}
let ex;
const why;
class Zed {};
var wut;
console.log(x, y, Zee);
}
expect: {
var w;
let ex;
const why;
class Zed {};
var wut;
console.log(x, y, Zee);
}
}
regression_block_scope_resolves: {
mangle = { };
options = {
dead_code: false
};
input: {
(function () {
if(1) {
let x;
const y;
class Zee {};
}
if(1) {
let ex;
const why;
class Zi {};
}
console.log(x, y, Zee, ex, why, Zi);
}());
}
expect: {
(function () {
if (1) {
let o;
const n;
class c {};
}
if (1) {
let o;
const n;
class c {};
}
console.log(x, y, Zee, ex, why, Zi);
}());
}
}
switch_block_scope_mangler: {
mangle = {}
input: {
var fn = function(code) {
switch (code) {
case 1:
let apple = code + 1;
let dog = code + 4;
console.log(apple, dog);
break;
case 2:
let banana = code + 2;
console.log(banana);
break;
default:
let cat = code + 3;
console.log(cat);
}
};
}
expect: {
var fn = function(o) {
switch (o) {
case 1:
let e = o + 1
let c = o + 4;
console.log(e, c);
break;
case 2:
let l = o + 2;
console.log(l);
break;
default:
let a = o + 3;
console.log(a);
}
};
}
}

View File

@@ -583,8 +583,8 @@ collapse_vars_assignment: {
return a = a; return a = a;
} }
function f1(c) { function f1(c) {
const a = 3 / c; var a = 3 / c;
const b = 1 - a; var b = 1 - a;
return b; return b;
} }
function f2(c) { function f2(c) {
@@ -724,10 +724,10 @@ collapse_vars_misc1: {
return t; return t;
} }
function f1(x) { var y = 5 - x; return y; } function f1(x) { var y = 5 - x; return y; }
function f2(x) { const z = foo(), y = z / (5 - x); return y; } function f2(x) { var z = foo(), y = z / (5 - x); return y; }
function f3(x) { var z = foo(), y = (5 - x) / z; return y; } function f3(x) { var z = foo(), y = (5 - x) / z; return y; }
function f4(x) { var z = foo(), y = (5 - u) / z; return y; } function f4(x) { var z = foo(), y = (5 - u) / z; return y; }
function f5(x) { const z = foo(), y = (5 - window.x) / z; return y; } function f5(x) { var z = foo(), y = (5 - window.x) / z; return y; }
function f6() { var b = window.a * window.z; return b && zap(); } function f6() { var b = window.a * window.z; return b && zap(); }
function f7() { var b = window.a * window.z; return b + b; } function f7() { var b = window.a * window.z; return b + b; }
function f8() { var b = window.a * window.z; var c = b + 5; return b + c; } function f8() { var b = window.a * window.z; var c = b + 5; return b + c; }
@@ -744,7 +744,7 @@ collapse_vars_misc1: {
function f2(x) { return foo() / (5 - x) } function f2(x) { return foo() / (5 - x) }
function f3(x) { return (5 - x) / foo() } function f3(x) { return (5 - x) / foo() }
function f4(x) { var z = foo(); return (5 - u) / z } function f4(x) { var z = foo(); return (5 - u) / z }
function f5(x) { const z = foo(); return (5 - window.x) / z } function f5(x) { var z = foo(); return (5 - window.x) / z }
function f6() { return window.a * window.z && zap() } function f6() { return window.a * window.z && zap() }
function f7() { var b = window.a * window.z; return b + b } function f7() { var b = window.a * window.z; return b + b }
function f8() { var b = window.a * window.z; return b + (b + 5) } function f8() { var b = window.a * window.z; return b + (b + 5) }
@@ -1328,7 +1328,6 @@ collapse_vars_regexp: {
issue_1537: { issue_1537: {
options = { options = {
collapse_vars: true, collapse_vars: true,
toplevel: true,
} }
input: { input: {
var k = ''; var k = '';
@@ -1340,104 +1339,6 @@ issue_1537: {
} }
} }
issue_1537_for_of: {
options = {
collapse_vars: true,
toplevel: true,
}
input: {
var k = '';
for (k of {prop: 'val'}){}
}
expect: {
var k = '';
for (k of {prop: 'val'});
}
}
issue_1537_destructuring_1: {
options = {
collapse_vars: true,
toplevel: true,
}
input: {
var x = 1, y = 2;
[x] = [y];
}
expect: {
var x = 1, y = 2;
[x] = [y];
}
}
issue_1537_destructuring_2: {
options = {
collapse_vars: true,
toplevel: true,
}
input: {
var x = foo();
[x] = [1];
}
expect: {
var x = foo();
[x] = [1];
}
}
issue_1537_destructuring_3: {
options = {
collapse_vars: true,
toplevel: true,
}
input: {
var x = Math.random();
({p: x = 9} = {v: 1});
}
expect: {
var x = Math.random();
({p: x = 9} = {v: 1});
}
}
issue_1537_destructuring_for_in: {
options = {
collapse_vars: true,
toplevel: true,
}
input: {
var x = 1, y = 2;
(function() {
for ([[x], y] in a);
})();
}
expect: {
var x = 1, y = 2;
(function() {
for ([[x], y] in a);
})();
}
}
issue_1537_destructuring_for_of: {
options = {
collapse_vars: true,
toplevel: true,
}
input: {
var x = 1, y = 2;
(function() {
for ([[x], y] of a);
})();
}
expect: {
var x = 1, y = 2;
(function() {
for ([[x], y] of a);
})();
}
}
issue_1562: { issue_1562: {
options = { options = {
collapse_vars: true, collapse_vars: true,
@@ -2203,10 +2104,11 @@ unused_orig: {
expect: { expect: {
var a = 1; var a = 1;
console.log(function(b) { console.log(function(b) {
var a;
var c = b; var c = b;
for (var d in c) for (var d in c) {
return --b + (a = c[0]); var a = c[0];
return --b + a;
}
a && a.NaN; a && a.NaN;
}([2]), a); }([2]), a);
} }
@@ -2284,49 +2186,3 @@ compound_assignment: {
} }
expect_stdout: "4" expect_stdout: "4"
} }
reassign_const_1: {
options = {
collapse_vars: true,
}
input: {
function f() {
const a = 1;
a = 2;
return a;
}
console.log(f());
}
expect: {
function f() {
const a = 1;
a = 2;
return a;
}
console.log(f());
}
expect_stdout: true
}
reassign_const_2: {
options = {
collapse_vars: true,
}
input: {
function f() {
const a = 1;
++a;
return a;
}
console.log(f());
}
expect: {
function f() {
const a = 1;
++a;
return a;
}
console.log(f());
}
expect_stdout: true
}

View File

@@ -1,166 +0,0 @@
issue_1191: {
options = {
evaluate : true,
booleans : true,
comparisons : true,
dead_code : true,
conditionals : true,
side_effects : true,
unused : true,
hoist_funs : true,
if_return : true,
join_vars : true,
sequences : false,
collapse_vars : false,
reduce_vars : true,
}
input: {
function foo(rot) {
const rotTol = 5;
if (rot < -rotTol || rot > rotTol)
bar();
baz();
}
}
expect: {
function foo(rot) {
(rot < -5 || rot > 5) && bar();
baz();
}
}
}
issue_1194: {
options = {
evaluate : true,
booleans : true,
comparisons : true,
dead_code : true,
conditionals : true,
side_effects : true,
unused : true,
hoist_funs : true,
if_return : true,
join_vars : true,
sequences : false,
collapse_vars : false,
reduce_vars : true,
}
input: {
function f1() {const a = "X"; return a + a;}
function f2() {const aa = "X"; return aa + aa;}
function f3() {const aaa = "X"; return aaa + aaa;}
}
expect: {
function f1(){return"XX"}
function f2(){return"XX"}
function f3(){return"XX"}
}
}
issue_1396: {
options = {
evaluate : true,
booleans : true,
comparisons : true,
dead_code : true,
conditionals : true,
side_effects : true,
unused : true,
hoist_funs : true,
if_return : true,
join_vars : true,
sequences : false,
collapse_vars : false,
reduce_vars : true,
}
input: {
function foo(a) {
const VALUE = 1;
console.log(2 | VALUE);
console.log(VALUE + 1);
console.log(VALUE);
console.log(a & VALUE);
}
function bar() {
const s = "01234567890123456789";
console.log(s + s + s + s + s);
const CONSTANT = "abc";
console.log(CONSTANT + CONSTANT + CONSTANT + CONSTANT + CONSTANT);
}
}
expect: {
function foo(a) {
console.log(3);
console.log(2);
console.log(1);
console.log(1 & a);
}
function bar() {
const s = "01234567890123456789";
console.log(s + s + s + s + s);
console.log("abcabcabcabcabc");
}
}
}
unused_regexp_literal: {
options = {
evaluate : true,
booleans : true,
comparisons : true,
dead_code : true,
conditionals : true,
side_effects : true,
unused : true,
hoist_funs : true,
if_return : true,
join_vars : true,
sequences : false,
collapse_vars : false,
}
input: {
function f(){ var a = /b/; }
}
expect: {
function f(){}
}
}
regexp_literal_not_const: {
options = {
evaluate : true,
booleans : true,
comparisons : true,
dead_code : true,
conditionals : true,
side_effects : true,
unused : true,
hoist_funs : true,
if_return : true,
join_vars : true,
sequences : false,
collapse_vars : false,
reduce_vars : true,
}
input: {
(function(){
var result;
const s = 'acdabcdeabbb';
const REGEXP_LITERAL = /ab*/g;
while (result = REGEXP_LITERAL.exec(s)) {
console.log(result[0]);
}
})();
}
expect: {
(function() {
var result;
const REGEXP_LITERAL = /ab*/g;
while (result = REGEXP_LITERAL.exec("acdabcdeabbb")) console.log(result[0]);
})();
}
expect_stdout: true
}

View File

@@ -31,7 +31,7 @@ dead_code_2_should_warn: {
function f() { function f() {
g(); g();
x = 10; x = 10;
throw "foo"; throw new Error("foo");
// completely discarding the `if` would introduce some // completely discarding the `if` would introduce some
// bugs. UglifyJS v1 doesn't deal with this issue; in v2 // bugs. UglifyJS v1 doesn't deal with this issue; in v2
// we copy any declarations to the upper scope. // we copy any declarations to the upper scope.
@@ -46,16 +46,60 @@ dead_code_2_should_warn: {
})(); })();
} }
} }
f();
} }
expect: { expect: {
function f() { function f() {
g(); g();
x = 10; x = 10;
throw "foo"; throw new Error("foo");
var x; var x;
function g(){}; function g(){};
} }
f();
} }
expect_stdout: true
node_version: "<=4"
}
dead_code_2_should_warn_strict: {
options = {
dead_code: true
};
input: {
"use strict";
function f() {
g();
x = 10;
throw new Error("foo");
// completely discarding the `if` would introduce some
// bugs. UglifyJS v1 doesn't deal with this issue; in v2
// we copy any declarations to the upper scope.
if (x) {
y();
var x;
function g(){};
// but nested declarations should not be kept.
(function(){
var q;
function y(){};
})();
}
}
f();
}
expect: {
"use strict";
function f() {
g();
x = 10;
throw new Error("foo");
var x;
}
f();
}
expect_stdout: true
node_version: ">=4"
} }
dead_code_constant_boolean_should_warn_more: { dead_code_constant_boolean_should_warn_more: {
@@ -78,6 +122,7 @@ dead_code_constant_boolean_should_warn_more: {
foo(); foo();
var moo; var moo;
} }
bar();
} }
expect: { expect: {
var foo; var foo;
@@ -86,155 +131,46 @@ dead_code_constant_boolean_should_warn_more: {
// as for the for, it should keep: // as for the for, it should keep:
var x = 10, y; var x = 10, y;
var moo; var moo;
bar();
} }
expect_stdout: true expect_stdout: true
node_version: "<=4"
} }
dead_code_block_decls_die: { dead_code_constant_boolean_should_warn_more_strict: {
options = {
dead_code : true,
conditionals : true,
booleans : true,
evaluate : true
};
input: {
if (0) {
let foo = 6;
const bar = 12;
class Baz {};
var qux;
}
console.log(foo, bar, Baz);
}
expect: {
var qux;
console.log(foo, bar, Baz);
}
}
dead_code_const_declaration: {
options = { options = {
dead_code : true, dead_code : true,
loops : true, loops : true,
booleans : true, booleans : true,
conditionals : true, conditionals : true,
evaluate : true, evaluate : true,
reduce_vars : true, side_effects : true,
}; };
input: { input: {
var unused; "use strict";
const CONST_FOO = false; while (!((foo && bar) || (x + "0"))) {
if (CONST_FOO) {
console.log("unreachable"); console.log("unreachable");
var moo; var foo;
function bar() {} function bar() {}
} }
for (var x = 10, y; x && (y || x) && (!typeof x); ++x) {
asdf();
foo();
var moo;
}
bar();
} }
expect: { expect: {
var unused; "use strict";
const CONST_FOO = !1; var foo;
// nothing for the while
// as for the for, it should keep:
var x = 10, y;
var moo; var moo;
function bar() {} bar();
}
expect_stdout: true
}
dead_code_const_annotation: {
options = {
dead_code : true,
loops : true,
booleans : true,
conditionals : true,
evaluate : true,
reduce_vars : true,
toplevel : true,
};
input: {
var unused;
/** @const */ var CONST_FOO_ANN = false;
if (CONST_FOO_ANN) {
console.log("unreachable");
var moo;
function bar() {}
}
}
expect: {
var unused;
var CONST_FOO_ANN = !1;
var moo;
function bar() {}
}
expect_stdout: true
}
dead_code_const_annotation_regex: {
options = {
dead_code : true,
loops : true,
booleans : true,
conditionals : true,
evaluate : true
};
input: {
var unused;
// @constraint this shouldn't be a constant
var CONST_FOO_ANN = false;
if (CONST_FOO_ANN) {
console.log("reachable");
}
}
expect: {
var unused;
var CONST_FOO_ANN = !1;
CONST_FOO_ANN && console.log('reachable');
}
expect_stdout: true
}
dead_code_const_annotation_complex_scope: {
options = {
dead_code : true,
loops : true,
booleans : true,
conditionals : true,
evaluate : true,
reduce_vars : true,
toplevel : true,
};
input: {
var unused_var;
/** @const */ var test = 'test';
// @const
var CONST_FOO_ANN = false;
var unused_var_2;
if (CONST_FOO_ANN) {
console.log("unreachable");
var moo;
function bar() {}
}
if (test === 'test') {
var beef = 'good';
/** @const */ var meat = 'beef';
var pork = 'bad';
if (meat === 'pork') {
console.log('also unreachable');
} else if (pork === 'good') {
console.log('reached, not const');
}
}
}
expect: {
var unused_var;
var test = 'test';
var CONST_FOO_ANN = !1;
var unused_var_2;
var moo;
function bar() {}
var beef = 'good';
var meat = 'beef';
var pork = 'bad';
} }
expect_stdout: true expect_stdout: true
node_version: ">=4"
} }
try_catch_finally: { try_catch_finally: {
@@ -278,3 +214,19 @@ try_catch_finally: {
"1", "1",
] ]
} }
accessor: {
options = {
side_effects: true,
}
input: {
({
get a() {},
set a(v){
this.b = 2;
},
b: 1
});
}
expect: {}
}

View File

@@ -1,332 +0,0 @@
destructuring_arrays: {
input: {
{const [aa, bb] = cc;}
{const [aa, [bb, cc]] = dd;}
{let [aa, bb] = cc;}
{let [aa, [bb, cc]] = dd;}
var [aa, bb] = cc;
var [aa, [bb, cc]] = dd;
var [,[,,,,,],,,zz,] = xx; // Trailing comma
var [,,zzz,,] = xxx; // Trailing comma after hole
}
expect: {
{const [aa, bb] = cc;}
{const [aa, [bb, cc]] = dd;}
{let [aa, bb] = cc;}
{let [aa, [bb, cc]] = dd;}
var [aa, bb] = cc;
var [aa, [bb, cc]] = dd;
var [,[,,,,,],,,zz] = xx;
var [,,zzz,,] = xxx;
}
}
destructuring_arrays_holes: {
input: {
var [,,,,] = a;
var [,,b,] = c;
var [d,,] = e;
}
expect_exact: "var[,,,,]=a;var[,,b]=c;var[d,,]=e;"
}
destructuring_objects: {
input: {
{const {aa, bb} = {aa:1, bb:2};}
{const {aa, bb: {cc, dd}} = {aa:1, bb: {cc:2, dd: 3}};}
{let {aa, bb} = {aa:1, bb:2};}
{let {aa, bb: {cc, dd}} = {aa:1, bb: {cc:2, dd: 3}};}
var {aa, bb} = {aa:1, bb:2};
var {aa, bb: {cc, dd}} = {aa:1, bb: {cc:2, dd: 3}};
}
expect: {
{const {aa, bb} = {aa:1, bb:2};}
{const {aa, bb: {cc, dd}} = {aa:1, bb: {cc:2, dd: 3}};}
{let {aa, bb} = {aa:1, bb:2};}
{let {aa, bb: {cc, dd}} = {aa:1, bb: {cc:2, dd: 3}};}
var {aa, bb} = {aa:1, bb:2};
var {aa, bb: {cc, dd}} = {aa:1, bb: {cc:2, dd: 3}};
}
}
destructuring_objects_trailing_elision: {
beautify = {
ecma: 6
}
input: {
var {cc,} = foo;
}
expect_exact: "var{cc}=foo;"
}
nested_destructuring_objects: {
beautify = {
ecma: 6
}
input: {
const [{a},b] = c;
let [{a},b] = c;
var [{a},b] = c;
}
expect_exact: 'const[{a},b]=c;let[{a},b]=c;var[{a},b]=c;';
}
destructuring_constdef_in_loops: {
beautify = {
ecma: 6
}
input: {
for (const [x,y] in pairs);
for (const [a] = 0;;);
for (const {c} of cees);
}
expect_exact: "for(const[x,y]in pairs);for(const[a]=0;;);for(const{c}of cees);"
}
destructuring_letdef_in_loops: {
beautify = {
ecma: 6
}
input: {
for (let [x,y] in pairs);
for (let [a] = 0;;);
for (let {c} of cees);
}
expect_exact: "for(let[x,y]in pairs);for(let[a]=0;;);for(let{c}of cees);"
}
destructuring_vardef_in_loops: {
beautify = {
ecma: 6
}
input: {
for (var [x,y] in pairs);
for (var [a] = 0;;);
for (var {c} of cees);
}
expect_exact: "for(var[x,y]in pairs);for(var[a]=0;;);for(var{c}of cees);"
}
destructuring_expressions: {
beautify = {
ecma: 6
}
input: {
({a, b});
[{a}];
f({x});
}
expect_exact: "({a,b});[{a}];f({x});"
}
destructuring_remove_unused_1: {
options = {
unused: true
}
input: {
function a() {
var unused = "foo";
var a = [1];
var [b] = a;
f(b);
}
function b() {
var unused = "foo";
var a = {b: 1};
var {b} = a;
f(b);
}
function c() {
var unused = "foo";
var a = [[1]];
var [[b]] = a;
f(b);
}
function d() {
var unused = "foo";
var a = {b: {b:1}};
var {b:{b}} = a;
f(b);
}
function e() {
var unused = "foo";
var a = [1, 2, 3, 4, 5];
var x = [[1, 2, 3]];
var y = {h: 1};
var [b, ...c] = a;
var [...[e, f]] = x;
var [...{g: h}] = y;
f(b, c, e, f, g);
}
}
expect: {
function a() {
var a = [1];
var [b] = a;
f(b);
}
function b() {
var a = {b: 1};
var {b} = a;
f(b);
}
function c() {
var a = [[1]];
var [[b]] = a;
f(b);
}
function d() {
var a = {b: {b:1}};
var {b:{b}} = a;
f(b);
}
function e() {
var a = [1, 2, 3, 4, 5];
var x = [[1, 2, 3]];
var y = {h: 1};
var [b, ...c] = a;
var [...[e, f]] = x;
var [...{g: h}] = y;
f(b, c, e, f, g);
}
}
}
destructuring_remove_unused_2: {
options = {
unused: true
}
input: {
function a() {
var unused = "foo";
var a = [,,1];
var [b] = a;
f(b);
}
function b() {
var unused = "foo";
var a = [{a: [1]}];
var [{b: a}] = a;
f(b);
}
}
expect: {
function a() {
var a = [,,1];
var [b] = a;
f(b);
}
function b() {
var a = [{a: [1]}];
var [{b: a}] = a;
f(b);
}
}
}
object_destructuring_may_need_parentheses: {
beautify = {
ecma: 6
}
input: {
({a, b} = {a: 1, b: 2});
}
expect_exact: "({a,b}={a:1,b:2});"
}
destructuring_with_undefined_as_default_assignment: {
options = {
evaluate: true
}
input: {
[foo = undefined] = bar;
[foo = void 0] = bar;
}
expect: {
[foo] = bar;
[foo] = bar;
}
}
destructuring_dont_evaluate_with_undefined_as_default_assignment: {
options = {
evaluate: false
}
input: {
[foo = undefined] = bar;
}
expect: {
[foo = void 0] = bar;
}
}
reduce_vars: {
options = {
reduce_vars: true,
}
input: {
{const [aa, [bb, cc]] = dd;}
{let [aa, [bb, cc]] = dd;}
var [aa, [bb, cc]] = dd;
[aa, [bb, cc]] = dd;
{const {aa, bb: {cc, dd}} = {aa:1, bb: {cc:2, dd: 3}};}
{let {aa, bb: {cc, dd}} = {aa:1, bb: {cc:2, dd: 3}};}
var {aa, bb: {cc, dd}} = {aa:1, bb: {cc:2, dd: 3}};
({aa, bb: {cc, dd}} = {aa:1, bb: {cc:2, dd: 3}});
const [{a},b] = c;
let [{a},b] = c;
var [{a},b] = c;
[{a},b] = c;
for (const [x,y] in pairs);
for (let [x,y] in pairs);
for (var [x,y] in pairs);
for ([x,y] in pairs);
}
expect: {
{const [aa, [bb, cc]] = dd;}
{let [aa, [bb, cc]] = dd;}
var [aa, [bb, cc]] = dd;
[aa, [bb, cc]] = dd;
{const {aa, bb: {cc, dd}} = {aa:1, bb: {cc:2, dd: 3}};}
{let {aa, bb: {cc, dd}} = {aa:1, bb: {cc:2, dd: 3}};}
var {aa, bb: {cc, dd}} = {aa:1, bb: {cc:2, dd: 3}};
({aa, bb: {cc, dd}} = {aa:1, bb: {cc:2, dd: 3}});
const [{a},b] = c;
let [{a},b] = c;
var [{a},b] = c;
[{a},b] = c;
for (const [x,y] in pairs);
for (let [x,y] in pairs);
for (var [x,y] in pairs);
for ([x,y] in pairs);
}
}
unused: {
options = {
unused: true,
}
input: {
let { foo: [, , ...a] } = { foo: [1, 2, 3, 4], bar: 5 };
console.log(a);
}
expect: {
let { foo: [, , ...a] } = { foo: [1, 2, 3, 4], bar: 5 };
console.log(a);
}
}
issue_1886: {
options = {
collapse_vars: true,
}
input: {
let [a] = [1];
console.log(a);
}
expect: {
let [a] = [1];
console.log(a);
}
expect_exact: "1"
}

View File

@@ -1,10 +0,0 @@
class_directives_compression: {
input: {
class foo {
foo() {
"use strict";
}
}
}
expect_exact: "class foo{foo(){}}"
}

View File

@@ -164,87 +164,6 @@ used_var_in_catch: {
} }
} }
unused_block_decls_in_catch: {
options = { unused: true };
input: {
function foo() {
try {
foo();
} catch(ex) {
let x = 10;
const y = 10;
class Zee {};
}
}
}
expect: {
function foo() {
try {
foo();
} catch(ex) {}
}
}
}
used_block_decls_in_catch: {
options = { unused: true };
input: {
function foo() {
try {
foo();
} catch(ex) {
let x = 10;
const y = 10;
class Zee {};
}
console.log(x, y, Zee);
}
}
expect: {
function foo() {
try {
foo();
} catch(ex) {}
console.log(x, y, Zee);
}
}
}
unused_block_decls: {
options = { unused: true };
input: {
function foo() {
{
const x;
}
{
let y;
}
console.log(x, y);
}
}
expect: {
function foo() {
console.log(x, y);
}
}
}
unused_keep_harmony_destructuring: {
options = { unused: true };
input: {
function foo() {
var {x, y} = foo;
var a = foo;
}
}
expect: {
function foo() {
var {x, y} = foo;
}
}
}
keep_fnames: { keep_fnames: {
options = { unused: true, keep_fnames: true, unsafe: true }; options = { unused: true, keep_fnames: true, unsafe: true };
input: { input: {
@@ -730,37 +649,6 @@ drop_value: {
} }
} }
const_assign: {
options = {
evaluate: true,
reduce_vars: true,
unused: true,
}
input: {
function f() {
const b = 2;
return 1 + b;
}
function g() {
const b = 2;
b = 3;
return 1 + b;
}
}
expect: {
function f() {
return 3;
}
function g() {
const b = 2;
b = 3;
return 1 + b;
}
}
}
issue_1539: { issue_1539: {
options = { options = {
cascade: true, cascade: true,
@@ -897,10 +785,6 @@ issue_1709: {
var x = 1; var x = 1;
return x; return x;
}(), }(),
function y() {
const y = 2;
return y;
}(),
function z() { function z() {
function z() {} function z() {}
return z; return z;
@@ -913,10 +797,6 @@ issue_1709: {
var x = 1; var x = 1;
return x; return x;
}(), }(),
function() {
const y = 2;
return y;
}(),
function() { function() {
function z() {} function z() {}
return z; return z;
@@ -1228,28 +1108,3 @@ var_catch_toplevel: {
}(); }();
} }
} }
reassign_const: {
options = {
cascade: true,
sequences: true,
side_effects: true,
unused: true,
}
input: {
function f() {
const a = 1;
a = 2;
return a;
}
console.log(f());
}
expect: {
function f() {
const a = 1;
return a = 2, a;
}
console.log(f());
}
expect_stdout: true
}

View File

@@ -224,100 +224,6 @@ positive_zero: {
expect_stdout: true expect_stdout: true
} }
pow: {
options = { evaluate: true }
input: {
var a = 5 ** 3;
}
expect: {
var a = 125;
}
}
pow_sequence: {
options = {
evaluate: true
}
input: {
var a = 2 ** 3 ** 2;
}
expect: {
var a = 512;
}
}
pow_mixed: {
options = {
evaluate: true
}
input: {
var a = 5 + 2 ** 3 + 5;
var b = 5 * 3 ** 2;
var c = 5 ** 3 * 2;
var d = 5 ** +3;
}
expect: {
var a = 18;
var b = 45;
var c = 250;
var d = 125;
}
}
pow_with_right_side_evaluating_to_unary: {
options = {
evaluate: true
}
input: {
var a = (4 - 7) ** foo;
var b = ++bar ** 3;
var c = --baz ** 2;
}
expect_exact: "var a=(-3)**foo;var b=++bar**3;var c=--baz**2;"
}
pow_with_number_constants: {
options = {
evaluate: true
}
input: {
var a = 5 ** NaN;
/* NaN exponent results to NaN */
var b = 42 ** +0;
/* +0 exponent results to NaN */
var c = 42 ** -0;
/* -0 exponent results to NaN */
var d = NaN ** 1;
/* NaN with non-zero exponent is NaN */
var e = 2 ** Infinity;
/* abs(base) > 1 with Infinity as exponent is Infinity */
var f = 2 ** -Infinity;
/* abs(base) > 1 with -Infinity as exponent is +0 */
var g = (-7) ** (0.5);
var h = 2324334 ** 34343443;
var i = (-2324334) ** 34343443;
var j = 2 ** (-3);
var k = 2.0 ** -3;
var l = 2.0 ** (5 - 7);
var m = 3 ** -10; // Result will be 0.000016935087808430286, which is too long
}
expect: {
var a = NaN;
var b = 1;
var c = 1;
var d = NaN;
var e = 1/0;
var f = 0;
var g = NaN;
var h = 1/0;
var i = -1/0;
var j = .125;
var k = .125;
var l = .25;
var m = 3 ** -10;
}
}
unsafe_constant: { unsafe_constant: {
options = { options = {
evaluate : true, evaluate : true,
@@ -739,16 +645,17 @@ call_args: {
options = { options = {
evaluate: true, evaluate: true,
reduce_vars: true, reduce_vars: true,
toplevel: true,
} }
input: { input: {
const a = 1; var a = 1;
console.log(a); console.log(a);
+function(a) { +function(a) {
return a; return a;
}(a); }(a);
} }
expect: { expect: {
const a = 1; var a = 1;
console.log(1); console.log(1);
+(1, 1); +(1, 1);
} }
@@ -760,17 +667,17 @@ call_args_drop_param: {
evaluate: true, evaluate: true,
keep_fargs: false, keep_fargs: false,
reduce_vars: true, reduce_vars: true,
toplevel: true,
unused: true, unused: true,
} }
input: { input: {
const a = 1; var a = 1;
console.log(a); console.log(a);
+function(a) { +function(a) {
return a; return a;
}(a, b); }(a, b);
} }
expect: { expect: {
const a = 1;
console.log(1); console.log(1);
+(b, 1); +(b, 1);
} }
@@ -873,13 +780,15 @@ unsafe_charAt_noop: {
input: { input: {
console.log( console.log(
s.charAt(0), s.charAt(0),
"string".charAt(x) "string".charAt(x),
(typeof x).charAt()
); );
} }
expect: { expect: {
console.log( console.log(
s.charAt(0), s.charAt(0),
"string".charAt(x) "string".charAt(x),
(typeof x)[0]
); );
} }
} }
@@ -1083,3 +992,78 @@ Infinity_NaN_undefined_LHS: {
"}", "}",
] ]
} }
issue_1964_1: {
options = {
evaluate: true,
reduce_vars: true,
unsafe_regexp: false,
unused: true,
}
input: {
function f() {
var long_variable_name = /\s/;
return "a b c".split(long_variable_name)[1];
}
console.log(f());
}
expect: {
function f() {
var long_variable_name = /\s/;
return "a b c".split(long_variable_name)[1];
}
console.log(f());
}
expect_stdout: "b"
}
issue_1964_2: {
options = {
evaluate: true,
reduce_vars: true,
unsafe_regexp: true,
unused: true,
}
input: {
function f() {
var long_variable_name = /\s/;
return "a b c".split(long_variable_name)[1];
}
console.log(f());
}
expect: {
function f() {
return "a b c".split(/\s/)[1];
}
console.log(f());
}
expect_stdout: "b"
}
array_slice_index: {
options = {
evaluate: true,
unsafe: true,
}
input: {
console.log([1,2,3].slice(1)[1]);
}
expect: {
console.log(3);
}
expect_stdout: "3"
}
string_charCodeAt: {
options = {
evaluate: true,
unsafe: true,
}
input: {
console.log("foo".charCodeAt("bar".length));
}
expect: {
console.log(NaN);
}
expect_stdout: "NaN"
}

View File

@@ -1,28 +0,0 @@
expand_arguments: {
input: {
func(a, ...rest);
func(...all);
}
expect_exact: "func(a,...rest);func(...all);"
}
expand_expression_arguments: {
input: {
f(...a.b);
f(...a.b());
f(...(a));
f(...(a.b));
f(...a[i]);
}
expect_exact: "f(...a.b);f(...a.b());f(...a);f(...a.b);f(...a[i]);"
}
expand_parameters: {
input: {
(function (a, ...b){});
(function (...args){});
}
expect_exact: "(function(a,...b){});(function(...args){});"
}

View File

@@ -1,52 +0,0 @@
pow: {
input: {
var a = 2 ** 7;
var b = 3;
b **= 2;
}
expect: {
var a = 2 ** 7;
var b = 3;
b **= 2;
}
}
pow_with_number_constants: {
input: {
var a = 5 ** NaN;
var b = 42 ** +0;
var c = 42 ** -0;
var d = NaN ** 1;
var e = 2 ** Infinity;
var f = 2 ** -Infinity;
}
expect: {
var a = 5 ** NaN;
var b = 42 ** +0;
var c = 42 ** -0;
var d = NaN ** 1;
var e = 2 ** (1/0);
var f = 2 ** (-1/0);
}
}
pow_with_parentheses: {
input: {
var g = (-7) ** (0.5);
var h = 2324334 ** 34343443;
var i = (-2324334) ** 34343443;
var j = 2 ** (-3);
var k = 2.0 ** -3;
var l = 2.0 ** (5 - 7);
}
expect_exact: "var g=(-7)**.5;var h=2324334**34343443;var i=(-2324334)**34343443;var j=2**-3;var k=2**-3;var l=2**(5-7);"
}
pow_with_unary_between_brackets: {
input: {
var a = (-(+5)) ** 3;
}
expect: {
var a = (-+5)**3;
}
}

View File

@@ -167,3 +167,103 @@ function_returning_constant_literal: {
} }
expect_stdout: "Hello there" expect_stdout: "Hello there"
} }
hoist_funs: {
options = {
hoist_funs: true,
}
input: {
console.log(1, typeof f, typeof g);
if (console.log(2, typeof f, typeof g))
console.log(3, typeof f, typeof g);
else {
console.log(4, typeof f, typeof g);
function f() {}
console.log(5, typeof f, typeof g);
}
function g() {}
console.log(6, typeof f, typeof g);
}
expect: {
function f() {}
function g() {}
console.log(1, typeof f, typeof g);
if (console.log(2, typeof f, typeof g))
console.log(3, typeof f, typeof g);
else {
console.log(4, typeof f, typeof g);
console.log(5, typeof f, typeof g);
}
console.log(6, typeof f, typeof g);
}
expect_stdout: [
"1 'function' 'function'",
"2 'function' 'function'",
"4 'function' 'function'",
"5 'function' 'function'",
"6 'function' 'function'",
]
node_version: "<=4"
}
hoist_funs_strict: {
options = {
hoist_funs: true,
}
input: {
"use strict";
console.log(1, typeof f, typeof g);
if (console.log(2, typeof f, typeof g))
console.log(3, typeof f, typeof g);
else {
console.log(4, typeof f, typeof g);
function f() {}
console.log(5, typeof f, typeof g);
}
function g() {}
console.log(6, typeof f, typeof g);
}
expect: {
"use strict";
function g() {}
console.log(1, typeof f, typeof g);
if (console.log(2, typeof f, typeof g))
console.log(3, typeof f, typeof g);
else {
console.log(4, typeof f, typeof g);
function f() {}
console.log(5, typeof f, typeof g);
}
console.log(6, typeof f, typeof g);
}
expect_stdout: [
"1 'undefined' 'function'",
"2 'undefined' 'function'",
"4 'function' 'function'",
"5 'function' 'function'",
"6 'undefined' 'function'",
]
node_version: ">=4"
}
issue_203: {
options = {
keep_fargs: false,
side_effects: true,
unsafe_Func: true,
unused: true,
}
input: {
var m = {};
var fn = Function("require", "module", "exports", "module.exports = 42;");
fn(null, m, m.exports);
console.log(m.exports);
}
expect: {
var m = {};
var fn = Function("a", "b", "b.exports=42");
fn(null, m, m.exports);
console.log(m.exports);
}
expect_stdout: "42"
}

View File

@@ -120,7 +120,7 @@ mixed: {
properties: true, properties: true,
} }
input: { input: {
const FOO = { BAR: 0 }; var FOO = { BAR: 0 };
console.log(FOO.BAR); console.log(FOO.BAR);
console.log(++CONFIG.DEBUG); console.log(++CONFIG.DEBUG);
console.log(++CONFIG.VALUE); console.log(++CONFIG.VALUE);
@@ -130,7 +130,7 @@ mixed: {
console.log(CONFIG); console.log(CONFIG);
} }
expect: { expect: {
const FOO = { BAR: 0 }; var FOO = { BAR: 0 };
console.log("moo"); console.log("moo");
console.log(++CONFIG.DEBUG); console.log(++CONFIG.DEBUG);
console.log(++CONFIG.VALUE); console.log(++CONFIG.VALUE);
@@ -160,3 +160,17 @@ issue_1801: {
console.log(!0); console.log(!0);
} }
} }
issue_1986: {
options = {
global_defs: {
"@alert": "console.log",
},
}
input: {
alert(42);
}
expect: {
console.log(42);
}
}

View File

@@ -1,484 +0,0 @@
arrow_function_parens: {
input: {
something && (() => {});
}
expect_exact: "something&&(()=>{});"
}
arrow_function_parens_2: {
input: {
(() => null)();
}
expect_exact: "(()=>null)();"
}
typeof_arrow_functions: {
options = {
evaluate: true
}
input: {
var foo = typeof (x) => null;
}
expect_exact: "var foo=\"function\";"
}
classes: {
input: {
class SomeClass {
constructor() {
};
foo() {};
};
class NoSemi {
constructor(...args) {
}
foo() {}
};
class ChildClass extends SomeClass {};
var asExpression = class AsExpression {};
var nameless = class {};
}
expect_exact: "class SomeClass{constructor(){}foo(){}}class NoSemi{constructor(...args){}foo(){}}class ChildClass extends SomeClass{}var asExpression=class AsExpression{};var nameless=class{};"
}
class_statics: {
input: {
x = class {
static staticMethod() {}
static get foo() {}
static set bar() {}
static() { /* "static" can be a method name! */ }
get() { /* "get" can be a method name! */ }
set() { /* "set" can be a method name! */ }
}
}
expect_exact: "x=class{static staticMethod(){}static get foo(){}static set bar(){}static(){}get(){}set(){}};"
}
class_name_can_be_mangled: {
mangle = { };
input: {
function x() {
class Foo {
}
var class1 = Foo
var class2 = class Bar {}
}
}
expect: {
function x() {
class a { }
var n = a
var r = class a {}
}
}
}
class_name_can_be_preserved: {
mangle = {
keep_classnames: true
}
input: {
function x() {
(class Baz { });
class Foo {};
}
}
expect: {
function x() {
(class Baz { });
class Foo {};
}
}
}
classes_can_have_generators: {
input: {
class Foo {
*bar() {}
static *baz() {}
}
}
expect: {
class Foo {
*bar() {}
static *baz() {}
}
}
}
classes_can_have_computed_generators: {
input: {
class C4 {
*['constructor']() {}
}
}
expect: {
class C4 {
*['constructor']() {}
}
}
}
classes_can_have_computed_static: {
input: {
class C4 {
static ['constructor']() {}
}
}
expect: {
class C4 {
static ['constructor']() {}
}
}
}
class_methods_and_getters_with_keep_quoted_props_enabled: {
beautify = {
quote_style: 3,
keep_quoted_props: true,
}
input: {
class clss {
a() {}
"b"() {}
get c() { return "c"}
get "d"() { return "d"}
set e(a) { doSomething(a); }
set 'f'(a) { doSomething(b); }
static g() {}
static "h"() {}
}
}
expect_exact: 'class clss{a(){}"b"(){}get c(){return"c"}get"d"(){return"d"}set e(a){doSomething(a)}set\'f\'(a){doSomething(b)}static g(){}static"h"(){}}'
}
classes_with_expression_as_expand: {
input: {
class D extends (calls++, C) {}
}
expect_exact: "class D extends(calls++,C){}"
}
new_target: {
input: {
new.target;
new.target.name;
}
expect_exact: "new.target;new.target.name;"
}
number_literals: {
input: {
0b1001;
0B1001;
0o11;
0O11;
}
expect: {
9;
9;
9;
9;
}
}
import_statement: {
input: {
import "mod-name";
import Foo from "bar";
import { Bar, Baz } from 'lel';
import Bar, { Foo } from 'lel';
import { Bar as kex, Baz as food } from 'lel';
}
expect_exact: 'import"mod-name";import Foo from"bar";import{Bar,Baz}from"lel";import Bar,{Foo}from"lel";import{Bar as kex,Baz as food}from"lel";'
}
import_all_statement: {
input: {
import * from 'lel';
import * as Lel from 'lel';
}
expect_exact: 'import*from"lel";import*as Lel from"lel";'
}
export_statement: {
input: {
export default 1;
export var foo = 4;
export let foo = 6;
export const foo = 6;
export function foo() {};
export class foo { };
}
expect_exact: "export default 1;export var foo=4;export let foo=6;export const foo=6;export function foo(){};export class foo{};"
}
export_module_statement: {
input: {
export * from "a.js";
export {A} from "a.js";
export {A, B} from "a.js";
export {C};
}
expect_exact: 'export*from"a.js";export{A}from"a.js";export{A,B}from"a.js";export{C};'
}
import_statement_mangling: {
mangle = { toplevel: true };
input: {
import Foo from "foo";
import Bar, {Food} from "lel";
import {What as Whatever} from "lel";
Foo();
Bar();
Food();
Whatever();
}
expect: {
import l from "foo";
import e, {Food as o} from "lel";
import {What as f} from "lel";
l();
e();
o();
f();
}
}
export_statement_mangling: {
mangle = { };
input: {
export var foo = 6;
export function bar() { }
export class Baz { }
bar(foo, Baz)
}
expect: {
export var foo = 6;
export function bar() { }
export class Baz { }
bar(foo, Baz)
}
}
// https://github.com/mishoo/UglifyJS2/issues/1021
regression_for_of_const: {
input: {
for (const x of y) {}
for (const x in y) {}
}
expect: {
for (const x of y);for (const x in y);
}
}
// Fabio: My patches accidentally caused a crash whenever
// there's an extraneous set of parens around an object.
regression_cannot_destructure: {
input: {
var x = ({ x : 3 });
x(({ x: 3 }));
}
expect_exact: "var x={x:3};x({x:3});";
}
regression_cannot_use_of: {
input: {
function of() {
}
var of = "is a valid variable name";
of = { of: "is ok" };
x.of;
of: foo()
}
expect: {
function of(){}
var of="is a valid variable name";
of={of:"is ok"};
x.of;
foo(); /* Label statement missing? No prob. */
}
}
fat_arrow_as_param: {
input: {
foo(x => x);
foo(x => x, y => y);
foo(x => (x, x));
foo(x => (x, x), y => (y, y));
}
expect_exact: "foo(x=>x);foo(x=>x,y=>y);foo(x=>(x,x));foo(x=>(x,x),y=>(y,y));"
}
default_assign: {
options = {
keep_fargs: false,
unused: true,
}
input: {
function f(a, b = 3) {
console.log(a);
}
g = ([[] = 123]) => {};
h = ([[x, y, z] = [4, 5, 6]] = []) => {};
function i([[x, y, z] = [4, 5, 6]] = []) {
console.log(b);
};
}
expect: {
function f(a) {
console.log(a);
}
g = ([[] = 123]) => {};
h = ([[x, y, z] = [4, 5, 6]] = []) => {};
function i([[x, y, z] = [4, 5, 6]] = []) {
console.log(b);
};
}
}
expansion: {
options = {
keep_fargs: false,
unused: true,
}
input: {
function f(a, ...b) {
console.log(a);
}
}
expect: {
function f(a) {
console.log(a);
}
}
}
issue_1613: {
mangle = { toplevel: true };
input: {
const name = 1;
const foo = {
name
};
}
expect_exact: "const n=1;const c={name:n};"
}
format_methods: {
beautify = {
beautify: true,
}
input: {
class A extends B {constructor(a){x()} static s(b,c){y()} run(d,e,f){z()}}
}
expect_exact: [
"class A extends B {",
" constructor(a) {",
" x();",
" }",
" static s(b, c) {",
" y();",
" }",
" run(d, e, f) {",
" z();",
" }",
"}",
]
}
issue_1898: {
options = {
}
mangle = {
}
input: {
class Foo {
bar() {
for (const x of [ 6, 5 ]) {
for (let y of [ 4, 3 ]) {
for (var z of [ 2, 1 ]) {
console.log(x, y, z);
}
}
}
}
}
new Foo().bar();
}
expect: {
class Foo {
bar() {
for (const n of [ 6, 5 ])
for (let r of [ 4, 3 ])
for (var o of [ 2, 1 ])
console.log(n, r, o);
}
}
new Foo().bar();
}
}
issue_1753: {
mangle = { safari10: true };
input: {
class SomeClass {
constructor(props) {
let pickedSets = [];
for (let i = 0; i < 6; i++) {
pickedSets.push({
mainDrawNumbers: [],
extraDrawNumbers: []
});
}
}
}
}
expect: {
class SomeClass {
constructor(r) {
let a = [];
for (let s = 0; s < 6; s++)
a.push({
mainDrawNumbers: [],
extraDrawNumbers: []
});
}
}
}
}
issue_1753_disable: {
mangle = { safari10: false }
input: {
class SomeClass {
constructor(props) {
let pickedSets = [];
for (let i = 0; i < 6; i++) {
pickedSets.push({
mainDrawNumbers: [],
extraDrawNumbers: []
});
}
}
}
}
expect: {
class SomeClass {
constructor(r) {
let a = [];
for (let r = 0; r < 6; r++)
a.push({
mainDrawNumbers: [],
extraDrawNumbers: []
});
}
}
}
}

View File

@@ -1,85 +0,0 @@
hoist_vars: {
options = {
hoist_vars: true
}
input: {
function a() {
bar();
var var1;
var var2;
}
function b(anArg) {
bar();
var var1;
var anArg;
}
}
expect: {
function a() {
var var1, var2; // Vars go up and are joined
bar();
}
function b(anArg) {
var var1;
bar();
// But vars named like arguments go away!
}
}
}
hoist_funs: {
options = {
hoist_funs: true
}
input: {
function a() {
bar();
function foo() {}
}
}
expect: {
function a() {
function foo() {} // Funs go up
bar();
}
}
}
hoist_no_destructurings: {
options = {
hoist_vars: true,
hoist_funs: true
}
input: {
function a([anArg]) {
bar();
var var1;
var anArg; // Because anArg is already declared, this goes away!
}
}
expect: {
function a([anArg]) {
var var1;
bar();
}
}
}
dont_hoist_var_destructurings: {
options = {
hoist_vars: true,
hoist_funs: true
}
input: {
function x() {
// If foo is null or undefined, this should be an exception
var {x,y} = foo;
}
}
expect: {
function x() {
var {x,y} = foo;
}
}
}

View File

@@ -53,12 +53,3 @@ html_comment_in_string_literal: {
} }
expect_exact: 'function f(){return"\\x3c!--HTML--\\x3ecomment in\\x3c!--string literal--\\x3e"}'; expect_exact: 'function f(){return"\\x3c!--HTML--\\x3ecomment in\\x3c!--string literal--\\x3e"}';
} }
html_comment_after_multiline_comment: {
input: {
var foo; /*
*/--> var bar;
var foobar;
}
expect_exact: "var foo;var foobar;"
}

View File

@@ -302,3 +302,27 @@ issue_1437_conditionals: {
} }
} }
} }
issue_512: {
options = {
conditionals: true,
if_return: true,
sequences: true,
side_effects: true,
}
input: {
function a() {
if (b()) {
c();
return;
}
throw e;
}
}
expect: {
function a() {
if (!b()) throw e;
c();
}
}
}

View File

@@ -1,8 +0,0 @@
parenthesis_strings_in_parenthesis: {
input: {
var foo = ('(');
a(')');
}
expect_exact: 'var foo="(";a(")");'
}

View File

@@ -116,3 +116,137 @@ non_hoisted_function_after_return_2b: {
"WARN: Dropping unreachable code [test/compress/issue-1034.js:101,12]", "WARN: Dropping unreachable code [test/compress/issue-1034.js:101,12]",
] ]
} }
non_hoisted_function_after_return_strict: {
options = {
hoist_funs: false, dead_code: true, conditionals: true, comparisons: true,
evaluate: true, booleans: true, loops: true, unused: true, keep_fargs: true,
if_return: true, join_vars: true, cascade: true, side_effects: true
}
input: {
"use strict";
function foo(x) {
if (x) {
return bar();
not_called1();
} else {
return baz();
not_called2();
}
function bar() { return 7; }
return not_reached;
function UnusedFunction() {}
function baz() { return 8; }
}
console.log(foo(0), foo(1));
}
expect: {
"use strict";
function foo(x) {
return x ? bar() : baz();
function bar() { return 7 }
function baz() { return 8 }
}
console.log(foo(0), foo(1));
}
expect_stdout: "8 7"
expect_warnings: [
'WARN: Dropping unreachable code [test/compress/issue-1034.js:131,16]',
"WARN: Dropping unreachable code [test/compress/issue-1034.js:134,16]",
"WARN: Dropping unreachable code [test/compress/issue-1034.js:137,12]",
"WARN: Dropping unused function UnusedFunction [test/compress/issue-1034.js:138,21]"
]
}
non_hoisted_function_after_return_2a_strict: {
options = {
hoist_funs: false, dead_code: true, conditionals: true, comparisons: true,
evaluate: true, booleans: true, loops: true, unused: true, keep_fargs: true,
if_return: true, join_vars: true, cascade: true, side_effects: true,
collapse_vars: false, passes: 2, warnings: "verbose"
}
input: {
"use strict";
function foo(x) {
if (x) {
return bar(1);
var a = not_called(1);
} else {
return bar(2);
var b = not_called(2);
}
var c = bar(3);
function bar(x) { return 7 - x; }
function nope() {}
return b || c;
}
console.log(foo(0), foo(1));
}
expect: {
"use strict";
function foo(x) {
return bar(x ? 1 : 2);
function bar(x) {
return 7 - x;
}
}
console.log(foo(0), foo(1));
}
expect_stdout: "5 6"
expect_warnings: [
"WARN: Dropping unreachable code [test/compress/issue-1034.js:173,16]",
"WARN: Declarations in unreachable code! [test/compress/issue-1034.js:173,16]",
"WARN: Dropping unreachable code [test/compress/issue-1034.js:176,16]",
"WARN: Declarations in unreachable code! [test/compress/issue-1034.js:176,16]",
"WARN: Dropping unused variable a [test/compress/issue-1034.js:173,20]",
"WARN: Dropping unused function nope [test/compress/issue-1034.js:180,21]",
"WARN: Dropping unreachable code [test/compress/issue-1034.js:178,12]",
"WARN: Declarations in unreachable code! [test/compress/issue-1034.js:178,12]",
"WARN: Dropping unreachable code [test/compress/issue-1034.js:181,12]",
"WARN: Dropping unused variable b [test/compress/issue-1034.js:176,20]",
"WARN: Dropping unused variable c [test/compress/issue-1034.js:178,16]",
]
}
non_hoisted_function_after_return_2b_strict: {
options = {
hoist_funs: false, dead_code: true, conditionals: true, comparisons: true,
evaluate: true, booleans: true, loops: true, unused: true, keep_fargs: true,
if_return: true, join_vars: true, cascade: true, side_effects: true,
collapse_vars: false
}
input: {
"use strict";
function foo(x) {
if (x) {
return bar(1);
} else {
return bar(2);
var b;
}
var c = bar(3);
function bar(x) {
return 7 - x;
}
return b || c;
}
console.log(foo(0), foo(1));
}
expect: {
"use strict";
function foo(x) {
return bar(x ? 1 : 2);
function bar(x) { return 7 - x; }
}
console.log(foo(0), foo(1));
}
expect_stdout: "5 6"
expect_warnings: [
// duplicate warnings no longer emitted
"WARN: Dropping unreachable code [test/compress/issue-1034.js:225,16]",
"WARN: Declarations in unreachable code! [test/compress/issue-1034.js:225,16]",
"WARN: Dropping unreachable code [test/compress/issue-1034.js:227,12]",
"WARN: Declarations in unreachable code! [test/compress/issue-1034.js:227,12]",
"WARN: Dropping unreachable code [test/compress/issue-1034.js:231,12]",
]
}

View File

@@ -1,16 +1,3 @@
const_declaration: {
options = {
evaluate: true
};
input: {
const goog = goog || {};
}
expect: {
const goog = goog || {};
}
}
const_pragma: { const_pragma: {
options = { options = {
evaluate: true, evaluate: true,

View File

@@ -1,30 +0,0 @@
issue_1043: {
options = {
side_effects: true
};
input: {
function* range(start = 0, end = null, step = 1) {
if (end == null) {
end = start;
start = 0;
}
for (let i = start; i < end; i += step) {
yield i;
}
}
}
expect: {
function* range(start = 0, end = null, step = 1) {
if (null == end) {
end = start;
start = 0;
}
for (let i = start; i < end; i += step)
yield i;
}
}
}

View File

@@ -1,9 +0,0 @@
issue_1044: {
options = { evaluate: true, conditionals: true };
input: {
const mixed = Base ? class extends Base {} : class {}
}
expect: {
const mixed = Base ? class extends Base {} : class {}
}
}

View File

@@ -1,90 +1,91 @@
multiple_functions: { multiple_functions: {
options = { if_return: true, hoist_funs: false }; options = {
hoist_funs: false,
if_return: true,
}
input: { input: {
( function() { ( function() {
if ( !window ) { if ( !window ) {
return; return;
} }
function f() {} function f() {}
function g() {} function g() {}
} )(); } )();
} }
expect: { expect: {
( function() { ( function() {
function f() {}
function g() {}
// NOTE: other compression steps will reduce this // NOTE: other compression steps will reduce this
// down to just `window`. // down to just `window`.
if ( window ); if ( window );
function f() {}
function g() {}
} )(); } )();
} }
} }
single_function: { single_function: {
options = { if_return: true, hoist_funs: false }; options = {
hoist_funs: false,
if_return: true,
}
input: { input: {
( function() { ( function() {
if ( !window ) { if ( !window ) {
return; return;
} }
function f() {} function f() {}
} )(); } )();
} }
expect: { expect: {
( function() { ( function() {
function f() {}
if ( window ); if ( window );
function f() {}
} )(); } )();
} }
} }
deeply_nested: { deeply_nested: {
options = { if_return: true, hoist_funs: false }; options = {
hoist_funs: false,
if_return: true,
}
input: { input: {
( function() { ( function() {
if ( !window ) { if ( !window ) {
return; return;
} }
function f() {} function f() {}
function g() {} function g() {}
if ( !document ) { if ( !document ) {
return; return;
} }
function h() {} function h() {}
} )(); } )();
} }
expect: { expect: {
( function() { ( function() {
function f() {}
function g() {}
function h() {}
// NOTE: other compression steps will reduce this // NOTE: other compression steps will reduce this
// down to just `window`. // down to just `window`.
if ( window ) if ( window )
if (document); if (document);
function f() {}
function g() {}
function h() {}
} )(); } )();
} }
} }
not_hoisted_when_already_nested: { not_hoisted_when_already_nested: {
options = { if_return: true, hoist_funs: false }; options = {
hoist_funs: false,
if_return: true,
}
input: { input: {
( function() { ( function() {
if ( !window ) { if ( !window ) {
return; return;
} }
if ( foo ) function f() {} if ( foo ) function f() {}
} )(); } )();
} }
expect: { expect: {
@@ -94,3 +95,70 @@ not_hoisted_when_already_nested: {
} )(); } )();
} }
} }
defun_if_return: {
options = {
hoist_funs: false,
if_return: true,
}
input: {
function e() {
function f() {}
if (!window) return;
else function g() {}
function h() {}
}
}
expect: {
function e() {
function f() {}
if (window) function g() {}
function h() {}
}
}
}
defun_hoist_funs: {
options = {
hoist_funs: true,
if_return: true,
}
input: {
function e() {
function f() {}
if (!window) return;
else function g() {}
function h() {}
}
}
expect: {
function e() {
function f() {}
function g() {}
function h() {}
if (window);
}
}
}
defun_else_if_return: {
options = {
hoist_funs: false,
if_return: true,
}
input: {
function e() {
function f() {}
if (window) function g() {}
else return;
function h() {}
}
}
expect: {
function e() {
function f() {}
if (window) function g() {}
function h() {}
}
}
}

View File

@@ -1,76 +0,0 @@
issue_1212_debug_false: {
options = {
global_defs : { DEBUG: false },
sequences : true,
properties : true,
dead_code : true,
conditionals : true,
comparisons : true,
evaluate : true,
booleans : true,
loops : true,
unused : true,
hoist_funs : true,
keep_fargs : true,
if_return : true,
join_vars : true,
cascade : true,
side_effects : true,
}
input: {
class foo {
bar() {
if (DEBUG)
console.log("DEV");
else
console.log("PROD");
}
}
new foo().bar();
}
expect: {
class foo{
bar() { console.log("PROD") }
}
(new foo).bar();
}
}
issue_1212_debug_true: {
options = {
global_defs : { DEBUG: true },
sequences : true,
properties : true,
dead_code : true,
conditionals : true,
comparisons : true,
evaluate : true,
booleans : true,
loops : true,
unused : true,
hoist_funs : true,
keep_fargs : true,
if_return : true,
join_vars : true,
cascade : true,
side_effects : true,
}
input: {
class foo {
bar() {
if (DEBUG)
console.log("DEV");
else
console.log("PROD");
}
}
new foo().bar();
}
expect: {
class foo{
bar() { console.log("DEV") }
}
(new foo).bar();
}
}

View File

@@ -1,291 +0,0 @@
same_variable_in_multiple_for_loop: {
options = {
hoist_funs: true,
dead_code: true,
conditionals: true,
comparisons: true,
evaluate: true,
booleans: true,
loops: true,
unused: true,
keep_fargs: true,
if_return: true,
join_vars: true,
cascade: true,
side_effects: true,
collapse_vars: true,
}
mangle = {}
input: {
for (let i = 0; i < 3; i++) {
let a = 100;
console.log(i, a);
for (let i = 0; i < 2; i++) {
console.log(i, a);
let c = 2;
console.log(c);
}
}
}
expect: {
for (let o = 0; o < 3; o++) {
let l = 100;
console.log(o, l);
for (let o = 0; o < 2; o++) {
console.log(o, l);
let c = 2;
console.log(c);
}
}
}
expect_stdout: true
}
same_variable_in_multiple_forOf: {
options = {
hoist_funs: true,
dead_code: true,
conditionals: true,
comparisons: true,
evaluate: true,
booleans: true,
loops: true,
unused: true,
keep_fargs: true,
if_return: true,
join_vars: true,
cascade: true,
side_effects: true,
collapse_vars: true,
}
mangle = {}
input: {
var test = [ "a", "b", "c" ];
for (let tmp of test) {
console.log(tmp);
let dd;
dd = [ "e", "f", "g" ];
for (let tmp of dd) {
console.log(tmp);
}
}
}
expect: {
var test = [ "a", "b", "c" ];
for (let o of test) {
console.log(o);
let e;
e = [ "e", "f", "g" ];
for (let o of e)
console.log(o);
}
}
expect_stdout: true
}
same_variable_in_multiple_forIn: {
options = {
hoist_funs: true,
dead_code: true,
conditionals: true,
comparisons: true,
evaluate: true,
booleans: true,
loops: true,
unused: false,
keep_fargs: true,
if_return: true,
join_vars: true,
cascade: true,
side_effects: true,
collapse_vars: true,
}
mangle = {}
input: {
var test = [ "a", "b", "c" ];
for (let tmp in test) {
console.log(tmp);
let dd;
dd = [ "e", "f", "g" ];
for (let tmp in test) {
console.log(tmp);
}
}
}
expect: {
var test = [ "a", "b", "c" ];
for (let o in test) {
console.log(o);
let e;
e = [ "e", "f", "g" ];
for (let o in test)
console.log(o);
}
}
expect_stdout: true
}
different_variable_in_multiple_for_loop: {
options = {
hoist_funs: true,
dead_code: true,
conditionals: true,
comparisons: true,
evaluate: true,
booleans: true,
loops: true,
unused: true,
keep_fargs: true,
if_return: true,
join_vars: true,
cascade: true,
side_effects: true,
collapse_vars: true,
}
mangle = {}
input: {
for (let i = 0; i < 3; i++) {
let a = 100;
console.log(i, a);
for (let j = 0; j < 2; j++) {
console.log(j, a);
let c = 2;
console.log(c);
}
}
}
expect: {
for (let o = 0; o < 3; o++) {
let l = 100;
console.log(o, l);
for (let o = 0; o < 2; o++) {
console.log(o, l);
let c = 2;
console.log(c);
}
}
}
expect_stdout: true
}
different_variable_in_multiple_forOf: {
options = {
hoist_funs: true,
dead_code: true,
conditionals: true,
comparisons: true,
evaluate: true,
booleans: true,
loops: true,
unused: true,
keep_fargs: true,
if_return: true,
join_vars: true,
cascade: true,
side_effects: true,
collapse_vars: true,
}
mangle = {}
input: {
var test = [ "a", "b", "c" ];
for (let tmp of test) {
console.log(tmp);
let dd;
dd = [ "e", "f", "g" ];
for (let t of dd) {
console.log(t);
}
}
}
expect: {
var test = [ "a", "b", "c" ];
for (let o of test) {
console.log(o);
let e;
e = [ "e", "f", "g" ];
for (let o of e)
console.log(o);
}
}
expect_stdout: true
}
different_variable_in_multiple_forIn: {
options = {
hoist_funs: true,
dead_code: true,
conditionals: true,
comparisons: true,
evaluate: true,
booleans: true,
loops: true,
unused: false,
keep_fargs: true,
if_return: true,
join_vars: true,
cascade: true,
side_effects: true,
collapse_vars: true,
}
mangle = {}
input: {
var test = [ "a", "b", "c" ];
for (let tmp in test) {
console.log(tmp);
let dd;
dd = [ "e", "f", "g" ];
for (let t in test) {
console.log(t);
}
}
}
expect: {
var test = [ "a", "b", "c" ];
for (let o in test) {
console.log(o);
let e;
e = [ "e", "f", "g" ];
for (let o in test)
console.log(o);
}
}
expect_stdout: true
}
more_variable_in_multiple_for: {
options = {
hoist_funs: true,
dead_code: true,
conditionals: true,
comparisons: true,
evaluate: true,
booleans: true,
loops: true,
unused: false,
keep_fargs: true,
if_return: true,
join_vars: true,
cascade: true,
side_effects: true,
collapse_vars: true,
}
mangle = {}
input: {
for (let a = 9, i = 0; i < 20; i += a) {
let b = a++ + i;
console.log(a, b, i);
for (let k = b, m = b*b, i = 0; i < 10; i++) {
console.log(a, b, m, k, i);
}
}
}
expect: {
for (let o = 9, l = 0; l < 20; l += o) {
let c = o++ + l;
console.log(o, c, l);
for (let l = c, e = c * c, f = 0; f < 10; f++)
console.log(o, c, e, l, f);
}
}
expect_stdout: true
}

View File

@@ -85,15 +85,3 @@ unsafe_undefined: {
} }
expect_stdout: true expect_stdout: true
} }
runtime_error: {
input: {
const a = 1;
console.log(a++);
}
expect: {
const a = 1;
console.log(a++);
}
expect_stdout: true
}

View File

@@ -103,3 +103,137 @@ numeric_literal: {
"8 7 8", "8 7 8",
] ]
} }
identifier: {
mangle_props = {}
input: {
var obj = {
abstract: 1,
boolean: 2,
byte: 3,
char: 4,
class: 5,
double: 6,
enum: 7,
export: 8,
extends: 9,
final: 10,
float: 11,
goto: 12,
implements: 13,
import: 14,
int: 15,
interface: 16,
let: 17,
long: 18,
native: 19,
package: 20,
private: 21,
protected: 22,
public: 23,
short: 24,
static: 25,
super: 26,
synchronized: 27,
this: 28,
throws: 29,
transient: 30,
volatile: 31,
yield: 32,
false: 33,
null: 34,
true: 35,
break: 36,
case: 37,
catch: 38,
const: 39,
continue: 40,
debugger: 41,
default: 42,
delete: 43,
do: 44,
else: 45,
finally: 46,
for: 47,
function: 48,
if: 49,
in: 50,
instanceof: 51,
new: 52,
return: 53,
switch: 54,
throw: 55,
try: 56,
typeof: 57,
var: 58,
void: 59,
while: 60,
with: 61,
};
}
expect: {
var obj = {
a: 1,
b: 2,
c: 3,
d: 4,
e: 5,
f: 6,
g: 7,
h: 8,
i: 9,
j: 10,
k: 11,
l: 12,
m: 13,
n: 14,
o: 15,
p: 16,
q: 17,
r: 18,
s: 19,
t: 20,
u: 21,
v: 22,
w: 23,
x: 24,
y: 25,
z: 26,
A: 27,
B: 28,
C: 29,
D: 30,
F: 31,
G: 32,
false: 33,
null: 34,
true: 35,
H: 36,
I: 37,
J: 38,
K: 39,
L: 40,
M: 41,
N: 42,
O: 43,
P: 44,
Q: 45,
R: 46,
S: 47,
T: 48,
U: 49,
V: 50,
W: 51,
X: 52,
Y: 53,
Z: 54,
$: 55,
_: 56,
aa: 57,
ba: 58,
ca: 59,
da: 60,
ea: 61,
};
}
}

View File

@@ -0,0 +1,31 @@
operator: {
input: {
a. //comment
typeof
}
expect_exact: "a.typeof;"
}
name: {
input: {
a. //comment
b
}
expect_exact: "a.b;"
}
keyword: {
input: {
a. //comment
default
}
expect_exact: "a.default;"
}
atom: {
input: {
a. //comment
true
}
expect_exact: "a.true;"
}

View File

@@ -1,34 +0,0 @@
compress_new_function: {
options = {
unsafe: true
}
input: {
new Function("aa, bb", 'return aa;');
}
expect: {
Function("a", "b", "return a");
}
}
compress_new_function_with_destruct: {
options = {
unsafe: true,
ecma: 6
}
beautify = {
ecma: 6
}
input: {
new Function("aa, [bb]", 'return aa;');
new Function("aa, {bb}", 'return aa;');
new Function("[[aa]], [{bb}]", 'return aa;');
}
expect: {
Function("a", "[b]", "return a");
Function("a", "{bb}", "return a");
Function("[[a]]", "[{bb}]", 'return a');
}
}

View File

@@ -38,7 +38,7 @@ mixed: {
} }
} }
input: { input: {
const ENV = 3; var ENV = 3;
var FOO = 4; var FOO = 4;
f(ENV * 10); f(ENV * 10);
--FOO; --FOO;
@@ -49,7 +49,7 @@ mixed: {
x = DEBUG; x = DEBUG;
} }
expect: { expect: {
const ENV = 3; var ENV = 3;
var FOO = 4; var FOO = 4;
f(10); f(10);
--FOO; --FOO;
@@ -60,7 +60,7 @@ mixed: {
x = 0; x = 0;
} }
expect_warnings: [ expect_warnings: [
'WARN: global_defs ENV redefined [test/compress/issue-208.js:41,14]', 'WARN: global_defs ENV redefined [test/compress/issue-208.js:41,12]',
'WARN: global_defs FOO redefined [test/compress/issue-208.js:42,12]', 'WARN: global_defs FOO redefined [test/compress/issue-208.js:42,12]',
'WARN: global_defs FOO redefined [test/compress/issue-208.js:44,10]', 'WARN: global_defs FOO redefined [test/compress/issue-208.js:44,10]',
'WARN: global_defs DEBUG redefined [test/compress/issue-208.js:45,8]', 'WARN: global_defs DEBUG redefined [test/compress/issue-208.js:45,8]',

View File

@@ -1,9 +0,0 @@
template_strings: {
input: {
foo(
`<span>${contents}</span>`,
`<a href="${url}">${text}</a>`
);
}
expect_exact: "foo(`<span>${contents}</span>`,`<a href=\"${url}\">${text}</a>`);"
}

View File

@@ -1,40 +0,0 @@
only_vars: {
options = { join_vars: true };
input: {
let netmaskBinary = '';
for (let i = 0; i < netmaskBits; ++i) {
netmaskBinary += '1';
}
}
expect: {
let netmaskBinary = '';
for (let i = 0; i < netmaskBits; ++i) netmaskBinary += '1';
}
}
issue_1079_with_vars: {
options = { join_vars: true };
input: {
var netmaskBinary = '';
for (var i = 0; i < netmaskBits; ++i) {
netmaskBinary += '1';
}
}
expect: {
for (var netmaskBinary = '', i = 0; i < netmaskBits; ++i) netmaskBinary += '1';
}
}
issue_1079_with_mixed: {
options = { join_vars: true };
input: {
var netmaskBinary = '';
for (let i = 0; i < netmaskBits; ++i) {
netmaskBinary += '1';
}
}
expect: {
var netmaskBinary = ''
for (let i = 0; i < netmaskBits; ++i) netmaskBinary += '1';
}
}

View File

@@ -146,50 +146,6 @@ parse_do_while_without_semicolon: {
} }
} }
keep_collapse_const_in_own_block_scope: {
options = {
join_vars: true,
loops: true
}
input: {
var i=2;
const c=5;
while(i--)
console.log(i);
console.log(c);
}
expect: {
var i=2;
const c=5;
for(;i--;)
console.log(i);
console.log(c);
}
expect_stdout: true
}
keep_collapse_const_in_own_block_scope_2: {
options = {
join_vars: true,
loops: true
}
input: {
const c=5;
var i=2; // Moves to loop, while it did not in previous test
while(i--)
console.log(i);
console.log(c);
}
expect: {
const c=5;
for(var i=2;i--;)
console.log(i);
console.log(c);
}
expect_stdout: true
}
evaluate: { evaluate: {
options = { options = {
loops: true, loops: true,

View File

@@ -82,19 +82,3 @@ new_with_unary_prefix: {
} }
expect_exact: 'var bar=(+new Date).toString(32);'; expect_exact: 'var bar=(+new Date).toString(32);';
} }
new_with_assignement_expression: {
options = {
evaluate: true
}
input: {
var a;
new x(a = 5 * 2, b = [1, 2, 3], c = {a: "a", b: "b", cd: "c" + "d"});
new y([a, b] = [3, 4]);
}
expect: {
var a;
new x(a = 10, b = [1, 2, 3], c = {a: "a", b: "b", cd: "cd"});
new y([a, b] = [3, 4]);
}
}

View File

@@ -0,0 +1,12 @@
eval_let: {
input: {
eval("let a;");
console.log();
}
expect: {
eval("let a;");
console.log();
}
expect_stdout: ""
node_version: ">=6"
}

View File

@@ -1,512 +0,0 @@
getter_setter: {
input: {
var get = "bar";
var a = {
get,
set: "foo",
get bar() {
return this.get;
},
get 5() {
return "five";
},
get 0xf55() {
return "f five five";
},
get "five"() {
return 5;
},
set one(value) {
this._one = value;
},
set 9(value) {
this._nine = value;
},
set 0b1010(value) {
this._ten = value;
},
set "eleven"(value) {
this._eleven = value;
}
};
var b = {
get() { return "gift"; },
set: function(code) { return "Storing code " + code; }
};
var c = {
["get"]: "foo",
["set"]: "bar"
};
var d = {
get: "foo",
set: "bar"
};
}
expect: {
var get = "bar";
var a = {
get,
set: "foo",
get bar() {
return this.get;
},
get 5() {
return "five";
},
get 0xf55() {
return "f five five";
},
get "five"() {
return 5;
},
set one(value) {
this._one = value;
},
set 9(value) {
this._nine = value;
},
set 0b1010(value) {
this._ten = value;
},
set "eleven"(value) {
this._eleven = value;
}
};
var b = {
get() { return "gift"; },
set: function(code) { return "Storing code " + code; }
};
var c = {
["get"]: "foo",
["set"]: "bar"
};
var d = {
get: "foo",
set: "bar"
};
}
}
getter_setter_mangler: {
mangle = {}
beautify = {
ecma: 6
}
input: {
function f(get,set) {
return {
get,
set,
get g(){},
set s(n){},
c,
a:1,
m(){}
};
}
}
expect_exact: "function f(n,t){return{get:n,set:t,get g(){},set s(n){},c,a:1,m(){}}}"
}
use_shorthand_opportunity: {
beautify = {
ecma: 6
}
input: {
var foo = 123;
var obj = {foo: foo};
}
expect_exact: "var foo=123;var obj={foo};"
}
computed_property_names: {
input: {
obj({ ["x" + "x"]: 6 });
}
expect_exact: 'obj({["x"+"x"]:6});'
}
computed_property_names_evaluated_1: {
options = {
evaluate: true
}
input: {
obj({
[1 + 1]: 2,
["x" + "x"]: 6
});
}
expect_exact: 'obj({[2]:2,["xx"]:6});'
}
computed_property_names_evaluated_2: {
options = {
evaluate: true
}
input: {
var foo = something();
var obj = {
[foo]() {
return "blah";
}
}
}
expect_exact: 'var foo=something();var obj={[foo](){return"blah"}};'
}
shorthand_properties: {
mangle = true;
input: {
(function() {
var prop = 1;
const value = {prop};
return value;
})();
}
expect: {
(function() {
var n = 1;
const r = {prop:n};
return r;
})();
}
}
concise_methods: {
beautify = {
ecma: 6
}
input: {
x = {
foo(a, b) {
return x;
}
}
y = {
foo([{a}]) {
return a;
},
bar(){}
}
}
expect_exact: "x={foo(a,b){return x}};y={foo([{a}]){return a},bar(){}};"
}
concise_methods_with_computed_property: {
options = {
evaluate: true
}
input: {
var foo = {
[Symbol.iterator]() {
return { /* stuff */ }
},
[1 + 2]() {
return 3;
},
["1" + "4"]() {
return 14;
}
}
}
expect: {
var foo = {
[Symbol.iterator]() {
return { /* stuff */ }
},
[3]() {
return 3;
},
["14"]() {
return 14;
}
}
}
}
concise_methods_with_computed_property2: {
options = {
evaluate: true
}
input: {
var foo = {
[[1]](){
return "success";
}
};
doSomething(foo[[1]]());
}
expect_exact: 'var foo={[[1]](){return"success"}};doSomething(foo[[1]]());'
}
concise_methods_with_various_property_names: {
input: {
var get = "bar";
var a = {
bar() {
return this.get;
},
5() {
return "five";
},
0xf55() {
return "f five five";
},
"five"() {
return 5;
},
0b1010(value) {
this._ten = value;
}
};
}
expect: {
var get = "bar";
var a = {
bar() {
return this.get;
},
5() {
return "five";
},
0xf55() {
return "f five five";
},
"five"() {
return 5;
},
0b1010(value) {
this._ten = value;
}
};
}
}
concise_methods_and_mangle_props: {
mangle_props = {
regex: /_/
};
input: {
function x() {
obj = {
_foo() { return 1; }
}
}
}
expect: {
function x() {
obj = {
a() { return 1; }
}
}
}
}
concise_generators: {
beautify = {
ecma: 6
}
input: {
x = {
*foo(a, b) {
return x;
}
}
y = {
*foo([{a}]) {
yield a;
},
bar(){}
}
}
expect_exact: "x={*foo(a,b){return x}};y={*foo([{a}]){yield a},bar(){}};"
}
concise_methods_and_keyword_names: {
input: {
x = {
catch() {},
throw() {}
}
}
expect: {
x={catch(){},throw(){}};
}
}
getter_setter_with_computed_value: {
input: {
class C {
get ['a']() {
return 'A';
}
set ['a'](value) {
do_something(a);
}
}
var x = {
get [a.b]() {
return 42;
}
};
class MyArray extends Array {
get [Symbol.species]() {
return Array;
}
}
}
expect_exact: 'class C{get["a"](){return"A"}set["a"](value){do_something(a)}}var x={get[a.b](){return 42}};class MyArray extends Array{get[Symbol.species](){return Array}}'
}
property_with_operator_value: {
input: {
var foo = {
"*": 1,
get "*"() {
return 2;
},
*"*"() {
return 3;
},
"%": 1,
get "%"() {
return 2;
},
*"%"() {
return 3;
}
}
class bar {
get "*"() {
return 1
}
*"*"() {
return 2;
}
get "%"() {
return 1
}
*"%"() {
return 2;
}
}
}
expect_exact: 'var foo={"*":1,get"*"(){return 2},*"*"(){return 3},"%":1,get"%"(){return 2},*"%"(){return 3}};class bar{get"*"(){return 1}*"*"(){return 2}get"%"(){return 1}*"%"(){return 2}}'
}
property_with_unprintable: {
input: {
var foo = {
"\x00\x01": "foo",
get "\x00\x01"() {
return "bar";
},
set "\x00\x01"(foo) {
save(foo);
},
*"\x00\x01"() {
return "foobar";
}
}
class bar {
get "\x00\x01"() {
return "bar"
}
set "\x00\x01"(foo) {
save(foo);
}
*"\x00\x01"() {
return "foobar";
}
}
}
expect_exact: 'var foo={"\\0\x01":"foo",get"\\0\x01"(){return"bar"},set"\\0\x01"(foo){save(foo)},*"\\0\x01"(){return"foobar"}};class bar{get"\\0\x01"(){return"bar"}set"\\0\x01"(foo){save(foo)}*"\\0\x01"(){return"foobar"}}'
}
property_with_unprintable_ascii_only: {
beautify = {
ascii_only: true,
}
input: {
var foo = {
"\x00\x01": "foo",
get "\x00\x01"() {
return "bar";
},
set "\x00\x01"(foo) {
save(foo);
},
*"\x00\x01"() {
return "foobar";
}
}
class bar {
get "\x00\x01"() {
return "bar"
}
set "\x00\x01"(foo) {
save(foo);
}
*"\x00\x01"() {
return "foobar";
}
}
}
expect_exact: 'var foo={"\\0\\x01":"foo",get"\\0\\x01"(){return"bar"},set"\\0\\x01"(foo){save(foo)},*"\\0\\x01"(){return"foobar"}};class bar{get"\\0\\x01"(){return"bar"}set"\\0\\x01"(foo){save(foo)}*"\\0\\x01"(){return"foobar"}}'
}
property_with_unprintable_ascii_only_static: {
beautify = {
ascii_only: true
}
input: {
class foo {
static get "\x02\x03"() {
return "bar";
}
static set "\x04\x05"(foo) {
save(foo);
}
}
}
expect_exact: 'class foo{static get"\\x02\\x03"(){return"bar"}static set"\\x04\\x05"(foo){save(foo)}}'
}
methods_and_getters_with_keep_quoted_props_enabled: {
beautify = {
quote_style: 3,
keep_quoted_props: true,
}
input: {
var obj = {
a() {},
"b"() {},
get c() { return "c"},
get "d"() { return "d"},
set e(a) { doSomething(a); },
set f(a) { doSomething(b); }
}
}
expect_exact: 'var obj={a(){},"b"(){},get c(){return"c"},get"d"(){return"d"},set e(a){doSomething(a)},set f(a){doSomething(b)}};'
}
allow_assignments_to_property_values: {
input: {
var foo = {123: foo = 123} = {foo: "456"};
}
expect: {
var foo = {123: foo = 123} = {foo: "456"};
}
}
variable_as_computed_property: {
input: {
function getLine(header) {
return {
[header]: {}
};
}
}
expect_exact: "function getLine(header){return{[header]:{}}}"
}

View File

@@ -1,180 +0,0 @@
arrow_functions: {
input: {
(a) => b; // 1 args
(a, b) => c; // n args
() => b; // 0 args
(a) => (b) => c; // func returns func returns func
(a) => ((b) => c); // So these parens are dropped
() => (b,c) => d; // func returns func returns func
a=>{return b;}
a => 'lel'; // Dropping the parens
}
expect_exact: "a=>b;(a,b)=>c;()=>b;a=>b=>c;a=>b=>c;()=>(b,c)=>d;a=>b;a=>\"lel\";"
}
arrow_return: {
input: {
() => {};
() => { return; };
a => { return 1; }
a => { return -b }
a => { return b; var b; }
(x, y) => { return x - y; }
}
expect_exact: "()=>{};()=>{};a=>1;a=>-b;a=>{return b;var b};(x,y)=>x-y;"
}
regression_arrow_functions_and_hoist: {
options = {
hoist_vars: true,
hoist_funs: true
}
input: {
(a) => b;
}
expect_exact: "a=>b;"
}
regression_assign_arrow_functions: {
input: {
oninstall = e => false;
oninstall = () => false;
}
expect: {
oninstall=e=>false;
oninstall=()=>false;
}
}
destructuring_arguments_1: {
input: {
(function ( a ) { });
(function ( [ a ] ) { });
(function ( [ a, b ] ) { });
(function ( [ [ a ] ] ) { });
(function ( [ [ a, b ] ] ) { });
(function ( [ a, [ b ] ] ) { });
(function ( [ [ b ], a ] ) { });
(function ( { a } ) { });
(function ( { a, b } ) { });
(function ( [ { a } ] ) { });
(function ( [ { a, b } ] ) { });
(function ( [ a, { b } ] ) { });
(function ( [ { b }, a ] ) { });
( [ a ] ) => { };
( [ a, b ] ) => { };
( { a } ) => { };
( { a, b, c, d, e } ) => { };
( [ a ] ) => b;
( [ a, b ] ) => c;
( { a } ) => b;
( { a, b } ) => c;
}
expect: {
(function(a){});
(function([a]){});
(function([a,b]){});
(function([[a]]){});
(function([[a,b]]){});
(function([a,[b]]){});
(function([[b],a]){});
(function({a}){});
(function({a,b}){});
(function([{a}]){});
(function([{a,b}]){});
(function([a,{b}]){});
(function([{b},a]){});
([a])=>{};
([a,b])=>{};
({a})=>{};
({a,b,c,d,e})=>{};
([a])=>b;
([a,b])=>c;
({a})=>b;
({a,b})=>c;
}
}
destructuring_arguments_2: {
input: {
(function([]) {});
(function({}) {});
(function([,,,,,]) {});
(function ([a, {b: c}]) {});
(function ([...args]) {});
(function ({x,}) {});
class a { *method({ [thrower()]: x } = {}) {}};
(function(a, b, c, d, [{e: [...f]}]){})(1, 2, 3, 4, [{e: [1, 2, 3]}]);
}
expect: {
(function([]) {});
(function({}) {});
(function([,,,,,]) {});
(function ([a, {b: c}]) {});
(function ([...args]) {});
(function ({x,}) {});
class a { *method({ [thrower()]: x } = {}) {}};
(function(a, b, c, d, [{e: [...f]}]){})(1, 2, 3, 4, [{e: [1, 2, 3]}]);
}
}
destructuring_arguments_3: {
beautify = {
ecma: 6
}
input: {
function fn3({x: {y: {z: {} = 42}}}) {}
const { cover = (function () {}), xCover = (0, function() {}) } = {};
let { cover = (function () {}), xCover = (0, function() {}) } = {};
var { cover = (function () {}), xCover = (0, function() {}) } = {};
}
expect_exact: "function fn3({x:{y:{z:{}=42}}}){}const{cover=function(){},xCover=(0,function(){})}={};let{cover=function(){},xCover=(0,function(){})}={};var{cover=function(){},xCover=(0,function(){})}={};"
}
default_arguments: {
beautify = {
ecma: 6
}
input: {
function x(a = 6) { }
function x(a = (6 + 5)) { }
function x({ foo } = {}, [ bar ] = [ 1 ]) { }
}
expect_exact: "function x(a=6){}function x(a=6+5){}function x({foo}={},[bar]=[1]){}"
}
default_values_in_destructurings: {
beautify = {
ecma: 6
}
input: {
function x({a=(4), b}) {}
function x([b, c=(12)]) {}
var { x = (6), y } = x;
var [ x, y = (6) ] = x;
}
expect_exact: "function x({a=4,b}){}function x([b,c=12]){}var{x=6,y}=x;var[x,y=6]=x;"
}
accept_duplicated_parameters_in_non_strict_without_spread_or_default_assignment: {
input: {
function a(b, b){}
function b({c: test, c: test}){}
}
expect: {
function a(b, b){}
function b({c: test, c: test}){}
}
}

View File

@@ -148,7 +148,6 @@ mangle_unquoted_properties: {
properties: false properties: false
} }
mangle_props = { mangle_props = {
builtins: true,
keep_quoted: true keep_quoted: true
} }
beautify = { beautify = {
@@ -239,7 +238,6 @@ mangle_debug_suffix_keep_quoted: {
properties: false properties: false
} }
mangle_props = { mangle_props = {
builtins: true,
keep_quoted: true, keep_quoted: true,
debug: "XYZ", debug: "XYZ",
reserved: [] reserved: []
@@ -557,3 +555,20 @@ native_prototype: {
"".indexOf.call(e, "bar"); "".indexOf.call(e, "bar");
} }
} }
issue_2040: {
input: {
var a = 1;
var b = {
get "a-b"() {
return a;
},
set "a-b"(c) {
a = c;
}
};
console.log(b["a-b"], b["a-b"] = 2, b["a-b"]);
}
expect_exact: 'var a=1;var b={get"a-b"(){return a},set"a-b"(c){a=c}};console.log(b["a-b"],b["a-b"]=2,b["a-b"]);'
expect_stdout: "1 2 2"
}

View File

@@ -119,3 +119,62 @@ chained: {
a.b.c; a.b.c;
} }
} }
impure_getter_1: {
options = {
pure_getters: "strict",
side_effects: true,
}
input: {
({
get a() {
console.log(1);
},
b: 1
}).a;
({
get a() {
console.log(1);
},
b: 1
}).b;
}
expect: {
({
get a() {
console.log(1);
},
b: 1
}).a;
({
get a() {
console.log(1);
},
b: 1
}).b;
}
expect_stdout: "1"
}
impure_getter_2: {
options = {
pure_getters: true,
side_effects: true,
}
input: {
// will produce incorrect output because getter is not pure
({
get a() {
console.log(1);
},
b: 1
}).a;
({
get a() {
console.log(1);
},
b: 1
}).b;
}
expect: {}
}

View File

@@ -41,20 +41,20 @@ reduce_vars: {
var A = 1; var A = 1;
(function() { (function() {
console.log(-3); console.log(-3);
console.log(-4); console.log(A - 5);
})(); })();
(function f1() { (function f1() {
var a = 2; var a = 2;
console.log(-3); console.log(a - 5);
eval("console.log(a);"); eval("console.log(a);");
})(); })();
(function f2(eval) { (function f2(eval) {
var a = 2; var a = 2;
console.log(-3); console.log(a - 5);
eval("console.log(a);"); eval("console.log(a);");
})(eval); })(eval);
"yes"; "yes";
console.log(2); console.log(A + 1);
} }
expect_stdout: true expect_stdout: true
} }
@@ -1749,7 +1749,10 @@ redefine_arguments_3: {
console.log(function() { console.log(function() {
var arguments; var arguments;
return typeof arguments; return typeof arguments;
}(), "number", "undefined"); }(), "number", function(x) {
var arguments = x;
return typeof arguments;
}());
} }
expect_stdout: "object number undefined" expect_stdout: "object number undefined"
} }
@@ -2187,10 +2190,11 @@ issue_1814_1: {
options = { options = {
evaluate: true, evaluate: true,
reduce_vars: true, reduce_vars: true,
toplevel: true,
unused: true, unused: true,
} }
input: { input: {
const a = 42; var a = 42;
!function() { !function() {
var b = a; var b = a;
!function(a) { !function(a) {
@@ -2199,7 +2203,6 @@ issue_1814_1: {
}(); }();
} }
expect: { expect: {
const a = 42;
!function() { !function() {
!function(a) { !function(a) {
console.log(a++, 42); console.log(a++, 42);
@@ -2213,10 +2216,11 @@ issue_1814_2: {
options = { options = {
evaluate: true, evaluate: true,
reduce_vars: true, reduce_vars: true,
toplevel: true,
unused: true, unused: true,
} }
input: { input: {
const a = "32"; var a = "32";
!function() { !function() {
var b = a + 1; var b = a + 1;
!function(a) { !function(a) {
@@ -2225,7 +2229,6 @@ issue_1814_2: {
}(); }();
} }
expect: { expect: {
const a = "32";
!function() { !function() {
!function(a) { !function(a) {
console.log(a++, "321"); console.log(a++, "321");
@@ -2461,3 +2464,76 @@ issue_1865: {
} }
expect_stdout: true expect_stdout: true
} }
issue_1922_1: {
options = {
evaluate: true,
reduce_vars: true,
unused: true,
}
input: {
console.log(function(a) {
arguments[0] = 2;
return a;
}(1));
}
expect: {
console.log(function(a) {
arguments[0] = 2;
return a;
}(1));
}
expect_stdout: "2"
}
issue_1922_2: {
options = {
evaluate: true,
reduce_vars: true,
unused: true,
}
input: {
console.log(function() {
var a;
eval("a = 1");
return a;
}(1));
}
expect: {
console.log(function() {
var a;
eval("a = 1");
return a;
}(1));
}
expect_stdout: "1"
}
accessor: {
options = {
evaluate: true,
reduce_vars: true,
toplevel: true,
}
input: {
var a = 1;
console.log({
get a() {
a = 2;
return a;
},
b: 1
}.b, a);
}
expect: {
var a = 1;
console.log({
get a() {
a = 2;
return a;
},
b: 1
}.b, a);
}
expect_stdout: "1 1"
}

View File

@@ -710,27 +710,3 @@ issue_27: {
})(jQuery); })(jQuery);
} }
} }
reassign_const: {
options = {
cascade: true,
sequences: true,
side_effects: true,
}
input: {
function f() {
const a = 1;
a++;
return a;
}
console.log(f());
}
expect: {
function f() {
const a = 1;
return a++, a;
}
console.log(f());
}
expect_stdout: true
}

View File

@@ -8,3 +8,12 @@ octal_escape_sequence: {
var border_check = "\x20\x30\x38\x30\x00\x30\xc0\x30"; var border_check = "\x20\x30\x38\x30\x00\x30\xc0\x30";
} }
} }
issue_1929: {
input: {
function f(s) {
return s.split(/[\\/]/);
}
}
expect_exact: "function f(s){return s.split(/[\\\\/]/)}"
}

View File

@@ -1,9 +0,0 @@
super_can_be_parsed: {
input: {
super(1,2);
super.meth();
}
expect_exact: "super(1,2);super.meth();"
}

View File

@@ -1,402 +0,0 @@
template_strings: {
beautify = {
quote_style: 3
}
input: {
``;
`xx\`x`;
`${ foo + 2 }`;
` foo ${ bar + `baz ${ qux }` }`;
}
expect_exact: "``;`xx\\`x`;`${foo+2}`;` foo ${bar+`baz ${qux}`}`;";
}
template_string_prefixes: {
beautify = {
quote_style: 3
}
input: {
String.raw`foo`;
foo `bar`;
}
expect_exact: "String.raw`foo`;foo`bar`;";
}
template_strings_ascii_only: {
beautify = {
ascii_only: true,
quote_style: 3
}
input: {
var foo = `foo
bar
ↂωↂ`;
var bar = `\``;
}
expect_exact: "var foo=`foo\\n bar\\n \\u2182\\u03c9\\u2182`;var bar=`\\``;"
}
template_strings_without_ascii_only: {
beautify = {
quote_style: 3
}
input: {
var foo = `foo
bar
ↂωↂ`
}
expect_exact: "var foo=`foo\\n bar\\n ↂωↂ`;"
}
template_string_with_constant_expression: {
options = {
evaluate: true
}
beautify = {
quote_style: 3
}
input: {
var foo = `${4 + 4} equals 4 + 4`;
}
expect: {
var foo = `8 equals 4 + 4`;
}
}
template_string_with_predefined_constants: {
options = {
evaluate: true
}
beautify = {
quote_style: 3
}
input: {
var foo = `This is ${undefined}`;
var bar = `This is ${NaN}`;
var baz = `This is ${null}`;
var foofoo = `This is ${Infinity}`;
var foobar = "This is ${1/0}";
var foobaz = 'This is ${1/0}';
var barfoo = "This is ${NaN}";
var bazfoo = "This is ${null}";
var bazbaz = `This is ${1/0}`;
var barbar = `This is ${0/0}`;
var barbar = "This is ${0/0}";
var barber = 'This is ${0/0}';
var a = `${4**11}`; // 8 in template vs 7 chars - 4194304
var b = `${4**12}`; // 8 in template vs 8 chars - 16777216
var c = `${4**14}`; // 8 in template vs 9 chars - 268435456
}
expect: {
var foo = `This is undefined`;
var bar = `This is NaN`;
var baz = `This is null`;
var foofoo = `This is ${1/0}`;
var foobar = "This is ${1/0}";
var foobaz = 'This is ${1/0}';
var barfoo = "This is ${NaN}";
var bazfoo = "This is ${null}";
var bazbaz = `This is ${1/0}`;
var barbar = `This is NaN`;
var barbar = "This is ${0/0}";
var barber = 'This is ${0/0}';
var a = `4194304`;
var b = `16777216`; // Potential for further concatentation
var c = `${4**14}`; // Not worth converting
}
}
template_string_evaluate_with_many_segments: {
options = {
evaluate: true
}
beautify = {
quote_style: 3
}
input: {
var foo = `Hello ${guest()}, welcome to ${location()}${"."}`;
var bar = `${1}${2}${3}${4}${5}${6}${7}${8}${9}${0}`;
var baz = `${foobar()}${foobar()}${foobar()}${foobar()}`;
var buzz = `${1}${foobar()}${2}${foobar()}${3}${foobar()}`;
}
expect: {
var foo = `Hello ${guest()}, welcome to ${location()}.`;
var bar = `1234567890`;
var baz = `${foobar()}${foobar()}${foobar()}${foobar()}`;
var buzz = `1${foobar()}2${foobar()}3${foobar()}`;
}
}
template_string_with_many_segments: {
beautify = {
quote_style: 3
}
input: {
var foo = `Hello ${guest()}, welcome to ${location()}${"."}`;
var bar = `${1}${2}${3}${4}${5}${6}${7}${8}${9}${0}`;
var baz = `${foobar()}${foobar()}${foobar()}${foobar()}`;
var buzz = `${1}${foobar()}${2}${foobar()}${3}${foobar()}`;
}
expect: {
var foo = `Hello ${guest()}, welcome to ${location()}${"."}`;
var bar = `${1}${2}${3}${4}${5}${6}${7}${8}${9}${0}`;
var baz = `${foobar()}${foobar()}${foobar()}${foobar()}`;
var buzz = `${1}${foobar()}${2}${foobar()}${3}${foobar()}`;
}
}
template_string_to_normal_string: {
options = {
evaluate: true
}
beautify = {
quote_style: 0
}
input: {
var foo = `This is ${undefined}`;
var bar = "Decimals " + `${1}${2}${3}${4}${5}${6}${7}${8}${9}${0}`;
}
expect: {
var foo = `This is undefined`;
var bar = "Decimals 1234567890";
}
}
template_concattenating_string: {
options = {
evaluate: true
}
beautify = {
quote_style: 3 // Yes, keep quotes
}
input: {
var foo = "Have a nice " + `day. ${`day. ` + `day.`}`;
var bar = "Have a nice " + `${day()}`;
}
expect: {
var foo = "Have a nice day. day. day.";
var bar = "Have a nice " + `${day()}`;
}
}
evaluate_nested_templates: {
options = {
evaluate: true
}
beautify = {
quote_style: 0
}
input: {
var baz = `${`${`${`foo`}`}`}`;
}
expect: {
var baz = `foo`;
}
}
enforce_double_quotes: {
beautify = {
quote_style: 1
}
input: {
var foo = `Hello world`;
var bar = `Hello ${'world'}`;
var baz = `Hello ${world()}`;
}
expect: {
var foo = `Hello world`;
var bar = `Hello ${"world"}`;
var baz = `Hello ${world()}`;
}
}
enforce_single_quotes: {
beautify = {
quote_style: 2
}
input: {
var foo = `Hello world`;
var bar = `Hello ${"world"}`;
var baz = `Hello ${world()}`;
}
expect: {
var foo = `Hello world`;
var bar = `Hello ${'world'}`;
var baz = `Hello ${world()}`;
}
}
enforce_double_quotes_and_evaluate: {
beautify = {
quote_style: 1
}
options = {
evaluate: true
}
input: {
var foo = `Hello world`;
var bar = `Hello ${'world'}`;
var baz = `Hello ${world()}`;
}
expect: {
var foo = `Hello world`;
var bar = `Hello world`;
var baz = `Hello ${world()}`;
}
}
enforce_single_quotes_and_evaluate: {
beautify = {
quote_style: 2
}
options = {
evaluate: true
}
input: {
var foo = `Hello world`;
var bar = `Hello ${"world"}`;
var baz = `Hello ${world()}`;
}
expect: {
var foo = `Hello world`;
var bar = `Hello world`;
var baz = `Hello ${world()}`;
}
}
respect_inline_script: {
beautify = {
inline_script: true,
quote_style: 3
}
input: {
var foo = `</script>${content}`;
var bar = `<!--`;
var baz = `-->`;
}
expect_exact: "var foo=`<\\/script>${content}`;var bar=`\\x3c!--`;var baz=`--\\x3e`;";
}
do_not_optimize_tagged_template_1: {
beautify = {
quote_style: 0
}
options = {
evaluate: true
}
input: {
var foo = tag`Shall not be optimized. ${"But " + "this " + "is " + "fine."}`;
var bar = tag`Don't even mind changing my quotes!`;
}
expect_exact:
'var foo=tag`Shall not be optimized. ${"But this is fine."}`;var bar=tag`Don\'t even mind changing my quotes!`;';
}
do_not_optimize_tagged_template_2: {
options = {
evaluate: true
}
input: {
var foo = tag`test` + " something out";
}
expect_exact: 'var foo=tag`test`+" something out";';
}
keep_raw_content_in_tagged_template: {
options = {
evaluate: true
}
input: {
var foo = tag`\u0020\u{20}\u{00020}\x20\40\040 `;
}
expect_exact: "var foo=tag`\\u0020\\u{20}\\u{00020}\\x20\\40\\040 `;";
}
allow_chained_templates: {
input: {
var foo = tag`a``b``c``d`;
}
expect: {
var foo = tag`a``b``c``d`;
}
}
check_escaped_chars: {
input: {
var foo = `\u0020\u{20}\u{00020}\x20\40\040 `;
}
expect_exact: "var foo=` `;";
}
escape_dollar_curly: {
options = {
evaluate: true
}
input: {
console.log(`\$\{ beep \}`)
console.log(`${1-0}\${2-0}$\{3-0}${4-0}`)
console.log(`$${""}{not an expression}`)
}
expect_exact: "console.log(`\\${ beep }`);console.log(`1\\${2-0}\\${3-0}4`);console.log(`\\${not an expression}`);"
}
template_starting_with_newline: {
options = {
dead_code: true
}
input: {
function foo(e) {
return `
this is a template string!`;
};
}
expect_exact: "function foo(e){return`\\nthis is a template string!`}"
}
template_with_newline: {
options = {
dead_code: true
}
input: {
function foo(e) {
return `yep,
this is a template string!`;
};
}
expect_exact: "function foo(e){return`yep,\\nthis is a template string!`}"
}
template_ending_with_newline: {
options = {
dead_code: true
}
input: {
function foo(e) {
return `this is a template string!
`;
};
}
expect_exact: "function foo(e){return`this is a template string!\\n`}"
}
issue_1856: {
beautify = {
ascii_only: false,
}
input: {
console.log(`\\n\\r\\u2028\\u2029\n\r\u2028\u2029`);
}
expect_exact: "console.log(`\\\\n\\\\r\\\\u2028\\\\u2029\\n\\r\\u2028\\u2029`);"
}
issue_1856_ascii_only: {
beautify = {
ascii_only: true,
}
input: {
console.log(`\\n\\r\\u2028\\u2029\n\r\u2028\u2029`);
}
expect_exact: "console.log(`\\\\n\\\\r\\\\u2028\\\\u2029\\n\\r\\u2028\\u2029`);"
}

View File

@@ -1,12 +0,0 @@
catch_destructuring_with_sequence: {
beautify = {
ecma: 6
}
input: {
try {
throw {};
} catch ({xCover = (0, function() {})} ) {
}
}
expect_exact: "try{throw{}}catch({xCover=(0,function(){})}){}"
}

View File

@@ -15,106 +15,3 @@ unicode_parse_variables: {
var l = 3; var l = 3;
} }
} }
unicode_escaped_identifier: {
beautify = {ecma: 6}
input: {
var \u{61} = "foo";
var \u{10000} = "bar";
}
expect_exact: 'var a="foo";var \u{10000}="bar";';
}
unicode_identifier_ascii_only: {
beautify = {ascii_only: true, ecma: 6}
input: {
var \u{0061} = "hi";
var bar = "h\u{0065}llo";
var \u{10000} = "testing \u{101111}";
}
expect_exact: 'var a="hi";var bar="hello";var \\u{10000}="testing \\u{101111}";'
}
unicode_string_literals: {
beautify = {ascii_only: true, ecma: 6}
input: {
var a = "6 length unicode character: \u{101111}";
}
expect_exact: 'var a="6 length unicode character: \\u{101111}";'
}
// Don't escape identifiers below es6 (or in this case double escaped in expect_exact)
unicode_output_es5_surrogates: {
beautify = {ascii_only: true, ecma: 5}
input: {
var \u{10000} = "6 length unicode character: \u{10FFFF}";
}
expect_exact: 'var \u{10000}="6 length unicode character: \\udbff\\udfff";'
}
check_escape_style: {
beautify = {ascii_only: true, ecma: 6}
input: {
var a = "\x01";
var \ua0081 = "\x10"; // \u0081 only in ID_Continue
var \u0100 = "\u0100";
var \u1000 = "\u1000";
var \u{10000} = "\u{10000}";
var \u{2f800} = "\u{100000}";
}
expect_exact: 'var a="\\x01";var \\ua0081="\\x10";var \\u0100="\\u0100";var \\u1000="\\u1000";var \\u{10000}="\\u{10000}";var \\u{2f800}="\\u{100000}";'
}
// Don't escape identifiers below es6, no escaped identifiers support and no \u{} syntax
check_escape_style_es5: {
beautify = {ascii_only: true, ecma: 5}
input: {
var a = "\x01";
var \ua0081 = "\x10"; // \u0081 only in ID_Continue
var \u0100 = "\u0100";
var \u1000 = "\u1000";
var \u{10000} = "\u{10000}"; // Identifier won't be escaped in es 5.1
var \u{2f800} = "\u{100000}"; // Same
}
expect_exact: 'var a="\\x01";var \\ua0081="\\x10";var \\u0100="\\u0100";var \\u1000="\\u1000";var \ud800\udc00="\\ud800\\udc00";var \ud87e\udc00="\\udbc0\\udc00";'
}
ID_continue_with_surrogate_pair: {
beautify = {ascii_only: true, ecma: 6}
input: {
var \u{2f800}\u{2f800}\u{2f800}\u{2f800} = "\u{100000}\u{100000}\u{100000}\u{100000}\u{100000}";
}
expect_exact: 'var \\u{2f800}\\u{2f800}\\u{2f800}\\u{2f800}="\\u{100000}\\u{100000}\\u{100000}\\u{100000}\\u{100000}";'
}
escape_non_escaped_identifier: {
beautify = {ascii_only: true, ecma: 6}
input: {
var µþ = "µþ";
}
expect_exact: 'var \\u00b5\\u00fe="\\xb5\\xfe";'
}
non_escape_2_non_escape: {
beautify = {ascii_only: false, ecma: 6}
input: {
var µþ = "µþ";
}
expect_exact: 'var µþ="µþ";'
}
non_escape_2_half_escape1: {
beautify = {ascii_only: false, ascii_identifiers: true, ecma: 6}
input: {
var µþ = "µþ";
}
expect_exact: 'var \\u00b5\\u00fe="µþ";'
}
non_escape_2_half_escape2: {
beautify = {ascii_only: true, ascii_identifiers: false, ecma: 6}
input: {
var µþ = "µþ";
}
expect_exact: 'var µþ="\\xb5\\xfe";'
}

View File

@@ -1,192 +0,0 @@
generators: {
input: {
function* fn() {};
}
expect_exact: "function*fn(){}"
}
generators_yield: {
input: {
function* fn() {
yield remote();
}
}
expect_exact: "function*fn(){yield remote()}"
}
generators_yield_assign: {
input: {
function* fn() {
var x = {};
x.prop = yield 5;
}
}
expect_exact: "function*fn(){var x={};x.prop=yield 5}"
}
generator_yield_undefined: {
input: {
function* fn() {
yield;
}
}
expect_exact: "function*fn(){yield}"
}
yield_optimize_expression: {
options = {
}
input: {
function* f1() { yield; }
function* f2() { yield undefined; }
function* f3() { yield null; }
function* f4() { yield* undefined; }
}
expect: {
function* f1() { yield }
function* f2() { yield; }
function* f3() { yield null; }
function* f4() { yield* void 0; }
}
}
yield_statements: {
input: {
function* fn() {
var a = (yield 1) + (yield 2);
var b = (yield 3) === (yield 4);
var c = (yield 5) << (yield 6);
var d = yield 7;
var e = (yield 8) ? yield 9 : yield 10;
var f = -(yield 11);
}
}
expect_exact: "function*fn(){var a=(yield 1)+(yield 2);var b=(yield 3)===(yield 4);var c=(yield 5)<<(yield 6);var d=yield 7;var e=(yield 8)?yield 9:yield 10;var f=-(yield 11)}"
}
yield_as_identifier_in_function_in_generator: {
input: {
var g = function*() {
function h() {
yield = 1;
}
};
}
expect: {
var g = function*() {
function h() {
yield = 1;
}
};
}
}
yield_before_punctuators: {
input: {
iter = (function*() {
assignmentResult = [ x = yield ] = value;
})();
function* g1() { (yield) }
function* g2() { [yield] }
function* g3() { return {yield} } // Added return to avoid {} drop
function* g4() { yield, yield; }
function* g5() { (yield) ? yield : yield; }
}
expect: {
iter = (function*() {
assignmentResult = [ x = yield ] = value;
})();
function* g1() { (yield) }
function* g2() { [yield] }
function* g3() { return {yield} }
function* g4() { yield, yield; }
function* g5() { (yield) ? yield : yield; }
}
}
yield_as_identifier_outside_strict_mode: {
input: {
import yield from "bar";
yield = 123;
while (true) {
yield:
for(;;) break yield;
foo();
}
while (true)
yield: for(;;) continue yield;
function yield(){}
function foo(...yield){}
try { new Error("") } catch (yield) {}
var yield = "foo";
}
expect: {
import yield from "bar";
yield = 123;
while (true) {
yield:
for(;;) break yield;
foo();
}
while (true)
yield: for(;;) continue yield;
function yield(){}
function foo(...yield){}
try { new Error("") } catch (yield) {}
var yield = "foo";
}
}
empty_generator_as_parameter_with_side_effects: {
options = {
side_effects: true
}
input: {
var GeneratorPrototype = Object.getPrototypeOf(
Object.getPrototypeOf(function*() {}())
);
evaluate(GeneratorPrototype);
}
expect_exact: "var GeneratorPrototype=Object.getPrototypeOf(Object.getPrototypeOf(function*(){}()));evaluate(GeneratorPrototype);"
}
empty_generator_as_parameter_without_side_effects: {
options = {
side_effects: false
}
input: {
var GeneratorPrototype = Object.getPrototypeOf(
Object.getPrototypeOf(function*() {}())
);
evaluate(GeneratorPrototype);
}
expect_exact: "var GeneratorPrototype=Object.getPrototypeOf(Object.getPrototypeOf(function*(){}()));evaluate(GeneratorPrototype);"
}
yield_dot: {
options = {
}
input: {
function* foo(){
yield x.foo;
(yield x).foo;
yield (yield obj.foo()).bar();
}
}
expect_exact: "function*foo(){yield x.foo;(yield x).foo;yield(yield obj.foo()).bar()}"
}
yield_sub: {
options = {
}
input: {
function* foo(){
yield x['foo'];
(yield x)['foo'];
yield (yield obj.foo())['bar']();
}
}
expect_exact: 'function*foo(){yield x["foo"];(yield x)["foo"];yield(yield obj.foo())["bar"]()}'
}

View File

@@ -1,8 +0,0 @@
function f() {
const a;
}
function g() {
"use strict";
const a;
}

View File

@@ -0,0 +1 @@
if (0) else 1;

View File

@@ -0,0 +1 @@
return 42;

View File

@@ -0,0 +1,5 @@
function test(callback) {
'aaaaaaaaaaaaaaaa';
callback(err, data);
callback(err, data);
}

View File

@@ -0,0 +1,5 @@
function test(a){
"aaaaaaaaaaaaaaaa"
;a(err,data),a(err,data)
}
//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIjAiXSwibmFtZXMiOlsidGVzdCIsImNhbGxiYWNrIiwiZXJyIiwiZGF0YSJdLCJtYXBwaW5ncyI6IkFBQUEsU0FBU0EsS0FBS0M7QUFDVjtDQUNBQSxFQUFTQyxJQUFLQyxNQUNkRixFQUFTQyxJQUFLQyJ9

View File

@@ -1,2 +1,2 @@
new function(){console.log(3)}; new function(){console.log(3)};
//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbInN0ZGluIl0sIm5hbWVzIjpbImNvbnNvbGUiLCJsb2ciXSwibWFwcGluZ3MiOiJBQUErQyxHQUFyQyxZQUFnQkEsUUFBUUMsSUFBSSIsInNvdXJjZXNDb250ZW50IjpbImNsYXNzIEZvbyB7IGNvbnN0cnVjdG9yKCl7Y29uc29sZS5sb2coMSsyKTt9IH0gbmV3IEZvbygpO1xuIl19 //# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbInN0ZGluIl0sIm5hbWVzIjpbImNvbnNvbGUiLCJsb2ciXSwibWFwcGluZ3MiOiJBQUErQyxJQUFyQyxXQUFnQkEsUUFBUUMsSUFBSSIsInNvdXJjZXNDb250ZW50IjpbImNsYXNzIEZvbyB7IGNvbnN0cnVjdG9yKCl7Y29uc29sZS5sb2coMSsyKTt9IH0gbmV3IEZvbygpO1xuIl19

View File

@@ -14,7 +14,7 @@ if (typeof phantom == "undefined") {
if (!args.length) { if (!args.length) {
args.push("-mc"); args.push("-mc");
} }
args.push("--stats"); args.push("--timings");
var child_process = require("child_process"); var child_process = require("child_process");
try { try {
require("phantomjs-prebuilt"); require("phantomjs-prebuilt");

View File

@@ -16,8 +16,8 @@ describe("Accessor tokens", function() {
assert.equal(node.start.pos, 12); assert.equal(node.start.pos, 12);
assert.equal(node.end.endpos, 46); assert.equal(node.end.endpos, 46);
assert(node.key instanceof UglifyJS.AST_SymbolMethod); assert(node.key instanceof UglifyJS.AST_SymbolAccessor);
assert.equal(node.key.start.pos, 12); assert.equal(node.key.start.pos, 16);
assert.equal(node.key.end.endpos, 22); assert.equal(node.key.end.endpos, 22);
assert(node.value instanceof UglifyJS.AST_Accessor); assert(node.value instanceof UglifyJS.AST_Accessor);

View File

@@ -27,253 +27,4 @@ describe("arguments", function() {
assert.strictEqual(ast.body[0].body[0].uses_arguments, true); assert.strictEqual(ast.body[0].body[0].uses_arguments, true);
assert.strictEqual(ast.body[0].body[0].body[0].uses_arguments, false); assert.strictEqual(ast.body[0].body[0].body[0].uses_arguments, false);
}); });
it("Should parse a function containing default assignment correctly", function() {
var ast = UglifyJS.parse("function foo(a = 123) {}");
assert(ast.body[0] instanceof UglifyJS.AST_Defun);
assert.strictEqual(ast.body[0].argnames.length, 1);
// First argument
assert(ast.body[0].argnames[0] instanceof UglifyJS.AST_DefaultAssign);
assert(ast.body[0].argnames[0].left instanceof UglifyJS.AST_SymbolFunarg);
assert.strictEqual(ast.body[0].argnames[0].operator, "=");
assert(ast.body[0].argnames[0].right instanceof UglifyJS.AST_Number);
ast = UglifyJS.parse("function foo(a = a) {}");
assert(ast.body[0] instanceof UglifyJS.AST_Defun);
assert.strictEqual(ast.body[0].argnames.length, 1);
// First argument
assert(ast.body[0].argnames[0] instanceof UglifyJS.AST_DefaultAssign);
assert(ast.body[0].argnames[0].left instanceof UglifyJS.AST_SymbolFunarg);
assert.strictEqual(ast.body[0].argnames[0].operator, "=");
assert(ast.body[0].argnames[0].right instanceof UglifyJS.AST_SymbolRef);
});
it("Should parse a function containing default assignments in destructuring correctly", function() {
var ast = UglifyJS.parse("function foo([a = 123]) {}");
assert(ast.body[0] instanceof UglifyJS.AST_Defun);
assert.strictEqual(ast.body[0].argnames.length, 1);
// First argument
assert(ast.body[0].argnames[0] instanceof UglifyJS.AST_Destructuring);
assert.strictEqual(ast.body[0].argnames[0].is_array, true);
assert.strictEqual(ast.body[0].argnames[0].names.length, 1);
assert(ast.body[0].argnames[0].names[0] instanceof UglifyJS.AST_DefaultAssign);
assert(ast.body[0].argnames[0].names[0].left instanceof UglifyJS.AST_SymbolFunarg);
assert.strictEqual(ast.body[0].argnames[0].names[0].operator, "=");
assert(ast.body[0].argnames[0].names[0].right instanceof UglifyJS.AST_Number);
ast = UglifyJS.parse("function foo({a = 123}) {}");
assert(ast.body[0] instanceof UglifyJS.AST_Defun);
assert.strictEqual(ast.body[0].argnames.length, 1);
// First argument
assert(ast.body[0].argnames[0] instanceof UglifyJS.AST_Destructuring);
assert.strictEqual(ast.body[0].argnames[0].is_array, false);
assert.strictEqual(ast.body[0].argnames[0].names.length, 1);
assert(ast.body[0].argnames[0].names[0] instanceof UglifyJS.AST_ObjectKeyVal);
assert.strictEqual(ast.body[0].argnames[0].names[0].key, "a");
// Property a of first argument
assert(ast.body[0].argnames[0].names[0].value instanceof UglifyJS.AST_DefaultAssign);
assert(ast.body[0].argnames[0].names[0].value.left instanceof UglifyJS.AST_SymbolFunarg);
assert.strictEqual(ast.body[0].argnames[0].names[0].value.operator, "=");
assert(ast.body[0].argnames[0].names[0].value.right instanceof UglifyJS.AST_Number);
ast = UglifyJS.parse("function foo({a: a = 123}) {}");
assert(ast.body[0] instanceof UglifyJS.AST_Defun);
assert.strictEqual(ast.body[0].argnames.length, 1);
// First argument
assert(ast.body[0].argnames[0] instanceof UglifyJS.AST_Destructuring);
assert.strictEqual(ast.body[0].argnames[0].is_array, false);
assert.strictEqual(ast.body[0].argnames[0].names.length, 1);
// Content destructuring of first argument
assert(ast.body[0].argnames[0].names[0] instanceof UglifyJS.AST_ObjectKeyVal);
assert.strictEqual(ast.body[0].argnames[0].names[0].key, "a");
assert(ast.body[0].argnames[0].names[0].value instanceof UglifyJS.AST_DefaultAssign);
// Property a of first argument
assert(ast.body[0].argnames[0].names[0].value instanceof UglifyJS.AST_DefaultAssign);
assert(ast.body[0].argnames[0].names[0].value.left instanceof UglifyJS.AST_SymbolFunarg);
assert.strictEqual(ast.body[0].argnames[0].names[0].value.operator, "=");
assert(ast.body[0].argnames[0].names[0].value.right instanceof UglifyJS.AST_Number);
});
it("Should parse a function containing default assignments in complex destructuring correctly", function() {
var ast = UglifyJS.parse("function foo([a, [b = 123]]){}");
assert(ast.body[0] instanceof UglifyJS.AST_Defun);
assert.strictEqual(ast.body[0].argnames.length, 1);
// Check first argument
assert(ast.body[0].argnames[0] instanceof UglifyJS.AST_Destructuring);
assert.strictEqual(ast.body[0].argnames[0].is_array, true);
assert.strictEqual(ast.body[0].argnames[0].names.length, 2);
// Check whole destructuring structure of first argument
assert(ast.body[0].argnames[0].names[0] instanceof UglifyJS.AST_SymbolFunarg);
assert(ast.body[0].argnames[0].names[1] instanceof UglifyJS.AST_Destructuring);
assert.strictEqual(ast.body[0].argnames[0].names[1].is_array, true);
// Check content of second destructuring element (which is the nested destructuring pattern)
assert(ast.body[0].argnames[0].names[1].names[0] instanceof UglifyJS.AST_DefaultAssign);
assert(ast.body[0].argnames[0].names[1].names[0].left instanceof UglifyJS.AST_SymbolFunarg);
assert.strictEqual(ast.body[0].argnames[0].names[1].names[0].operator, "=");
assert(ast.body[0].argnames[0].names[1].names[0].right instanceof UglifyJS.AST_Number);
ast = UglifyJS.parse("function foo([a, {b: c = 123}]){}");
assert(ast.body[0] instanceof UglifyJS.AST_Defun);
assert.strictEqual(ast.body[0].argnames.length, 1);
// Check first argument
assert(ast.body[0].argnames[0] instanceof UglifyJS.AST_Destructuring);
assert.strictEqual(ast.body[0].argnames[0].is_array, true);
assert.strictEqual(ast.body[0].argnames[0].names.length, 2);
// Check whole destructuring structure of first argument
assert(ast.body[0].argnames[0].names[0] instanceof UglifyJS.AST_SymbolFunarg);
assert(ast.body[0].argnames[0].names[1] instanceof UglifyJS.AST_Destructuring);
assert.strictEqual(ast.body[0].argnames[0].names[1].is_array, false);
// Check content of second destructuring element (which is the nested destructuring pattern)
assert(ast.body[0].argnames[0].names[1].names[0] instanceof UglifyJS.AST_ObjectKeyVal);
assert.strictEqual(ast.body[0].argnames[0].names[1].names[0].key, "b");
assert(ast.body[0].argnames[0].names[1].names[0].value instanceof UglifyJS.AST_DefaultAssign);
// Property b of second argument
assert(ast.body[0].argnames[0].names[1].names[0].value instanceof UglifyJS.AST_DefaultAssign);
assert(ast.body[0].argnames[0].names[1].names[0].value.left instanceof UglifyJS.AST_SymbolFunarg);
assert.strictEqual(ast.body[0].argnames[0].names[1].names[0].value.operator, "=");
assert(ast.body[0].argnames[0].names[1].names[0].value.right instanceof UglifyJS.AST_Number);
ast = UglifyJS.parse("function foo({a, b: {b = 123}}){}");
assert(ast.body[0] instanceof UglifyJS.AST_Defun);
assert.strictEqual(ast.body[0].argnames.length, 1);
// Check first argument
assert(ast.body[0].argnames[0] instanceof UglifyJS.AST_Destructuring);
assert.strictEqual(ast.body[0].argnames[0].is_array, false);
assert.strictEqual(ast.body[0].argnames[0].names.length, 2);
// Check whole destructuring structure of first argument
assert(ast.body[0].argnames[0].names[0] instanceof UglifyJS.AST_ObjectKeyVal);
assert.strictEqual(ast.body[0].argnames[0].names[0].key, "a");
assert(ast.body[0].argnames[0].names[0].value instanceof UglifyJS.AST_SymbolFunarg);
assert(ast.body[0].argnames[0].names[1] instanceof UglifyJS.AST_ObjectKeyVal);
assert.strictEqual(ast.body[0].argnames[0].names[1].key, "b");
assert(ast.body[0].argnames[0].names[1].value instanceof UglifyJS.AST_Destructuring);
// Check content of nested destructuring in first parameter
var content = ast.body[0].argnames[0].names[1].value
assert.strictEqual(content.is_array, false);
assert.strictEqual(content.names.length, 1);
assert(content.names[0] instanceof UglifyJS.AST_ObjectKeyVal);
assert.strictEqual(content.names[0].key, "b");
assert(content.names[0].value instanceof UglifyJS.AST_DefaultAssign);
assert(content.names[0].value.left instanceof UglifyJS.AST_SymbolFunarg);
assert.strictEqual(content.names[0].value.operator, "=");
assert(content.names[0].value.right instanceof UglifyJS.AST_Number);
ast = UglifyJS.parse("function foo({a: {b = 123}}){}");
assert(ast.body[0] instanceof UglifyJS.AST_Defun);
assert.strictEqual(ast.body[0].argnames.length, 1);
// Check first argument
assert(ast.body[0].argnames[0] instanceof UglifyJS.AST_Destructuring);
assert.strictEqual(ast.body[0].argnames[0].is_array, false);
assert.strictEqual(ast.body[0].argnames[0].names.length, 1);
// Check whole destructuring structure of first argument
assert(ast.body[0].argnames[0].names[0] instanceof UglifyJS.AST_ObjectKeyVal);
assert.strictEqual(ast.body[0].argnames[0].names[0].key, "a");
assert(ast.body[0].argnames[0].names[0].value instanceof UglifyJS.AST_Destructuring);
// Check content of nested destructuring
content = ast.body[0].argnames[0].names[0].value
assert.strictEqual(content.is_array, false);
assert.strictEqual(content.names.length, 1);
assert(content.names[0] instanceof UglifyJS.AST_ObjectKeyVal);
assert.strictEqual(content.names[0].key, "b");
assert(content.names[0].value instanceof UglifyJS.AST_DefaultAssign);
assert(content.names[0].value.left instanceof UglifyJS.AST_SymbolFunarg);
assert.strictEqual(content.names[0].value.operator, "=");
assert(content.names[0].value.right instanceof UglifyJS.AST_Number);
});
it("Should parse spread correctly", function() {
var ast = UglifyJS.parse("function foo(a, b, ...c){}");
assert(ast.body[0] instanceof UglifyJS.AST_Defun);
assert.strictEqual(ast.body[0].argnames.length, 3);
// Check parameters
assert(ast.body[0].argnames[0] instanceof UglifyJS.AST_SymbolFunarg);
assert(ast.body[0].argnames[1] instanceof UglifyJS.AST_SymbolFunarg);
assert(ast.body[0].argnames[2] instanceof UglifyJS.AST_Expansion);
assert(ast.body[0].argnames[2].expression instanceof UglifyJS.AST_SymbolFunarg);
ast = UglifyJS.parse("function foo([a, b, ...c]){}");
assert(ast.body[0] instanceof UglifyJS.AST_Defun);
assert.strictEqual(ast.body[0].argnames.length, 1);
// Check first parameter
assert(ast.body[0].argnames[0] instanceof UglifyJS.AST_Destructuring);
assert.strictEqual(ast.body[0].argnames[0].is_array, true);
// Check content first parameter
assert(ast.body[0].argnames[0].names[0] instanceof UglifyJS.AST_SymbolFunarg);
assert(ast.body[0].argnames[0].names[1] instanceof UglifyJS.AST_SymbolFunarg);
assert(ast.body[0].argnames[0].names[2] instanceof UglifyJS.AST_Expansion);
assert(ast.body[0].argnames[0].names[2].expression instanceof UglifyJS.AST_SymbolFunarg);
ast = UglifyJS.parse("function foo([a, b, [c, ...d]]){}");
assert(ast.body[0] instanceof UglifyJS.AST_Defun);
assert.strictEqual(ast.body[0].argnames.length, 1);
// Check first parameter
assert(ast.body[0].argnames[0] instanceof UglifyJS.AST_Destructuring);
assert.strictEqual(ast.body[0].argnames[0].is_array, true);
// Check content outer destructuring array
assert(ast.body[0].argnames[0].names[0] instanceof UglifyJS.AST_SymbolFunarg);
assert(ast.body[0].argnames[0].names[1] instanceof UglifyJS.AST_SymbolFunarg);
assert(ast.body[0].argnames[0].names[2] instanceof UglifyJS.AST_Destructuring);
assert.strictEqual(ast.body[0].argnames[0].names[2].is_array, true);
// Check content nested destructuring array
assert.strictEqual(ast.body[0].argnames[0].names[2].names.length, 2);
assert(ast.body[0].argnames[0].names[2].names[0] instanceof UglifyJS.AST_SymbolFunarg);
assert(ast.body[0].argnames[0].names[2].names[1] instanceof UglifyJS.AST_Expansion);
assert(ast.body[0].argnames[0].names[2].names[1].expression instanceof UglifyJS.AST_SymbolFunarg);
ast = UglifyJS.parse("function foo({a: [b, ...c]}){}");
assert(ast.body[0] instanceof UglifyJS.AST_Defun);
assert.strictEqual(ast.body[0].argnames.length, 1);
// Check first parameter
assert(ast.body[0].argnames[0] instanceof UglifyJS.AST_Destructuring);
assert.strictEqual(ast.body[0].argnames[0].is_array, false);
// Check outer destructuring object
assert.strictEqual(ast.body[0].argnames[0].names.length, 1);
assert(ast.body[0].argnames[0].names[0] instanceof UglifyJS.AST_ObjectKeyVal);
assert.strictEqual(ast.body[0].argnames[0].names[0].key, "a");
assert(ast.body[0].argnames[0].names[0].value instanceof UglifyJS.AST_Destructuring);
assert.strictEqual(ast.body[0].argnames[0].names[0].value.is_array, true);
// Check content nested destructuring array
assert.strictEqual(ast.body[0].argnames[0].names[0].value.names.length, 2);
assert(ast.body[0].argnames[0].names[0].value.names[0] instanceof UglifyJS.AST_SymbolFunarg);
assert(ast.body[0].argnames[0].names[0].value.names[1] instanceof UglifyJS.AST_Expansion);
assert(ast.body[0].argnames[0].names[0].value.names[1].expression instanceof UglifyJS.AST_SymbolFunarg);
});
}); });

View File

@@ -1,402 +0,0 @@
var assert = require("assert");
var uglify = require("../node");
describe("Arrow functions", function() {
it("Should not accept spread tokens on non-last parameters or without arguments parentheses", function() {
var tests = [
"var a = ...a => {return a.join()}",
"var b = (a, ...b, c) => { return a + b.join() + c}",
"var c = (...a, b) => a.join()"
];
var test = function(code) {
return function() {
uglify.parse(code);
}
}
var error = function(e) {
return e instanceof uglify.JS_Parse_Error &&
e.message === "Unexpected token: expand (...)";
}
for (var i = 0; i < tests.length; i++) {
assert.throws(test(tests[i]), error);
}
});
it("Should not accept holes in object binding patterns, while still allowing a trailing elision", function() {
var tests = [
"f = ({, , ...x} = [1, 2]) => {};"
];
var test = function(code) {
return function() {
uglify.parse(code);
}
}
var error = function(e) {
return e instanceof uglify.JS_Parse_Error &&
e.message === "Unexpected token: punc (,)";
}
for (var i = 0; i < tests.length; i++) {
assert.throws(test(tests[i]), error);
}
});
it("Should not accept newlines before arrow token", function() {
var tests = [
"f = foo\n=> 'foo';",
"f = (foo, bar)\n=> 'foo';",
"f = ()\n=> 'foo';",
"foo((bar)\n=>'baz';);"
];
var test = function(code) {
return function() {
uglify.parse(code);
}
}
var error = function(e) {
return e instanceof uglify.JS_Parse_Error &&
e.message === "Unexpected newline before arrow (=>)";
}
for (var i = 0; i < tests.length; i++) {
assert.throws(test(tests[i]), error);
}
});
it("Should not accept arrow functions in the middle or end of an expression", function() {
var tests = [
"typeof x => 0",
"0 + x => 0"
];
var test = function(code) {
return function() {
uglify.parse(code);
}
}
var error = function(e) {
return e instanceof uglify.JS_Parse_Error &&
e.message === "Unexpected token: arrow (=>)";
}
for (var i = 0; i < tests.length; i++) {
assert.throws(test(tests[i]), error);
}
});
it("Should parse a function containing default assignment correctly", function() {
var ast = uglify.parse("var a = (a = 123) => {}");
assert(ast.body[0] instanceof uglify.AST_Var);
assert.strictEqual(ast.body[0].definitions.length, 1);
assert(ast.body[0].definitions[0] instanceof uglify.AST_VarDef);
assert(ast.body[0].definitions[0].name instanceof uglify.AST_SymbolVar);
assert(ast.body[0].definitions[0].value instanceof uglify.AST_Arrow);
assert.strictEqual(ast.body[0].definitions[0].value.argnames.length, 1);
// First argument
assert(ast.body[0].definitions[0].value.argnames[0] instanceof uglify.AST_DefaultAssign);
assert(ast.body[0].definitions[0].value.argnames[0].left instanceof uglify.AST_SymbolFunarg);
assert.strictEqual(ast.body[0].definitions[0].value.argnames[0].operator, "=");
assert(ast.body[0].definitions[0].value.argnames[0].right instanceof uglify.AST_Number);
ast = uglify.parse("var a = (a = a) => {}");
assert(ast.body[0] instanceof uglify.AST_Var);
assert.strictEqual(ast.body[0].definitions.length, 1);
assert(ast.body[0].definitions[0] instanceof uglify.AST_VarDef);
assert(ast.body[0].definitions[0].name instanceof uglify.AST_SymbolVar);
assert(ast.body[0].definitions[0].value instanceof uglify.AST_Arrow);
assert.strictEqual(ast.body[0].definitions[0].value.argnames.length, 1);
// First argument
assert(ast.body[0].definitions[0].value.argnames[0] instanceof uglify.AST_DefaultAssign);
assert(ast.body[0].definitions[0].value.argnames[0].left instanceof uglify.AST_SymbolFunarg);
assert.strictEqual(ast.body[0].definitions[0].value.argnames[0].operator, "=");
assert(ast.body[0].definitions[0].value.argnames[0].right instanceof uglify.AST_SymbolRef);
});
it("Should parse a function containing default assignments in destructuring correctly", function() {
var ast = uglify.parse("var a = ([a = 123]) => {}");
assert(ast.body[0] instanceof uglify.AST_Var);
assert.strictEqual(ast.body[0].definitions.length, 1);
assert(ast.body[0].definitions[0] instanceof uglify.AST_VarDef);
assert(ast.body[0].definitions[0].name instanceof uglify.AST_SymbolVar);
assert(ast.body[0].definitions[0].value instanceof uglify.AST_Arrow);
assert.strictEqual(ast.body[0].definitions[0].value.argnames.length, 1);
// First argument
assert(ast.body[0].definitions[0].value.argnames[0] instanceof uglify.AST_Destructuring);
assert.strictEqual(ast.body[0].definitions[0].value.argnames[0].is_array, true);
assert.strictEqual(ast.body[0].definitions[0].value.argnames[0].names.length, 1);
assert(ast.body[0].definitions[0].value.argnames[0].names[0] instanceof uglify.AST_DefaultAssign);
assert(ast.body[0].definitions[0].value.argnames[0].names[0].left instanceof uglify.AST_SymbolFunarg);
assert.strictEqual(ast.body[0].definitions[0].value.argnames[0].names[0].operator, "=");
assert(ast.body[0].definitions[0].value.argnames[0].names[0].right instanceof uglify.AST_Number);
ast = uglify.parse("var a = ({a = 123}) => {}");
assert(ast.body[0] instanceof uglify.AST_Var);
assert.strictEqual(ast.body[0].definitions.length, 1);
assert(ast.body[0].definitions[0] instanceof uglify.AST_VarDef);
assert(ast.body[0].definitions[0].name instanceof uglify.AST_SymbolVar);
assert(ast.body[0].definitions[0].value instanceof uglify.AST_Arrow);
assert.strictEqual(ast.body[0].definitions[0].value.argnames.length, 1);
// First argument
assert(ast.body[0].definitions[0].value.argnames[0] instanceof uglify.AST_Destructuring);
assert.strictEqual(ast.body[0].definitions[0].value.argnames[0].is_array, false);
assert.strictEqual(ast.body[0].definitions[0].value.argnames[0].names.length, 1);
assert(ast.body[0].definitions[0].value.argnames[0].names[0] instanceof uglify.AST_ObjectKeyVal);
// First object element in first argument
assert.strictEqual(ast.body[0].definitions[0].value.argnames[0].names[0].key, "a");
assert(ast.body[0].definitions[0].value.argnames[0].names[0].value instanceof uglify.AST_DefaultAssign);
assert(ast.body[0].definitions[0].value.argnames[0].names[0].value.left instanceof uglify.AST_SymbolFunarg);
assert.strictEqual(ast.body[0].definitions[0].value.argnames[0].names[0].value.operator, "=");
assert(ast.body[0].definitions[0].value.argnames[0].names[0].value.right instanceof uglify.AST_Number);
ast = uglify.parse("var a = ({a: a = 123}) => {}");
assert(ast.body[0] instanceof uglify.AST_Var);
assert.strictEqual(ast.body[0].definitions.length, 1);
assert(ast.body[0].definitions[0] instanceof uglify.AST_VarDef);
assert(ast.body[0].definitions[0].name instanceof uglify.AST_SymbolVar);
assert(ast.body[0].definitions[0].value instanceof uglify.AST_Arrow);
assert.strictEqual(ast.body[0].definitions[0].value.argnames.length, 1);
// First argument
assert(ast.body[0].definitions[0].value.argnames[0] instanceof uglify.AST_Destructuring);
assert.strictEqual(ast.body[0].definitions[0].value.argnames[0].is_array, false);
assert.strictEqual(ast.body[0].definitions[0].value.argnames[0].names.length, 1);
// Content destructuring of first argument
assert(ast.body[0].definitions[0].value.argnames[0].names[0] instanceof uglify.AST_ObjectProperty);
assert.strictEqual(ast.body[0].definitions[0].value.argnames[0].names[0].key, "a");
assert(ast.body[0].definitions[0].value.argnames[0].names[0].value instanceof uglify.AST_DefaultAssign);
assert(ast.body[0].definitions[0].value.argnames[0].names[0].value.left instanceof uglify.AST_SymbolFunarg);
assert.strictEqual(ast.body[0].definitions[0].value.argnames[0].names[0].value.operator, "=");
assert(ast.body[0].definitions[0].value.argnames[0].names[0].value.right instanceof uglify.AST_Number);
});
it("Should parse a function containing default assignments in complex destructuring correctly", function() {
var ast = uglify.parse("var a = ([a, [b = 123]]) => {}");
assert(ast.body[0] instanceof uglify.AST_Var);
assert.strictEqual(ast.body[0].definitions.length, 1);
assert(ast.body[0].definitions[0] instanceof uglify.AST_VarDef);
assert(ast.body[0].definitions[0].name instanceof uglify.AST_SymbolVar);
assert(ast.body[0].definitions[0].value instanceof uglify.AST_Arrow);
assert.strictEqual(ast.body[0].definitions[0].value.argnames.length, 1);
// Check first argument
assert(ast.body[0].definitions[0].value.argnames[0] instanceof uglify.AST_Destructuring);
assert.strictEqual(ast.body[0].definitions[0].value.argnames[0].is_array, true);
assert.strictEqual(ast.body[0].definitions[0].value.argnames[0].names.length, 2);
// Check whole destructuring structure of first argument
assert(ast.body[0].definitions[0].value.argnames[0].names[0] instanceof uglify.AST_SymbolFunarg);
assert(ast.body[0].definitions[0].value.argnames[0].names[1] instanceof uglify.AST_Destructuring);
assert.strictEqual(ast.body[0].definitions[0].value.argnames[0].names[1].is_array, true);
// Check content of second destructuring element (which is the nested destructuring pattern)
assert(ast.body[0].definitions[0].value.argnames[0].names[1].names[0] instanceof uglify.AST_DefaultAssign);
assert(ast.body[0].definitions[0].value.argnames[0].names[1].names[0].left instanceof uglify.AST_SymbolFunarg);
assert.strictEqual(ast.body[0].definitions[0].value.argnames[0].names[1].names[0].operator, "=");
assert(ast.body[0].definitions[0].value.argnames[0].names[1].names[0].right instanceof uglify.AST_Number);
ast = uglify.parse("var a = ([a, {b: c = 123}]) => {}");
assert(ast.body[0] instanceof uglify.AST_Var);
assert.strictEqual(ast.body[0].definitions.length, 1);
assert(ast.body[0].definitions[0] instanceof uglify.AST_VarDef);
assert(ast.body[0].definitions[0].name instanceof uglify.AST_SymbolVar);
assert(ast.body[0].definitions[0].value instanceof uglify.AST_Arrow);
assert.strictEqual(ast.body[0].definitions[0].value.argnames.length, 1);
// Check first argument
assert(ast.body[0].definitions[0].value.argnames[0] instanceof uglify.AST_Destructuring);
assert.strictEqual(ast.body[0].definitions[0].value.argnames[0].is_array, true);
assert.strictEqual(ast.body[0].definitions[0].value.argnames[0].names.length, 2);
// Check whole destructuring structure of first argument
assert(ast.body[0].definitions[0].value.argnames[0].names[0] instanceof uglify.AST_SymbolFunarg);
assert(ast.body[0].definitions[0].value.argnames[0].names[1] instanceof uglify.AST_Destructuring);
assert.strictEqual(ast.body[0].definitions[0].value.argnames[0].names[1].is_array, false);
// Check content of second destructuring element (which is the nested destructuring pattern)
assert(ast.body[0].definitions[0].value.argnames[0].names[1].names[0] instanceof uglify.AST_ObjectKeyVal);
assert.strictEqual(ast.body[0].definitions[0].value.argnames[0].names[1].names[0].key, "b");
assert(ast.body[0].definitions[0].value.argnames[0].names[1].names[0].value instanceof uglify.AST_DefaultAssign);
assert(ast.body[0].definitions[0].value.argnames[0].names[1].names[0].value.left instanceof uglify.AST_SymbolFunarg);
assert.strictEqual(ast.body[0].definitions[0].value.argnames[0].names[1].names[0].value.operator, "=");
assert(ast.body[0].definitions[0].value.argnames[0].names[1].names[0].value.right instanceof uglify.AST_Number);
ast = uglify.parse("var a = ({a, b: {b = 123}}) => {}");
assert(ast.body[0] instanceof uglify.AST_Var);
assert.strictEqual(ast.body[0].definitions.length, 1);
assert(ast.body[0].definitions[0] instanceof uglify.AST_VarDef);
assert(ast.body[0].definitions[0].name instanceof uglify.AST_SymbolVar);
assert(ast.body[0].definitions[0].value instanceof uglify.AST_Arrow);
assert.strictEqual(ast.body[0].definitions[0].value.argnames.length, 1);
// Check first argument
assert(ast.body[0].definitions[0].value.argnames[0] instanceof uglify.AST_Destructuring);
assert.strictEqual(ast.body[0].definitions[0].value.argnames[0].is_array, false);
assert.strictEqual(ast.body[0].definitions[0].value.argnames[0].names.length, 2);
// First argument, property 1
assert(ast.body[0].definitions[0].value.argnames[0].names[0] instanceof uglify.AST_ObjectKeyVal);
assert.strictEqual(ast.body[0].definitions[0].value.argnames[0].names[0].key, "a");
assert(ast.body[0].definitions[0].value.argnames[0].names[0].value instanceof uglify.AST_SymbolFunarg);
// First argument, property 2
assert(ast.body[0].definitions[0].value.argnames[0].names[1] instanceof uglify.AST_ObjectKeyVal);
assert.strictEqual(ast.body[0].definitions[0].value.argnames[0].names[1].key, "b");
assert(ast.body[0].definitions[0].value.argnames[0].names[1].value instanceof uglify.AST_Destructuring);
// Check content of nested destructuring
var content = ast.body[0].definitions[0].value.argnames[0].names[1].value
assert.strictEqual(content.is_array, false);
assert.strictEqual(content.names.length, 1);
assert(content.names[0] instanceof uglify.AST_ObjectKeyVal);
// Content of first property in nested destructuring
assert.strictEqual(content.names[0].key, "b");
assert(content.names[0].value instanceof uglify.AST_DefaultAssign);
assert(content.names[0].value.left instanceof uglify.AST_SymbolFunarg);
assert.strictEqual(content.names[0].value.operator, "=");
assert(content.names[0].value.right instanceof uglify.AST_Number);
ast = uglify.parse("var a = ({a: {b = 123}}) => {}");
assert(ast.body[0] instanceof uglify.AST_Var);
assert.strictEqual(ast.body[0].definitions.length, 1);
assert(ast.body[0].definitions[0] instanceof uglify.AST_VarDef);
assert(ast.body[0].definitions[0].name instanceof uglify.AST_SymbolVar);
assert(ast.body[0].definitions[0].value instanceof uglify.AST_Arrow);
assert.strictEqual(ast.body[0].definitions[0].value.argnames.length, 1);
// Check first argument
assert(ast.body[0].definitions[0].value.argnames[0] instanceof uglify.AST_Destructuring);
assert.strictEqual(ast.body[0].definitions[0].value.argnames[0].is_array, false);
assert.strictEqual(ast.body[0].definitions[0].value.argnames[0].names.length, 1);
// Check whole destructuring structure of first argument
assert(ast.body[0].definitions[0].value.argnames[0].names[0] instanceof uglify.AST_ObjectKeyVal);
assert.strictEqual(ast.body[0].definitions[0].value.argnames[0].names[0].key, "a");
assert(ast.body[0].definitions[0].value.argnames[0].names[0].value instanceof uglify.AST_Destructuring);
// Check content of nested destructuring
content = ast.body[0].definitions[0].value.argnames[0].names[0].value
assert.strictEqual(content.is_array, false);
assert.strictEqual(content.names.length, 1);
assert(content.names[0] instanceof uglify.AST_ObjectKeyVal);
// Check first property of nested destructuring
assert.strictEqual(content.names[0].key, "b");
assert(content.names[0].value instanceof uglify.AST_DefaultAssign);
assert(content.names[0].value.left instanceof uglify.AST_SymbolFunarg);
assert.strictEqual(content.names[0].value.operator, "=");
assert(content.names[0].value.right instanceof uglify.AST_Number);
});
it("Should parse spread correctly", function() {
var ast = uglify.parse("var a = (a, b, ...c) => {}");
assert(ast.body[0] instanceof uglify.AST_Var);
assert.strictEqual(ast.body[0].definitions.length, 1);
assert(ast.body[0].definitions[0] instanceof uglify.AST_VarDef);
assert(ast.body[0].definitions[0].name instanceof uglify.AST_SymbolVar);
assert(ast.body[0].definitions[0].value instanceof uglify.AST_Arrow);
assert.strictEqual(ast.body[0].definitions[0].value.argnames.length, 3);
// Check parameters
assert(ast.body[0].definitions[0].value.argnames[0] instanceof uglify.AST_SymbolFunarg);
assert(ast.body[0].definitions[0].value.argnames[1] instanceof uglify.AST_SymbolFunarg);
assert(ast.body[0].definitions[0].value.argnames[2] instanceof uglify.AST_Expansion);
assert(ast.body[0].definitions[0].value.argnames[2].expression instanceof uglify.AST_SymbolFunarg);
ast = uglify.parse("var a = ([a, b, ...c]) => {}");
assert(ast.body[0] instanceof uglify.AST_Var);
assert.strictEqual(ast.body[0].definitions.length, 1);
assert(ast.body[0].definitions[0] instanceof uglify.AST_VarDef);
assert(ast.body[0].definitions[0].name instanceof uglify.AST_SymbolVar);
assert(ast.body[0].definitions[0].value instanceof uglify.AST_Arrow);
assert.strictEqual(ast.body[0].definitions[0].value.argnames.length, 1);
// Check first parameter
assert(ast.body[0].definitions[0].value.argnames[0] instanceof uglify.AST_Destructuring);
assert.strictEqual(ast.body[0].definitions[0].value.argnames[0].is_array, true);
// Check content first parameter
assert(ast.body[0].definitions[0].value.argnames[0].names[0] instanceof uglify.AST_SymbolFunarg);
assert(ast.body[0].definitions[0].value.argnames[0].names[1] instanceof uglify.AST_SymbolFunarg);
assert(ast.body[0].definitions[0].value.argnames[0].names[2] instanceof uglify.AST_Expansion);
assert(ast.body[0].definitions[0].value.argnames[0].names[2].expression instanceof uglify.AST_SymbolFunarg);
ast = uglify.parse("var a = ([a, b, [c, ...d]]) => {}");
assert(ast.body[0] instanceof uglify.AST_Var);
assert.strictEqual(ast.body[0].definitions.length, 1);
assert(ast.body[0].definitions[0] instanceof uglify.AST_VarDef);
assert(ast.body[0].definitions[0].name instanceof uglify.AST_SymbolVar);
assert(ast.body[0].definitions[0].value instanceof uglify.AST_Arrow);
assert.strictEqual(ast.body[0].definitions[0].value.argnames.length, 1);
// Check first parameter
assert(ast.body[0].definitions[0].value.argnames[0] instanceof uglify.AST_Destructuring);
assert.strictEqual(ast.body[0].definitions[0].value.argnames[0].is_array, true);
// Check content outer destructuring array
assert(ast.body[0].definitions[0].value.argnames[0].names[0] instanceof uglify.AST_SymbolFunarg);
assert(ast.body[0].definitions[0].value.argnames[0].names[1] instanceof uglify.AST_SymbolFunarg);
assert(ast.body[0].definitions[0].value.argnames[0].names[2] instanceof uglify.AST_Destructuring);
assert.strictEqual(ast.body[0].definitions[0].value.argnames[0].names[2].is_array, true);
// Check content nested destructuring array
assert.strictEqual(ast.body[0].definitions[0].value.argnames[0].names[2].names.length, 2);
assert(ast.body[0].definitions[0].value.argnames[0].names[2].names[0] instanceof uglify.AST_SymbolFunarg);
assert(ast.body[0].definitions[0].value.argnames[0].names[2].names[1] instanceof uglify.AST_Expansion);
assert(ast.body[0].definitions[0].value.argnames[0].names[2].names[1].expression instanceof uglify.AST_SymbolFunarg);
ast = uglify.parse("var a = ({a: [b, ...c]}) => {}");
assert(ast.body[0] instanceof uglify.AST_Var);
assert.strictEqual(ast.body[0].definitions.length, 1);
assert(ast.body[0].definitions[0] instanceof uglify.AST_VarDef);
assert(ast.body[0].definitions[0].name instanceof uglify.AST_SymbolVar);
assert(ast.body[0].definitions[0].value instanceof uglify.AST_Arrow);
assert.strictEqual(ast.body[0].definitions[0].value.argnames.length, 1);
// Check first parameter
assert(ast.body[0].definitions[0].value.argnames[0] instanceof uglify.AST_Destructuring);
assert.strictEqual(ast.body[0].definitions[0].value.argnames[0].is_array, false);
// Check outer destructuring object
assert.strictEqual(ast.body[0].definitions[0].value.argnames[0].names.length, 1);
assert(ast.body[0].definitions[0].value.argnames[0].names[0] instanceof uglify.AST_ObjectKeyVal);
assert.strictEqual(ast.body[0].definitions[0].value.argnames[0].names[0].key, "a");
assert(ast.body[0].definitions[0].value.argnames[0].names[0].value instanceof uglify.AST_Destructuring);
assert.strictEqual(ast.body[0].definitions[0].value.argnames[0].names[0].value.is_array, true);
// Check content nested destructuring array
assert.strictEqual(ast.body[0].definitions[0].value.argnames[0].names[0].value.names.length, 2);
assert(ast.body[0].definitions[0].value.argnames[0].names[0].value.names[0] instanceof uglify.AST_SymbolFunarg);
assert(ast.body[0].definitions[0].value.argnames[0].names[0].value.names[1] instanceof uglify.AST_Expansion);
assert(ast.body[0].definitions[0].value.argnames[0].names[0].value.names[1].expression instanceof uglify.AST_SymbolFunarg);
});
it("Should handle arrow function with bind", function() {
function minify(code) {
return uglify.minify(code, {
mangle: false
}).code;
}
assert.strictEqual(
minify(minify("test(((index) => { console.log(this, index); }).bind(this, 1));")),
"test((index=>{console.log(this,index)}).bind(this,1));"
);
assert.strictEqual(
minify(minify('test(((index) => { console.log(this, index); })["bind"](this, 1));')),
"test((index=>{console.log(this,index)}).bind(this,1));"
);
});
});

View File

@@ -1,34 +0,0 @@
var UglifyJS = require("../../");
var assert = require("assert");
describe("builtins", function() {
it ("Should not mangle builtins", function() {
var test = "function foo(something){\n" +
" return [Object,Array,Function,Number,String,Boolean,Error,Math,Date,RegExp,Symbol,Map,Promise,Proxy,Reflect,Set,WeakMap,WeakSet,Float32Array,something];\n" +
"};";
var result = UglifyJS.minify(test, {parse: {bare_returns: true}}).code;
assert.strictEqual(result.indexOf("something"), -1);
assert.notEqual(result.indexOf("Object"), -1);
assert.notEqual(result.indexOf("Array"), -1);
assert.notEqual(result.indexOf("Function"), -1);
assert.notEqual(result.indexOf("Number"), -1);
assert.notEqual(result.indexOf("String"), -1);
assert.notEqual(result.indexOf("Boolean"), -1);
assert.notEqual(result.indexOf("Error"), -1);
assert.notEqual(result.indexOf("Math"), -1);
assert.notEqual(result.indexOf("Date"), -1);
assert.notEqual(result.indexOf("RegExp"), -1);
assert.notEqual(result.indexOf("Symbol"), -1);
assert.notEqual(result.indexOf("Promise"), -1);
assert.notEqual(result.indexOf("Proxy"), -1);
assert.notEqual(result.indexOf("Reflect"), -1);
assert.notEqual(result.indexOf("Set"), -1);
assert.notEqual(result.indexOf("WeakMap"), -1);
assert.notEqual(result.indexOf("WeakSet"), -1);
assert.notEqual(result.indexOf("Map"), -1);
assert.notEqual(result.indexOf("Float32Array"), -1);
});
});

View File

@@ -1,57 +0,0 @@
var assert = require("assert");
var uglify = require("../node");
describe("Class", function() {
it("Should not accept spread on non-last parameters in methods", function() {
var tests = [
"class foo { bar(...a, b) { return a.join(b) } }",
"class foo { bar(a, b, ...c, d) { return c.join(a + b) + d } }",
"class foo { *bar(...a, b) { return a.join(b) } }",
"class foo { *bar(a, b, ...c, d) { return c.join(a + b) + d } }"
];
var test = function(code) {
return function() {
uglify.parse(code);
}
}
var error = function(e) {
return e instanceof uglify.JS_Parse_Error &&
/^Unexpected token: /.test(e.message);
}
for (var i = 0; i < tests.length; i++) {
assert.throws(test(tests[i]), error);
}
});
it("Should return the correct token for class methods", function() {
var tests = [
{
code: "class foo{static test(){}}",
token_value_start: "static",
token_value_end: "}"
},
{
code: "class bar{*procedural(){}}",
token_value_start: "*",
token_value_end: "}"
},
{
code: "class foobar{aMethod(){}}",
token_value_start: "aMethod",
token_value_end: "}"
},
{
code: "class foobaz{get something(){}}",
token_value_start: "get",
token_value_end: "}"
}
];
for (var i = 0; i < tests.length; i++) {
var ast = uglify.parse(tests[i].code);
assert.strictEqual(ast.body[0].properties[0].start.value, tests[i].token_value_start);
assert.strictEqual(ast.body[0].properties[0].end.value, tests[i].token_value_end);
}
});
});

View File

@@ -19,7 +19,9 @@ describe("bin/uglifyjs", function () {
eval(stdout); eval(stdout);
assert.strictEqual(typeof WrappedUglifyJS, 'object'); assert.strictEqual(typeof WrappedUglifyJS, 'object');
assert.strictEqual(WrappedUglifyJS.minify("foo([true,,2+3]);").code, "foo([!0,,5]);"); var result = WrappedUglifyJS.minify("foo([true,,2+3]);");
assert.strictEqual(result.error, undefined);
assert.strictEqual(result.code, "foo([!0,,5]);");
done(); done();
}); });
@@ -61,7 +63,7 @@ describe("bin/uglifyjs", function () {
if (err) throw err; if (err) throw err;
assert.strictEqual(stdout, "var bar=function(){function foo(bar){return bar}return foo}();\n" + assert.strictEqual(stdout, "var bar=function(){function foo(bar){return bar}return foo}();\n" +
"//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbInRlc3QvaW5wdXQvaXNzdWUtMTMyMy9zYW1wbGUuanMiXSwibmFtZXMiOlsiYmFyIiwiZm9vIl0sIm1hcHBpbmdzIjoiQUFBQSxHQUFJQSxLQUFNLFdBQ04sUUFBU0MsS0FBS0QsS0FDVixNQUFPQSxLQUdYLE1BQU9DIn0=\n"); "//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbInRlc3QvaW5wdXQvaXNzdWUtMTMyMy9zYW1wbGUuanMiXSwibmFtZXMiOlsiYmFyIiwiZm9vIl0sIm1hcHBpbmdzIjoiQUFBQSxJQUFJQSxJQUFNLFdBQ04sU0FBU0MsSUFBS0QsS0FDVixPQUFPQSxJQUdYLE9BQU9DIn0=\n");
done(); done();
}); });
}); });
@@ -173,7 +175,7 @@ describe("bin/uglifyjs", function () {
assert.strictEqual(stdout, [ assert.strictEqual(stdout, [
"var bar=function(){function foo(bar){return bar}return foo}();", "var bar=function(){function foo(bar){return bar}return foo}();",
"//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbInRlc3QvaW5wdXQvaXNzdWUtMTMyMy9zYW1wbGUuanMiXSwibmFtZXMiOlsiYmFyIiwiZm9vIl0sIm1hcHBpbmdzIjoiQUFBQSxHQUFJQSxLQUFNLFdBQ04sUUFBU0MsS0FBS0QsS0FDVixNQUFPQSxLQUdYLE1BQU9DIn0=", "//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbInRlc3QvaW5wdXQvaXNzdWUtMTMyMy9zYW1wbGUuanMiXSwibmFtZXMiOlsiYmFyIiwiZm9vIl0sIm1hcHBpbmdzIjoiQUFBQSxJQUFJQSxJQUFNLFdBQ04sU0FBU0MsSUFBS0QsS0FDVixPQUFPQSxJQUdYLE9BQU9DIn0=",
"", "",
].join("\n")); ].join("\n"));
assert.strictEqual(stderr, "WARN: inline source map not found\n"); assert.strictEqual(stderr, "WARN: inline source map not found\n");
@@ -379,21 +381,6 @@ describe("bin/uglifyjs", function () {
done(); done();
}); });
}); });
it("Should throw syntax error (const a)", function(done) {
var command = uglifyjscmd + ' test/input/invalid/const.js';
exec(command, function (err, stdout, stderr) {
assert.ok(err);
assert.strictEqual(stdout, "");
assert.strictEqual(stderr.split(/\n/).slice(0, 4).join("\n"), [
"Parse error at test/input/invalid/const.js:7,11",
" const a;",
" ^",
"ERROR: Missing initializer in const declaration"
].join("\n"));
done();
});
});
it("Should throw syntax error (delete x)", function(done) { it("Should throw syntax error (delete x)", function(done) {
var command = uglifyjscmd + ' test/input/invalid/delete.js'; var command = uglifyjscmd + ' test/input/invalid/delete.js';
@@ -464,7 +451,7 @@ describe("bin/uglifyjs", function () {
"Parse error at test/input/invalid/try.js:7,18", "Parse error at test/input/invalid/try.js:7,18",
" try {} catch (eval) {}", " try {} catch (eval) {}",
" ^", " ^",
"ERROR: Unexpected eval identifier as parameter inside strict mode" "ERROR: Unexpected eval in strict mode"
].join("\n")); ].join("\n"));
done(); done();
}); });
@@ -484,6 +471,36 @@ describe("bin/uglifyjs", function () {
done(); done();
}); });
}); });
it("Should throw syntax error (else)", function(done) {
var command = uglifyjscmd + ' test/input/invalid/else.js';
exec(command, function (err, stdout, stderr) {
assert.ok(err);
assert.strictEqual(stdout, "");
assert.strictEqual(stderr.split(/\n/).slice(0, 4).join("\n"), [
"Parse error at test/input/invalid/else.js:1,7",
"if (0) else 1;",
" ^",
"ERROR: Unexpected token: keyword (else)"
].join("\n"));
done();
});
});
it("Should throw syntax error (return)", function(done) {
var command = uglifyjscmd + ' test/input/invalid/return.js';
exec(command, function (err, stdout, stderr) {
assert.ok(err);
assert.strictEqual(stdout, "");
assert.strictEqual(stderr.split(/\n/).slice(0, 4).join("\n"), [
"Parse error at test/input/invalid/return.js:1,0",
"return 42;",
"^",
"ERROR: 'return' outside of function"
].join("\n"));
done();
});
});
it("Should handle literal string as source map input", function(done) { it("Should handle literal string as source map input", function(done) {
var command = [ var command = [
uglifyjscmd, uglifyjscmd,
@@ -497,7 +514,7 @@ describe("bin/uglifyjs", function () {
assert.strictEqual(stdout, [ assert.strictEqual(stdout, [
'"use strict";var foo=function foo(x){return"foo "+x};console.log(foo("bar"));', '"use strict";var foo=function foo(x){return"foo "+x};console.log(foo("bar"));',
"//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbImluZGV4LmpzIl0sIm5hbWVzIjpbImZvbyIsIngiLCJjb25zb2xlIiwibG9nIl0sIm1hcHBpbmdzIjoiWUFBQSxJQUFJQSxLQUFNLFFBQU5BLEtBQU1DLEdBQUEsTUFBSyxPQUFTQSxFQUN4QkMsU0FBUUMsSUFBSUgsSUFBSSJ9", "//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbImluZGV4LmpzIl0sIm5hbWVzIjpbImZvbyIsIngiLCJjb25zb2xlIiwibG9nIl0sIm1hcHBpbmdzIjoiYUFBQSxJQUFJQSxJQUFNLFNBQU5BLElBQU1DLEdBQUEsTUFBSyxPQUFTQSxHQUN4QkMsUUFBUUMsSUFBSUgsSUFBSSJ9",
"" ""
].join("\n")); ].join("\n"));
done(); done();
@@ -520,4 +537,13 @@ describe("bin/uglifyjs", function () {
done(); done();
}); });
}); });
it("Should print supported options on invalid option syntax", function(done) {
var command = uglifyjscmd + " test/input/comments/filter.js -b ascii-only";
exec(command, function (err, stdout, stderr) {
assert.ok(err);
assert.strictEqual(stdout, "");
assert.ok(/^Supported options:\n\{[^}]+}\nERROR: `ascii-only` is not a supported option/.test(stderr), stderr);
done();
});
});
}); });

View File

@@ -31,8 +31,7 @@ describe("Comment", function() {
"/*Some comment 2\r\n\r\n\r\n*/\r\n>\n\n\n\n\n\n", "/*Some comment 2\r\n\r\n\r\n*/\r\n>\n\n\n\n\n\n",
"/*Some comment 3\r\r\r*/\r>\n\n\n\n\n\n", "/*Some comment 3\r\r\r*/\r>\n\n\n\n\n\n",
"/*Some comment 4\u2028\u2028\u2028*/\u2028>\n\n\n\n\n\n", "/*Some comment 4\u2028\u2028\u2028*/\u2028>\n\n\n\n\n\n",
"/*Some comment 5\u2029\u2029\u2029*/\u2029>\n\n\n\n\n\n", "/*Some comment 5\u2029\u2029\u2029*/\u2029>\n\n\n\n\n\n"
"/*Some comment 6\udbff\udfff\udbff\udfff\n\n\n*/\n>\n\n\n\n\n"
]; ];
var fail = function(e) { var fail = function(e) {

View File

@@ -1,138 +0,0 @@
var assert = require("assert");
var uglify = require("../node");
describe("Destructuring", function() {
it("Should generate similar trees for destructuring in left hand side expressions, definitions, functions and arrow functions", function() {
var patterns = [
"[]",
"{}",
"[a, b, c]",
"{a: b, c: d}",
"{a}",
"{a, b}",
"{a: {}}",
"{a: []}",
"[{}]",
"[[]]",
"{a: {b}}",
// Can't do `a = 123` with lhs expression, so only test in destructuring
"[foo = bar]",
"{a = 123}",
"[{foo: abc = 123}]",
"{foo: [abc = 123]}",
"[...foo]",
"[...{}]",
"[...[]]"
// Can't do `...` because that is an invalid lhs expression, spread in array destructuring should be fine though
];
var types = [
{
name: "lhs",
symbolType: uglify.AST_SymbolRef,
tree: function (ast) {
return ast.body[0].body.left;
},
generate: function (code) {
return "(" + code + " = a)";
}
},
{
name: "var",
symbolType: uglify.AST_SymbolVar,
tree: function (ast) {
return ast.body[0].definitions[0].name;
},
generate: function (code) {
return "var " + code + " = a";
}
},
{
name: "function",
symbolType: uglify.AST_SymbolFunarg,
tree: function (ast) {
return ast.body[0].argnames[0];
},
generate: function (code) {
return "function a(" + code + ") {}";
}
},
{
name: "arrow",
symbolType: uglify.AST_SymbolFunarg,
tree: function (ast) {
return ast.body[0].definitions[0].value.argnames[0];
},
generate: function (code) {
return "var a = (" + code + ") => {}";
}
}
];
var walker = function(type, ref, code, result) {
var w = new uglify.TreeWalker(function(node) {
if (w.parent() instanceof uglify.AST_DefaultAssign &&
w.parent().right === node
) {
return true; // Don't check the content of the default assignment
} else if (node instanceof uglify.AST_Symbol) {
assert(node instanceof type.symbolType,
node.TYPE + " while " + type.symbolType.TYPE + " expected at pos " +
node.start.pos + " in `" + code + "` (" + ref + ")"
);
result.push([
new uglify.AST_Symbol({
start: node.start,
name: node.name,
end: node.end
}),
w.parent()
]);
return;
}
result.push([node, w.parent()]);
});
return w;
};
var getNodeType = function(node) {
return node[0].TYPE + (node[1] ? " " + node[1].TYPE : "");
}
for (var i = 0; i < patterns.length; i++) {
var results = [];
for (var j = 0; j < types.length; j++) {
var code = types[j].generate(patterns[i])
var ast = types[j].tree(
uglify.parse(code)
);
results.push([]);
ast.walk(walker(
types[j],
"`" + patterns[i] + "` on " + types[j].name,
code,
results[j]
));
if (j > 0) {
assert.deepEqual(
results[0].map(getNodeType),
results[j].map(getNodeType),
"AST disagree on " + patterns[i] + " with " + types[j].name
);
}
}
}
});
});

View File

@@ -62,10 +62,10 @@ describe("Directives", function() {
it("Should know which strings are directive and which ones are not", function() { it("Should know which strings are directive and which ones are not", function() {
var test_directive = function(tokenizer, test) { var test_directive = function(tokenizer, test) {
test.directives.map(function(directive) { test.directives.map(function(directive) {
assert.strictEqual(tokenizer.has_directive(directive), true, "Didn't found directive `" + directive + "` at the end of `" + test.input + '`'); assert.strictEqual(tokenizer.has_directive(directive), true, directive + " in " + test.input);
}); });
test.non_directives.map(function(fake_directive) { test.non_directives.map(function(fake_directive) {
assert.strictEqual(tokenizer.has_directive(fake_directive), false, "Unexpectedly found directive `" + fake_directive + "` at the end of `" + test.input + '`'); assert.strictEqual(tokenizer.has_directive(fake_directive), false, fake_directive + " in " + test.input);
}); });
} }
@@ -156,16 +156,6 @@ describe("Directives", function() {
input: '"use strict";try{"use asm";', input: '"use strict";try{"use asm";',
directives: ["use strict"], directives: ["use strict"],
non_directives: ["use\nstrict", "use \nstrict", "use asm"] non_directives: ["use\nstrict", "use \nstrict", "use asm"]
},
{
input: 'class foo {',
directives: ["use strict"],
non_directives: ["use\nstrict", "use asm"]
},
{
input: 'class foo {}',
directives: [],
non_directives: ["use strict", "use asm", "use\nstrict"]
} }
]; ];
@@ -361,61 +351,31 @@ describe("Directives", function() {
var tests = [ var tests = [
[ [
'"use strict";"use strict";"use strict";"use foo";"use strict";;"use sloppy";doSomething("foo");', '"use strict";"use strict";"use strict";"use foo";"use strict";;"use sloppy";doSomething("foo");',
'"use strict";"use foo";doSomething("foo");' '"use strict";"use foo";doSomething("foo");',
'function f(){ "use strict" }',
'function f(){ "use asm" }',
'function f(){ "use nondirective" }',
'function f(){ ;"use strict" }',
'function f(){ "use \n"; }',
], ],
[ [
// Nothing gets optimised in the compressor because "use asm" is the first statement // Nothing gets optimised in the compressor because "use asm" is the first statement
'"use asm";"use\\x20strict";1+1;', '"use asm";"use\\x20strict";1+1;',
'"use asm";;"use strict";1+1;' // Yet, the parser noticed that "use strict" wasn't a directive '"use asm";;"use strict";1+1;', // Yet, the parser noticed that "use strict" wasn't a directive
'function f(){"use strict"}',
'function f(){"use asm"}',
'function f(){"use nondirective"}',
'function f(){}',
'function f(){}',
] ]
]; ];
for (var i = 0; i < tests.length; i++) { for (var i = 0; i < tests.length; i++) {
assert.strictEqual( assert.strictEqual(
uglify.minify(tests[i][0], {compress: {collapse_vars: true, side_effects: true}}).code, uglify.minify(tests[i][0]).code,
tests[i][1], tests[i][1],
tests[i][0] tests[i][0]
); );
} }
}); });
it("Should be detect implicit usages of strict mode from tree walker", function() {
var tests = [
{
input: 'class foo {bar(){_check_}}',
directives: ["use strict"],
non_directives: ["use bar"]
},
{
input: 'class foo {bar(){}}_check_',
directives: [],
non_directives: ["use strict", "use bar"]
}
];
var i = 0;
var checked;
var checkWalker = new uglify.TreeWalker(function(node, descend) {
if (node instanceof uglify.AST_Symbol && node.name === "_check_") {
checked = true;
for (var j = 0; j < tests[i].directives.length; j++) {
assert.ok(checkWalker.has_directive(tests[i].directives[j]),
"Did not found directive '" + tests[i].directives[j] + "' in test " + tests[i].input)
}
for (var k = 0; k < tests[i].non_directives.length; k++) {
assert.equal(checkWalker.has_directive(tests[i].non_directives[k]), undefined,
"Found directive '" + tests[i].non_directives[k] + "' in test " + tests[i].input)
}
}
});
for (; i < tests.length; i++) {
// Do tests - iterate the ast in each test - check only when _check_ occurs - fail when no _check_ has been found
checked = false;
var ast = uglify.parse(tests[i].input);
ast.walk(checkWalker);
if (!checked) {
throw "No _check_ symbol found in " + tests[i].input;
}
}
});
}); });

View File

@@ -1,35 +0,0 @@
var assert = require("assert");
var uglify = require("../node");
describe("EOF", function() {
it("Should test code for at least throwing syntax error when incomplete", function() {
var error = function(e) {
return e instanceof uglify.JS_Parse_Error;
}
var parse = function(test) {
return function() {
uglify.parse(test);
}
}
// Chops off 1 char at a time until limit or start of string is reached
// The passed code must still be valid when unchopped
var test_eol = function(input, chopLimit) {
if (chopLimit === undefined) {
chopLimit = input.length - 1;
}
assert.doesNotThrow(parse(input), "Expected valid code for \n" + input);
for (var i = input.length - 1; chopLimit > 0; chopLimit--, i--) {
var code = input.substr(0, i);
assert.throws(parse(code), error, code);
}
}
test_eol("var \\u1234", 7); // Incomplete identifier
test_eol("'Incomplete string'");
test_eol("/Unterminated regex/");
test_eol("` Unterminated template string`");
test_eol("/* Unfinishing multiline comment */");
});
});

View File

@@ -1,40 +0,0 @@
var assert = require("assert");
var uglify = require("../node");
describe("Export", function() {
it ("Should parse export directives", function() {
var inputs = [
['export * from "a.js"', ['*'], "a.js"],
['export {A} from "a.js"', ['A'], "a.js"],
['export {A as X} from "a.js"', ['X'], "a.js"],
['export {A as Foo, B} from "a.js"', ['Foo', 'B'], "a.js"],
['export {A, B} from "a.js"', ['A', 'B'], "a.js"],
];
var test = function(code) {
return uglify.parse(code);
};
var extractNames = function(symbols) {
var ret = [];
for (var i = 0; i < symbols.length; i++) {
ret.push(symbols[i].name.name)
}
return ret;
};
for (var i = 0; i < inputs.length; i++) {
var ast = test(inputs[i][0]);
var names = inputs[i][1];
var filename = inputs[i][2];
assert(ast instanceof uglify.AST_Toplevel);
assert.equal(ast.body.length, 1);
var st = ast.body[0];
assert(st instanceof uglify.AST_Export);
var actualNames = extractNames(st.exported_names);
assert.deepEqual(actualNames, names);
assert.equal(st.module_name.value, filename)
}
})
});

View File

@@ -1,32 +0,0 @@
var assert = require("assert");
var uglify = require("../node");
describe("Expression", function() {
it("Should not allow the first exponentiation operator to be prefixed with an unary operator", function() {
var tests = [
"+5 ** 3",
"-5 ** 3",
"~5 ** 3",
"!5 ** 3",
"void 5 ** 3",
"typeof 5 ** 3",
"delete 5 ** 3",
"var a = -(5) ** 3;"
];
var fail = function(e) {
return e instanceof uglify.JS_Parse_Error &&
/^Unexpected token: operator \((?:[!+~-]|void|typeof|delete)\)/.test(e.message);
}
var exec = function(test) {
return function() {
uglify.parse(test);
}
}
for (var i = 0; i < tests.length; i++) {
assert.throws(exec(tests[i]), fail, tests[i]);
}
});
});

View File

@@ -1,252 +0,0 @@
var assert = require("assert");
var uglify = require("../node");
describe("Function", function() {
it ("Should parse binding patterns correctly", function() {
// Function argument nodes are correct
function get_args(args) {
return args.map(function (arg) {
return [arg.TYPE, arg.name];
});
}
// Destructurings as arguments
var destr_fun1 = uglify.parse('(function ({a, b}) {})').body[0].body;
var destr_fun2 = uglify.parse('(function ([a, [b]]) {})').body[0].body;
var destr_fun3 = uglify.parse('({a, b}) => null').body[0].body;
var destr_fun4 = uglify.parse('([a, [b]]) => null').body[0].body;
assert.equal(destr_fun1.argnames.length, 1);
assert.equal(destr_fun2.argnames.length, 1);
assert.equal(destr_fun3.argnames.length, 1);
assert.equal(destr_fun4.argnames.length, 1);
var destruct1 = destr_fun1.argnames[0];
var destruct2 = destr_fun2.argnames[0];
var destruct3 = destr_fun3.argnames[0];
var destruct4 = destr_fun4.argnames[0];
assert(destruct1 instanceof uglify.AST_Destructuring);
assert(destruct2 instanceof uglify.AST_Destructuring);
assert(destruct3 instanceof uglify.AST_Destructuring);
assert(destruct4 instanceof uglify.AST_Destructuring);
assert(destruct2.names[1] instanceof uglify.AST_Destructuring);
assert(destruct4.names[1] instanceof uglify.AST_Destructuring);
assert.equal(destruct1.start.value, '{');
assert.equal(destruct1.end.value, '}');
assert.equal(destruct2.start.value, '[');
assert.equal(destruct2.end.value, ']');
assert.equal(destruct3.start.value, '{');
assert.equal(destruct3.end.value, '}');
assert.equal(destruct4.start.value, '[');
assert.equal(destruct4.end.value, ']');
assert.equal(destruct1.is_array, false);
assert.equal(destruct2.is_array, true);
assert.equal(destruct3.is_array, false);
assert.equal(destruct4.is_array, true);
// destruct 1
assert.deepEqual(
[
destruct1.names[0].TYPE,
destruct1.names[0].key,
destruct1.names[0].value.name
],
['ObjectKeyVal', 'a', 'a']
);
assert.deepEqual(
[
destruct1.names[1].TYPE,
destruct1.names[1].key,
destruct1.names[1].value.name
],
['ObjectKeyVal', 'b', 'b']
);
// destruct 2
assert.deepEqual(
[
destruct2.names[0].TYPE,
destruct2.names[0].name
],
['SymbolFunarg', 'a']
);
assert.deepEqual(
[
destruct2.names[1].names[0].TYPE,
destruct2.names[1].names[0].name
],
['SymbolFunarg', 'b']
);
// destruct 3
assert.strictEqual(typeof destruct3.names[0].key, "string");
assert.strictEqual(destruct3.names[0].key, "a");
assert.strictEqual(destruct3.names[0].value.TYPE, "SymbolFunarg");
assert.strictEqual(destruct3.names[0].value.name, "a");
assert.strictEqual(typeof destruct3.names[1].key, "string");
assert.strictEqual(destruct3.names[1].key, "b");
assert.strictEqual(destruct3.names[1].value.TYPE, "SymbolFunarg");
assert.strictEqual(destruct3.names[1].value.name, "b");
// destruct 4
assert.deepEqual(
[
destruct4.names[0].TYPE,
destruct4.names[0].name
],
['SymbolFunarg', 'a']
);
assert.strictEqual(destruct4.names[1].TYPE, "Destructuring");
assert.deepEqual(
[
destruct4.names[1].names[0].TYPE,
destruct4.names[1].names[0].name
],
['SymbolFunarg', 'b']
);
assert.deepEqual(
get_args(destr_fun1.args_as_names()),
[['SymbolFunarg', 'a'], ['SymbolFunarg', 'b']]
);
assert.deepEqual(
get_args(destr_fun2.args_as_names()),
[['SymbolFunarg', 'a'], ['SymbolFunarg', 'b']]
);
assert.deepEqual(
get_args(destr_fun3.args_as_names()),
[['SymbolFunarg', 'a'], ['SymbolFunarg', 'b']]
);
assert.deepEqual(
get_args(destr_fun4.args_as_names()),
[['SymbolFunarg', 'a'], ['SymbolFunarg', 'b']]
);
// Making sure we don't accidentally accept things which
// Aren't argument destructurings
assert.throws(function () {
uglify.parse('(function ( { a, [ b ] } ) { })')
});
assert.throws(function () {
uglify.parse('(function (1) { })');
}, /Invalid function parameter/);
assert.throws(function () {
uglify.parse('(function (this) { })');
});
assert.throws(function () {
uglify.parse('(function ([1]) { })');
}, /Invalid function parameter/);
assert.throws(function () {
uglify.parse('(function [a] { })');
});
// generators
var generators_def = uglify.parse('function* fn() {}').body[0];
assert.equal(generators_def.is_generator, true);
assert.throws(function () {
uglify.parse('function* (){ }');
});
var generators_yield_def = uglify.parse('function* fn() {\nyield remote();\}').body[0].body[0];
assert.strictEqual(generators_yield_def.body.is_star, false);
});
it("Should not accept spread on non-last parameters", function() {
var tests = [
"var a = function(...a, b) { return a.join(b) }",
"var b = function(a, b, ...c, d) { return c.join(a + b) + d }",
"function foo(...a, b) { return a.join(b) }",
"function bar(a, b, ...c, d) { return c.join(a + b) + d }",
"var a = function*(...a, b) { return a.join(b) }",
"var b = function*(a, b, ...c, d) { return c.join(a + b) + d }",
"function* foo(...a, b) { return a.join(b) }",
"function* bar(a, b, ...c, d) { return c.join(a + b) + d }"
];
var test = function(code) {
return function() {
uglify.parse(code);
}
}
var error = function(e) {
return e instanceof uglify.JS_Parse_Error &&
/^Unexpected token: /.test(e.message);
}
for (var i = 0; i < tests.length; i++) {
assert.throws(test(tests[i]), error);
}
});
it("Should not accept empty parameters after elision", function() {
var tests = [
"(function(,){})()",
"(function(a,){})()",
];
var test = function(code) {
return function() {
uglify.parse(code);
}
}
var error = function(e) {
return e instanceof uglify.JS_Parse_Error &&
e.message === "Invalid function parameter";
}
for (var i = 0; i < tests.length; i++) {
assert.throws(test(tests[i]), error);
}
});
it("Should not accept an initializer when parameter is a rest parameter", function() {
var tests = [
"(function(...a = b){})()",
"(function(a, ...b = [c, d]))"
];
var test = function(code) {
return function () {
uglify.parse(code);
}
}
var error = function (e) {
return e instanceof uglify.JS_Parse_Error;
}
for (var i = 0; i < tests.length; i++) {
assert.throws(test(tests[i]), error, tests[i]);
}
});
it("Shoult not accept duplicated identifiers inside parameters in strict mode or when using default assigment or spread", function() {
// From: ES2016 9.2.12 FunctionDeclarationInstantiation (func, argumentsList)
// NOTE Early errors ensure that duplicate parameter names can only occur
// in non-strict functions that do not have parameter default values or
// rest parameters.
var tests = [
"(function(a = 1, a){})()",
"(function(a, [a = 3]){})()",
"(function(a, b, c, d, [{e: [...a]}]){})()",
"'use strict'; (function(a, a){})",
"(function({a, a = b}))",
"(function(a, [...a]){})",
"(function(a, ...a){})",
"(function(a, [a, ...b]){})",
"(function(a, {b: a, c: [...d]}){})",
"(function(a, a, {b: [...c]}){})"
];
var test = function(code) {
return function () {
uglify.parse(code);
}
}
var error = function (e) {
return e instanceof uglify.JS_Parse_Error &&
/^Parameter [a-zA-Z]+ was used already$/.test(e.message);
}
for (var i = 0; i < tests.length; i++) {
assert.throws(test(tests[i]), error, tests[i]);
}
});
});

View File

@@ -71,7 +71,7 @@ describe("Getters and setters", function() {
var fail = function(data) { var fail = function(data) {
return function (e) { return function (e) {
return e instanceof UglifyJS.JS_Parse_Error && return e instanceof UglifyJS.JS_Parse_Error &&
/^Unexpected token: /.test(e.message); e.message === "Unexpected token: operator (" + data.operator + ")";
}; };
}; };

View File

@@ -1,265 +0,0 @@
var assert = require("assert");
var uglify = require("../node");
describe("Left-hand side expressions", function () {
it("Should parse destructuring with const/let/var correctly", function () {
var decls = uglify.parse('var {a,b} = foo, { c, d } = bar');
assert.equal(decls.body[0].TYPE, 'Var');
assert.equal(decls.body[0].definitions.length, 2);
// Item 1
assert.equal(decls.body[0].definitions[0].name.TYPE, 'Destructuring');
assert.equal(decls.body[0].definitions[0].value.TYPE, 'SymbolRef');
// Item 2
assert.equal(decls.body[0].definitions[1].name.TYPE, 'Destructuring');
assert.equal(decls.body[0].definitions[1].value.TYPE, 'SymbolRef');
var nested_def = uglify.parse('var [{x}] = foo').body[0].definitions[0];
assert.equal(nested_def.name.names[0].names[0].TYPE, 'ObjectKeyVal');
assert.equal(nested_def.name.names[0].names[0].value.TYPE, 'SymbolVar');
assert.equal(nested_def.name.names[0].names[0].key, 'x');
assert.equal(nested_def.name.names[0].names[0].value.name, 'x');
var holey_def = uglify.parse('const [,,third] = [1,2,3]').body[0].definitions[0];
assert.equal(holey_def.name.names[0].TYPE, 'Hole');
assert.equal(holey_def.name.names[1].TYPE, 'Hole');
assert.equal(holey_def.name.names[2].TYPE, 'SymbolConst');
var expanding_def = uglify.parse('var [first, ...rest] = [1,2,3]').body[0].definitions[0];
assert.equal(expanding_def.name.names[0].TYPE, 'SymbolVar');
assert.equal(expanding_def.name.names[1].TYPE, 'Expansion');
assert.equal(expanding_def.name.names[1].expression.TYPE, 'SymbolVar');
});
it("Parser should use AST_Array for array literals", function() {
var ast = uglify.parse('["foo", "bar"]');
assert(ast.body[0] instanceof uglify.AST_SimpleStatement);
assert(ast.body[0].body instanceof uglify.AST_Array);
ast = uglify.parse('a = ["foo"]');
assert(ast.body[0] instanceof uglify.AST_SimpleStatement);
assert(ast.body[0].body instanceof uglify.AST_Assign);
assert(ast.body[0].body.left instanceof uglify.AST_SymbolRef);
assert.equal(ast.body[0].body.operator, "=");
assert(ast.body[0].body.right instanceof uglify.AST_Array);
});
it("Parser should use AST_Object for object literals", function() {
var ast = uglify.parse('({foo: "bar"})');
assert(ast.body[0] instanceof uglify.AST_SimpleStatement);
assert(ast.body[0].body instanceof uglify.AST_Object);
// This example should be fine though
ast = uglify.parse('a = {foo: "bar"}');
assert(ast.body[0] instanceof uglify.AST_SimpleStatement);
assert(ast.body[0].body instanceof uglify.AST_Assign);
assert(ast.body[0].body.left instanceof uglify.AST_SymbolRef);
assert.equal(ast.body[0].body.operator, "=");
assert(ast.body[0].body.right instanceof uglify.AST_Object);
});
it("Parser should use AST_Destructuring for array assignment patterns", function() {
var ast = uglify.parse('[foo, bar] = [1, 2]');
assert(ast.body[0] instanceof uglify.AST_SimpleStatement);
assert(ast.body[0].body instanceof uglify.AST_Assign);
assert(ast.body[0].body.left instanceof uglify.AST_Destructuring);
assert.strictEqual(ast.body[0].body.left.is_array, true);
assert.equal(ast.body[0].body.operator, "=");
assert(ast.body[0].body.right instanceof uglify.AST_Array);
});
it("Parser should use AST_Destructuring for object assignment patterns", function() {
var ast = uglify.parse('({a: b, b: c} = {b: "c", c: "d"})');
assert(ast.body[0] instanceof uglify.AST_SimpleStatement);
assert(ast.body[0].body instanceof uglify.AST_Assign);
assert(ast.body[0].body.left instanceof uglify.AST_Destructuring);
assert.strictEqual(ast.body[0].body.left.is_array, false);
assert.equal(ast.body[0].body.operator, "=");
assert(ast.body[0].body.right instanceof uglify.AST_Object);
});
it("Parser should be able to handle nested destructuring", function() {
var ast = uglify.parse('[{a,b},[d, e, f, {g, h}]] = [{a: 1, b: 2}, [3, 4, 5, {g: 6, h: 7}]]');
assert(ast.body[0] instanceof uglify.AST_SimpleStatement);
assert(ast.body[0].body instanceof uglify.AST_Assign);
assert(ast.body[0].body.left instanceof uglify.AST_Destructuring);
assert.strictEqual(ast.body[0].body.left.is_array, true);
assert.equal(ast.body[0].body.operator, "=");
assert(ast.body[0].body.right instanceof uglify.AST_Array);
assert(ast.body[0].body.left.names[0] instanceof uglify.AST_Destructuring);
assert.strictEqual(ast.body[0].body.left.names[0].is_array, false);
assert(ast.body[0].body.left.names[1] instanceof uglify.AST_Destructuring);
assert.strictEqual(ast.body[0].body.left.names[1].is_array, true);
assert(ast.body[0].body.left.names[1].names[3] instanceof uglify.AST_Destructuring);
assert.strictEqual(ast.body[0].body.left.names[1].names[3].is_array, false);
});
it("Should handle spread operator in destructuring", function() {
var ast = uglify.parse("[a, b, ...c] = [1, 2, 3, 4, 5]");
assert(ast.body[0] instanceof uglify.AST_SimpleStatement);
assert(ast.body[0].body instanceof uglify.AST_Assign);
assert(ast.body[0].body.left instanceof uglify.AST_Destructuring);
assert.strictEqual(ast.body[0].body.left.is_array, true);
assert.equal(ast.body[0].body.operator, "=");
assert(ast.body[0].body.right instanceof uglify.AST_Array);
assert(ast.body[0].body.left.names[0] instanceof uglify.AST_SymbolRef);
assert(ast.body[0].body.left.names[1] instanceof uglify.AST_SymbolRef);
assert(ast.body[0].body.left.names[2] instanceof uglify.AST_Expansion);
});
it("Should handle default assignments in destructuring", function() {
var ast = uglify.parse("({x: v, z = z + 5} = obj);");
assert(ast.body[0] instanceof uglify.AST_SimpleStatement);
assert(ast.body[0].body instanceof uglify.AST_Assign);
assert(ast.body[0].body.left instanceof uglify.AST_Destructuring);
assert.strictEqual(ast.body[0].body.left.is_array, false);
assert.equal(ast.body[0].body.operator, "=");
assert(ast.body[0].body.right instanceof uglify.AST_SymbolRef);
assert(ast.body[0].body.left.names[0].value instanceof uglify.AST_SymbolRef);
assert.strictEqual(ast.body[0].body.left.names[0].start.value, "x");
assert(ast.body[0].body.left.names[1].value instanceof uglify.AST_DefaultAssign);
assert.strictEqual(ast.body[0].body.left.names[1].start.value, "z");
assert(ast.body[0].body.left.names[1].value.left instanceof uglify.AST_SymbolRef);
assert.strictEqual(ast.body[0].body.left.names[1].value.operator, "=");
assert(ast.body[0].body.left.names[1].value.right instanceof uglify.AST_Binary);
ast = uglify.parse("({x = 123} = obj);");
assert(ast.body[0] instanceof uglify.AST_SimpleStatement);
assert(ast.body[0].body instanceof uglify.AST_Assign);
assert(ast.body[0].body.left instanceof uglify.AST_Destructuring);
assert.strictEqual(ast.body[0].body.left.is_array, false);
assert.equal(ast.body[0].body.operator, "=");
assert(ast.body[0].body.right instanceof uglify.AST_SymbolRef);
assert(ast.body[0].body.left.names[0].value instanceof uglify.AST_DefaultAssign);
assert.strictEqual(ast.body[0].body.left.names[0].value.operator, "=");
assert(ast.body[0].body.left.names[0].value.left instanceof uglify.AST_SymbolRef);
ast = uglify.parse("[x, y = 5] = foo");
assert(ast.body[0] instanceof uglify.AST_SimpleStatement);
assert(ast.body[0].body instanceof uglify.AST_Assign);
assert(ast.body[0].body.left instanceof uglify.AST_Destructuring);
assert.strictEqual(ast.body[0].body.left.is_array, true);
assert.equal(ast.body[0].body.operator, "=");
assert(ast.body[0].body.right instanceof uglify.AST_SymbolRef);
assert(ast.body[0].body.left.names[0] instanceof uglify.AST_SymbolRef);
assert.strictEqual(ast.body[0].body.left.names[0].start.value, "x");
assert(ast.body[0].body.left.names[1] instanceof uglify.AST_DefaultAssign);
assert(ast.body[0].body.left.names[1].left instanceof uglify.AST_SymbolRef);
assert.strictEqual(ast.body[0].body.left.names[1].start.value, "y");
});
it("Should handle default assignments containing assignments in a destructuring", function() {
var ast = uglify.parse("[x, y = z = 2] = a;");
assert(ast.body[0] instanceof uglify.AST_SimpleStatement);
assert(ast.body[0].body instanceof uglify.AST_Assign);
assert(ast.body[0].body.left instanceof uglify.AST_Destructuring);
assert.strictEqual(ast.body[0].body.left.is_array, true);
assert.equal(ast.body[0].body.operator, "=");
assert(ast.body[0].body.right instanceof uglify.AST_SymbolRef);
assert(ast.body[0].body.left.names[0] instanceof uglify.AST_SymbolRef);
assert(ast.body[0].body.left.names[1] instanceof uglify.AST_DefaultAssign);
assert(ast.body[0].body.left.names[1].left instanceof uglify.AST_SymbolRef);
assert.equal(ast.body[0].body.left.names[1].operator, "=");
assert(ast.body[0].body.left.names[1].right instanceof uglify.AST_Assign);
assert(ast.body[0].body.left.names[1].right.left instanceof uglify.AST_SymbolRef);
assert.equal(ast.body[0].body.left.names[1].right.operator, "=");
assert(ast.body[0].body.left.names[1].right.right instanceof uglify.AST_Number);
ast = uglify.parse("({a: a = 123} = obj)");
assert(ast.body[0] instanceof uglify.AST_SimpleStatement);
assert(ast.body[0].body instanceof uglify.AST_Assign);
assert(ast.body[0].body.left instanceof uglify.AST_Destructuring);
assert.strictEqual(ast.body[0].body.left.is_array, false);
assert.equal(ast.body[0].body.operator, "=");
assert(ast.body[0].body.right instanceof uglify.AST_SymbolRef);
assert(ast.body[0].body.left.names[0] instanceof uglify.AST_ObjectProperty);
assert.strictEqual(ast.body[0].body.left.names[0].key, "a");
assert(ast.body[0].body.left.names[0].value instanceof uglify.AST_DefaultAssign);
assert(ast.body[0].body.left.names[0].value.left instanceof uglify.AST_SymbolRef);
assert.strictEqual(ast.body[0].body.left.names[0].value.operator, "=");
assert(ast.body[0].body.left.names[0].value.right instanceof uglify.AST_Number);
});
it("Should allow multiple spread in array literals", function() {
var ast = uglify.parse("var a = [1, 2, 3], b = [4, 5, 6], joined; joined = [...a, ...b]");
assert(ast.body[0] instanceof uglify.AST_Var);
assert(ast.body[1] instanceof uglify.AST_SimpleStatement);
// Check statement containing array with spreads
assert(ast.body[1].body instanceof uglify.AST_Assign);
assert(ast.body[1].body.left instanceof uglify.AST_SymbolRef);
assert.equal(ast.body[1].body.operator, "=");
assert(ast.body[1].body.right instanceof uglify.AST_Array);
// Check array content
assert.strictEqual(ast.body[1].body.right.elements.length, 2);
assert(ast.body[1].body.right.elements[0] instanceof uglify.AST_Expansion);
assert(ast.body[1].body.right.elements[0].expression instanceof uglify.AST_SymbolRef);
assert(ast.body[1].body.right.elements[0].expression.name, "a");
assert(ast.body[1].body.right.elements[1] instanceof uglify.AST_Expansion);
assert(ast.body[1].body.right.elements[1].expression instanceof uglify.AST_SymbolRef);
assert(ast.body[1].body.right.elements[1].expression.name, "b");
});
it("Should not allow spread on invalid locations", function() {
var expect = function(input, expected) {
var execute = function(input) {
return function() {
uglify.parse(input);
}
}
var check = function(e) {
return e instanceof uglify.JS_Parse_Error &&
e.message === expected;
}
assert.throws(execute(input), check);
}
// Spreads are not allowed in destructuring array if it's not the last element
expect("[...a, ...b] = [1, 2, 3, 4]", "Spread must the be last element in destructuring array");
// Multiple spreads are not allowed in destructuring array
expect("[...a, ...b] = [1, 2, 3, 4]", "Spread must the be last element in destructuring array");
// Spread in obvious object pattern
expect("({...a} = foo)", "Unexpected token: expand (...)");
// Spread in block should not be allowed
expect("{...a} = foo", "Unexpected token: expand (...)");
// Not in standard yet
expect("let foo = {bar: 42}, bar; bar = {...foo}", "Unexpected token: expand (...)");
});
});

View File

@@ -138,13 +138,25 @@ describe("minify", function() {
}); });
var code = result.code; var code = result.code;
assert.strictEqual(code, "var a=function(n){return n};\n" + assert.strictEqual(code, "var a=function(n){return n};\n" +
"//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIjAiXSwibmFtZXMiOlsiYSIsImZvbyJdLCJtYXBwaW5ncyI6IkFBQUEsR0FBSUEsR0FBSSxTQUFTQyxHQUFPLE1BQU9BIn0="); "//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIjAiXSwibmFtZXMiOlsiYSIsImZvbyJdLCJtYXBwaW5ncyI6IkFBQUEsSUFBSUEsRUFBSSxTQUFTQyxHQUFPLE9BQU9BIn0=");
}); });
it("should not append source map to output js when sourceMapInline is not enabled", function() { it("should not append source map to output js when sourceMapInline is not enabled", function() {
var result = Uglify.minify('var a = function(foo) { return foo; };'); var result = Uglify.minify('var a = function(foo) { return foo; };');
var code = result.code; var code = result.code;
assert.strictEqual(code, "var a=function(n){return n};"); assert.strictEqual(code, "var a=function(n){return n};");
}); });
it("should work with max_line_len", function() {
var result = Uglify.minify(read("./test/input/issue-505/input.js"), {
output: {
max_line_len: 20
},
sourceMap: {
url: "inline"
}
});
assert.strictEqual(result.error, undefined);
assert.strictEqual(result.code, read("./test/input/issue-505/output.js"));
});
}); });
describe("#__PURE__", function() { describe("#__PURE__", function() {
@@ -181,4 +193,19 @@ describe("minify", function() {
assert.strictEqual(err.col, 12); assert.strictEqual(err.col, 12);
}); });
}); });
describe("global_defs", function() {
it("should throw for non-trivial expressions", function() {
var result = Uglify.minify("alert(42);", {
compress: {
global_defs: {
"@alert": "debugger"
}
}
});
var err = result.error;
assert.ok(err instanceof Error);
assert.strictEqual(err.stack.split(/\n/)[0], "Error: Can't handle expression: debugger");
});
});
}); });

View File

@@ -1,5 +1,5 @@
var assert = require("assert"); var assert = require("assert");
var uglify = require("../node"); var uglify = require("../../");
describe("New", function() { describe("New", function() {
it("Should add trailing parentheses for new expressions with zero arguments in beautify mode", function() { it("Should add trailing parentheses for new expressions with zero arguments in beautify mode", function() {
@@ -83,11 +83,4 @@ describe("New", function() {
); );
} }
}); });
it("Should check target in new.target", function() {
assert.throws(function() {uglify.parse("new.blah")}, function(e) {
return e instanceof uglify.JS_Parse_Error
&& e.message === "Unexpected token name «blah», expected name «target»";
});
});
}); });

View File

@@ -1,131 +0,0 @@
var Uglify = require("../node");
var assert = require("assert");
describe("Object", function() {
it("Should allow objects to have a methodDefinition as property", function() {
var code = "var a = {test() {return true;}}";
assert.equal(Uglify.minify(code).code, "var a={test(){return!0}};");
});
it("Should not allow objects to use static keywords like in classes", function() {
var code = "{static test() {}}";
var parse = function() {
Uglify.parse(code);
}
var expect = function(e) {
return e instanceof Uglify.JS_Parse_Error;
}
assert.throws(parse, expect);
});
it("Should not allow objects to have static computed properties like in classes", function() {
var code = "var foo = {static [123](){}}";
var parse = function() {
console.log(Uglify.parse(code).body[0].body[0]);
}
var expect = function(e) {
return e instanceof Uglify.JS_Parse_Error;
}
assert.throws(parse, expect);
});
it("Should not accept operator tokens as property/getter/setter name", function() {
var illegalOperators = [
"++",
"--",
"+",
"-",
"!",
"~",
"&",
"|",
"^",
"*",
"/",
"%",
">>",
"<<",
">>>",
"<",
">",
"<=",
">=",
"==",
"===",
"!=",
"!==",
"?",
"=",
"+=",
"-=",
"/=",
"*=",
"%=",
">>=",
"<<=",
">>>=",
"|=",
"^=",
"&=",
"&&",
"||"
];
var generator = function() {
var results = [];
for (var i in illegalOperators) {
results.push({
code: "var obj = { get " + illegalOperators[i] + "() { return test; }};",
operator: illegalOperators[i],
method: "get"
});
results.push({
code: "var obj = { set " + illegalOperators[i] + "(value) { test = value}};",
operator: illegalOperators[i],
method: "set"
});
results.push({
code: "var obj = { " + illegalOperators[i] + ': "123"};',
operator: illegalOperators[i],
method: "key"
});
results.push({
code: "var obj = { " + illegalOperators[i] + "(){ return test; }};",
operator: illegalOperators[i],
method: "method"
});
}
return results;
};
var testCase = function(data) {
return function() {
Uglify.parse(data.code);
};
};
var fail = function(data) {
return function (e) {
return e instanceof Uglify.JS_Parse_Error && (
e.message === "Unexpected token: operator (" + data.operator + ")" ||
(e.message === "Unterminated regular expression" && data.operator[0] === "/") ||
(e.message === "Unexpected token: punc (()" && data.operator === "*")
);
};
};
var errorMessage = function(data) {
return "Expected but didn't get a syntax error while parsing following line:\n" + data.code;
};
var tests = generator();
for (var i = 0; i < tests.length; i++) {
var test = tests[i];
assert.throws(testCase(test), fail(test), errorMessage(test));
}
});
it("Should be able to use shorthand properties", function() {
var ast = Uglify.parse("var foo = 123\nvar obj = {foo: foo}");
assert.strictEqual(ast.print_to_string({ecma: 6}), "var foo=123;var obj={foo};");
})
});

View File

@@ -15,7 +15,9 @@ describe("spidermonkey export/import sanity test", function() {
eval(stdout); eval(stdout);
assert.strictEqual(typeof SpiderUglify, "object"); assert.strictEqual(typeof SpiderUglify, "object");
assert.strictEqual(SpiderUglify.minify("foo([true,,2+3]);").code, "foo([!0,,5]);"); var result = SpiderUglify.minify("foo([true,,2+3]);");
assert.strictEqual(result.error, undefined);
assert.strictEqual(result.code, "foo([!0,,5]);");
done(); done();
}); });

View File

@@ -1,33 +0,0 @@
var assert = require("assert");
var uglify = require("../node");
describe("Template string", function() {
it("Should not accept invalid sequences", function() {
var tests = [
// Stress invalid expression
"var foo = `Hello ${]}`",
"var foo = `Test 123 ${>}`",
"var foo = `Blah ${;}`",
// Stress invalid template_substitution after expression
"var foo = `Blablabla ${123 456}`",
"var foo = `Blub ${123;}`",
"var foo = `Bleh ${a b}`"
];
var exec = function(test) {
return function() {
uglify.parse(test);
}
};
var fail = function(e) {
return e instanceof uglify.JS_Parse_Error
&& /^Unexpected token: /.test(e.message);
};
for (var i = 0; i < tests.length; i++) {
assert.throws(exec(tests[i]), fail, tests[i]);
}
});
});

View File

@@ -1,22 +0,0 @@
var assert = require("assert");
var uglify = require("../node");
describe("Try", function() {
it("Should not allow catch with an empty parameter", function() {
var tests = [
"try {} catch() {}"
];
var test = function(code) {
return function () {
uglify.parse(code);
}
}
var error = function (e) {
return e instanceof uglify.JS_Parse_Error;
}
for (var i = 0; i < tests.length; i++) {
assert.throws(test(tests[i]), error, tests[i]);
}
});
});

View File

@@ -1,145 +0,0 @@
var assert = require("assert");
var uglify = require("../node");
describe("Unicode", function() {
it("Should not accept invalid code ranges in unicode escape", function() {
var tests = [
"\\u{110000}", // A bit over the unicode range
"\\u{100000061} = 'foo'", // 32-bit overflow resulting in "a"
"\\u{fffffffffff}", // A bit too much over the unicode range
];
var exec = function(test) {
return function() {
uglify.parse(test);
}
}
var fail = function(e) {
return e instanceof uglify.JS_Parse_Error
&& e.message === "Unicode reference out of bounce";
}
for (var i = 0; i < tests.length; i++) {
assert.throws(exec(tests[i]), fail);
}
});
it("Should not accept invalid unicode sequences", function() {
var tests = [
"var foo = '\\u-111'",
"var bar = '\\u{-1}'",
"var baz = '\\ugggg'"
];
var exec = function(test) {
return function() {
uglify.parse(test);
}
}
var fail = function(e) {
return e instanceof uglify.JS_Parse_Error
&& e.message === "Invalid hex-character pattern in string";
}
for (var i = 0; i < tests.length; i++) {
assert.throws(exec(tests[i]), fail);
}
});
it("Should throw error if escaped first identifier char is not part of ID_start", function() {
var tests = [
'var \\u{0} = "foo";',
'var \\u{10ffff} = "bar";',
'var \\u000a = "what\'s up";',
// Valid ID_Start, but using up 2 escaped characters and not fitting in IdentifierStart
'var \\ud800\\udc00 = "Hello";',
'var \\udbff\\udfff = "Unicode";', // Same as previous test
'var \\ud800\udc01 = "Weird unicode";', // Same as above, but mixed escaped with unicode chars
];
var exec = function(test) {
return function() {
uglify.parse(test);
}
}
var fail = function(e) {
return e instanceof uglify.JS_Parse_Error
&& e.message === "First identifier char is an invalid identifier char";
}
for (var i = 0; i < tests.length; i++) {
assert.throws(exec(tests[i]), fail);
}
});
it("Should throw error if escaped non-first identifier char is not part of ID_start", function() {
var tests = [
'var a\\u{0} = "foo";',
'var a\\u{10ffff} = "bar";',
'var z\\u000a = "what\'s up";'
];
var exec = function(test) {
return function() {
uglify.parse(test);
}
}
var fail = function(e) {
return e instanceof uglify.JS_Parse_Error
&& e.message === "Invalid escaped identifier char";
}
for (var i = 0; i < tests.length; i++) {
assert.throws(exec(tests[i]), fail);
}
});
it("Should throw error if identifier is a keyword with a escape sequences", function() {
var tests = [
'var \\u0069\\u006e = "foo"', // in
'var \\u0076\\u0061\\u0072 = "bar"', // var
'var \\u{66}\\u{6f}\\u{72} = "baz"', // for
'var \\u0069\\u{66} = "foobar"', // if
'var \\u{73}uper' // super
];
var exec = function(test) {
return function() {
uglify.parse(test);
}
}
var fail = function(e) {
return e instanceof uglify.JS_Parse_Error
&& e.message === "Escaped characters are not allowed in keywords";
}
for (var i = 0; i < tests.length; i++) {
assert.throws(exec(tests[i]), fail);
}
});
it("Should read strings containing surigates correctly", function() {
var tests = [
['var a = "\ud800\udc00";', 'var a="\\u{10000}";'],
['var b = "\udbff\udfff";', 'var b="\\u{10ffff}";']
];
for (var i = 0; i < tests.length; i++) {
assert.strictEqual(uglify.minify(tests[i][0], {
output: { ascii_only: true, ecma: 6 }
}).code, tests[i][1]);
}
});
it("Should parse raw characters correctly", function() {
var ast = uglify.parse('console.log("\\udbff");');
assert.strictEqual(ast.print_to_string(), 'console.log("\udbff");');
ast = uglify.parse(ast.print_to_string());
assert.strictEqual(ast.print_to_string(), 'console.log("\udbff");');
});
});

View File

@@ -1,106 +0,0 @@
var UglifyJS = require("../node");
var assert = require("assert");
describe("Yield", function() {
it("Should not delete statements after yield", function() {
var js = 'function *foo(bar) { yield 1; yield 2; return 3; }';
var result = UglifyJS.minify(js);
assert.strictEqual(result.code, 'function*foo(e){return yield 1,yield 2,3}');
});
it("Should not allow yield as labelIdentifier within generators", function() {
var js = "function* g() {yield: 1}"
var test = function() {
UglifyJS.parse(js);
}
var expect = function(e) {
return e instanceof UglifyJS.JS_Parse_Error &&
e.message === "Yield cannot be used as label inside generators";
}
assert.throws(test, expect);
});
it("Should not allow yield* followed by a semicolon in generators", function() {
var js = "function* test() {yield*\n;}";
var test = function() {
UglifyJS.parse(js);
}
var expect = function(e) {
return e instanceof UglifyJS.JS_Parse_Error &&
e.message === "Unexpected token: punc (;)";
}
assert.throws(test, expect);
});
it("Should not allow yield with next token star on next line", function() {
var js = "function* test() {yield\n*123;}";
var test = function() {
UglifyJS.parse(js);
}
var expect = function(e) {
return e instanceof UglifyJS.JS_Parse_Error &&
e.message === "Unexpected token: operator (*)";
}
assert.throws(test, expect);
});
it("Should be able to compress its expression", function() {
assert.strictEqual(
UglifyJS.minify("function *f() { yield 3-4; }", {compress: true}).code,
"function*f(){yield-1}"
);
});
it("Should keep undefined after yield without compression if found in ast", function() {
assert.strictEqual(
UglifyJS.minify("function *f() { yield undefined; yield; yield* undefined; yield void 0}", {compress: false}).code,
"function*f(){yield undefined;yield;yield*undefined;yield void 0}"
);
});
it("Should be able to drop undefined after yield if necessary with compression", function() {
assert.strictEqual(
UglifyJS.minify("function *f() { yield undefined; yield; yield* undefined; yield void 0}", {compress: true}).code,
"function*f(){yield,yield,yield*void 0,yield}"
);
});
it("Should not allow yield to be used as symbol, identifier or property outside generators in strict mode", function() {
var tests = [
// Fail as as_symbol
'"use strict"; import yield from "bar";',
'"use strict"; yield = 123;',
'"use strict"; yield: "123";',
'"use strict"; for(;;){break yield;}',
'"use strict"; for(;;){continue yield;}',
'"use strict"; function yield(){}',
'"use strict"; function foo(...yield){}',
'"use strict"; try { new Error("")} catch (yield) {}',
'"use strict"; var yield = "foo";',
'"use strict"; class yield {}',
// Fail as maybe_assign
'"use strict"; var foo = yield;',
'"use strict"; var foo = bar = yield',
// Fail as as_property_name
'"use strict"; var foo = {yield};',
'"use strict"; var bar = {yield: "foo"};'
];
var fail = function(e) {
return e instanceof UglifyJS.JS_Parse_Error &&
/^Unexpected yield identifier (?:as parameter )?inside strict mode/.test(e.message);
}
var test = function(input) {
return function() {
UglifyJS.parse(input);
}
}
for (var i = 0; i < tests.length; i++) {
assert.throws(test(tests[i]), fail, tests[i]);
}
});
});

View File

@@ -1,103 +1,73 @@
// Testing UglifyJS <-> SpiderMonkey AST conversion // Testing UglifyJS <-> SpiderMonkey AST conversion
// through generative testing. "use strict";
var UglifyJS = require("./node"),
escodegen = require("escodegen"),
esfuzz = require("esfuzz"),
estraverse = require("estraverse"),
prefix = "\r ";
// Normalizes input AST for UglifyJS in order to get correct comparison.
function normalizeInput(ast) {
return estraverse.replace(ast, {
enter: function(node, parent) {
switch (node.type) {
// Internally mark all the properties with semi-standard type "Property".
case "ObjectExpression":
node.properties.forEach(function (property) {
property.type = "Property";
});
break;
// Since UglifyJS doesn"t recognize different types of property keys,
// decision on SpiderMonkey node type is based on check whether key
// can be valid identifier or not - so we do in input AST.
case "Property":
var key = node.key;
if (key.type === "Literal" && typeof key.value === "string" && UglifyJS.is_identifier(key.value)) {
node.key = {
type: "Identifier",
name: key.value
};
} else if (key.type === "Identifier" && !UglifyJS.is_identifier(key.name)) {
node.key = {
type: "Literal",
value: key.name
};
}
break;
// UglifyJS internally flattens all the expression sequences - either
// to one element (if sequence contains only one element) or flat list.
case "SequenceExpression":
node.expressions = node.expressions.reduce(function flatten(list, expr) {
return list.concat(expr.type === "SequenceExpression" ? expr.expressions.reduce(flatten, []) : [expr]);
}, []);
if (node.expressions.length === 1) {
return node.expressions[0];
}
break;
}
}
});
}
module.exports = function(options) {
console.log("--- UglifyJS <-> Mozilla AST conversion");
for (var counter = 0; counter < options.iterations; counter++) {
process.stdout.write(prefix + counter + "/" + options.iterations);
var ast1 = normalizeInput(esfuzz.generate({
maxDepth: options.maxDepth
}));
var ast2 =
UglifyJS
.AST_Node
.from_mozilla_ast(ast1)
.to_mozilla_ast();
var astPair = [
{name: 'expected', value: ast1},
{name: 'actual', value: ast2}
];
var jsPair = astPair.map(function(item) {
return {
name: item.name,
value: escodegen.generate(item.value)
}
});
if (jsPair[0].value !== jsPair[1].value) {
var fs = require("fs");
var acorn = require("acorn"); var acorn = require("acorn");
var ufuzz = require("./ufuzz");
var UglifyJS = require("..");
fs.existsSync("tmp") || fs.mkdirSync("tmp"); function try_beautify(code) {
var beautified = UglifyJS.minify(code, {
jsPair.forEach(function (item) { compress: false,
var fileName = "tmp/dump_" + item.name; mangle: false,
var ast = acorn.parse(item.value); output: {
fs.writeFileSync(fileName + ".js", item.value); beautify: true,
fs.writeFileSync(fileName + ".json", JSON.stringify(ast, null, 2)); bracketize: true
}
}); });
if (beautified.error) {
process.stdout.write("\n"); console.log("// !!! beautify failed !!!");
throw new Error("Got different outputs, check out tmp/dump_*.{js,json} for codes and ASTs."); console.log(beautified.error.stack);
console.log(code);
} else {
console.log("// (beautified)");
console.log(beautified.code);
} }
} }
process.stdout.write(prefix + "Probability of error is less than " + (100 / options.iterations) + "%, stopping.\n"); function test(original, estree, description) {
}; var transformed = UglifyJS.minify(UglifyJS.AST_Node.from_mozilla_ast(estree), {
compress: false,
mangle: false
});
if (transformed.error || original !== transformed.code) {
console.log("//=============================================================");
console.log("// !!!!!! Failed... round", round);
console.log("// original code");
try_beautify(original);
console.log();
console.log();
console.log("//-------------------------------------------------------------");
console.log("//", description);
if (transformed.error) {
console.log(transformed.error.stack);
} else {
try_beautify(transformed.code);
}
console.log("!!!!!! Failed... round", round);
process.exit(1);
}
}
var num_iterations = ufuzz.num_iterations;
for (var round = 1; round <= num_iterations; round++) {
process.stdout.write(round + " of " + num_iterations + "\r");
var code = ufuzz.createTopLevelCode();
var uglified = UglifyJS.minify(code, {
compress: false,
mangle: false,
output: {
ast: true
}
});
test(uglified.code, uglified.ast.to_mozilla_ast(), "AST_Node.to_mozilla_ast()");
try {
test(uglified.code, acorn.parse(code), "acorn.parse()");
} catch (e) {
console.log("//=============================================================");
console.log("// acorn parser failed... round", round);
console.log(e);
console.log("// original code");
console.log(code);
}
}
console.log();

View File

@@ -5,6 +5,7 @@ var path = require("path");
var fs = require("fs"); var fs = require("fs");
var assert = require("assert"); var assert = require("assert");
var sandbox = require("./sandbox"); var sandbox = require("./sandbox");
var semver = require("semver");
var tests_dir = path.dirname(module.filename); var tests_dir = path.dirname(module.filename);
var failures = 0; var failures = 0;
@@ -23,12 +24,6 @@ mocha_tests();
var run_sourcemaps_tests = require('./sourcemaps'); var run_sourcemaps_tests = require('./sourcemaps');
run_sourcemaps_tests(); run_sourcemaps_tests();
var run_ast_conversion_tests = require("./mozilla-ast");
run_ast_conversion_tests({
iterations: 1000
});
/* -----[ utils ]----- */ /* -----[ utils ]----- */
function tmpl() { function tmpl() {
@@ -170,7 +165,8 @@ function run_compress_tests() {
failed_files[file] = 1; failed_files[file] = 1;
} }
} }
if (test.expect_stdout) { if (test.expect_stdout
&& (!test.node_version || semver.satisfies(process.version, test.node_version))) {
var stdout = sandbox.run_code(input_code); var stdout = sandbox.run_code(input_code);
if (test.expect_stdout === true) { if (test.expect_stdout === true) {
test.expect_stdout = stdout; test.expect_stdout = stdout;
@@ -280,7 +276,14 @@ function parse_test(file) {
if (node instanceof U.AST_LabeledStatement) { if (node instanceof U.AST_LabeledStatement) {
var label = node.label; var label = node.label;
assert.ok( assert.ok(
["input", "expect", "expect_exact", "expect_warnings", "expect_stdout"].indexOf(label.name) >= 0, [
"input",
"expect",
"expect_exact",
"expect_warnings",
"expect_stdout",
"node_version",
].indexOf(label.name) >= 0,
tmpl("Unsupported label {name} [{line},{col}]", { tmpl("Unsupported label {name} [{line},{col}]", {
name: label.name, name: label.name,
line: label.start.line, line: label.start.line,
@@ -288,7 +291,7 @@ function parse_test(file) {
}) })
); );
var stat = node.body; var stat = node.body;
if (label.name == "expect_exact") { if (label.name == "expect_exact" || label.name == "node_version") {
test[label.name] = read_string(stat); test[label.name] = read_string(stat);
} else if (label.name == "expect_stdout") { } else if (label.name == "expect_stdout") {
if (stat.TYPE == "SimpleStatement" && stat.body instanceof U.AST_Boolean) { if (stat.TYPE == "SimpleStatement" && stat.body instanceof U.AST_Boolean) {

View File

@@ -1,14 +1,17 @@
var semver = require("semver");
var vm = require("vm"); var vm = require("vm");
function safe_log(arg) { function safe_log(arg, level) {
if (arg) switch (typeof arg) { if (arg) switch (typeof arg) {
case "function": case "function":
return arg.toString(); return arg.toString();
case "object": case "object":
if (/Error$/.test(arg.name)) return arg.toString(); if (/Error$/.test(arg.name)) return arg.toString();
arg.constructor.toString(); arg.constructor.toString();
for (var key in arg) { if (level--) for (var key in arg) {
arg[key] = safe_log(arg[key]); if (!Object.getOwnPropertyDescriptor(arg, key).get) {
arg[key] = safe_log(arg[key], level);
}
} }
} }
return arg; return arg;
@@ -48,7 +51,9 @@ exports.run_code = function(code) {
].join("\n"), { ].join("\n"), {
console: { console: {
log: function() { log: function() {
return console.log.apply(console, [].map.call(arguments, safe_log)); return console.log.apply(console, [].map.call(arguments, function(arg) {
return safe_log(arg, 3);
}));
} }
} }
}, { timeout: 5000 }); }, { timeout: 5000 });
@@ -59,7 +64,7 @@ exports.run_code = function(code) {
process.stdout.write = original_write; process.stdout.write = original_write;
} }
}; };
exports.same_stdout = ~process.version.lastIndexOf("v0.12.", 0) ? function(expected, actual) { exports.same_stdout = semver.satisfies(process.version, "0.12") ? function(expected, actual) {
if (typeof expected != typeof actual) return false; if (typeof expected != typeof actual) return false;
if (typeof expected != "string") { if (typeof expected != "string") {
if (expected.name != actual.name) return false; if (expected.name != actual.name) return false;

View File

@@ -1,4 +1,4 @@
var UglifyJS = require("./node"); var UglifyJS = require("..");
var ok = require("assert"); var ok = require("assert");
module.exports = function () { module.exports = function () {
@@ -26,11 +26,11 @@ module.exports = function () {
} }
function source_map(js) { function source_map(js) {
var source_map = UglifyJS.SourceMap(); return JSON.parse(UglifyJS.minify(js, {
var stream = UglifyJS.OutputStream({ source_map: source_map }); compress: false,
var parsed = UglifyJS.parse(js); mangle: false,
parsed.print(stream); sourceMap: true
return JSON.parse(source_map.toString()); }).map);
} }
// Run standalone // Run standalone

View File

@@ -12,7 +12,7 @@
stream._handle.setBlocking(true); stream._handle.setBlocking(true);
}); });
var UglifyJS = require("./node"); var UglifyJS = require("..");
var randomBytes = require("crypto").randomBytes; var randomBytes = require("crypto").randomBytes;
var sandbox = require("./sandbox"); var sandbox = require("./sandbox");
@@ -48,8 +48,9 @@ var STMT_COUNT_FROM_GLOBAL = true; // count statement depth from nearest functio
var num_iterations = +process.argv[2] || 1/0; var num_iterations = +process.argv[2] || 1/0;
var verbose = false; // log every generated test var verbose = false; // log every generated test
var verbose_interval = false; // log every 100 generated tests var verbose_interval = false; // log every 100 generated tests
var verbose_error = false;
var use_strict = false; var use_strict = false;
var catch_redef = require.main === module;
var generate_directive = require.main === module;
for (var i = 2; i < process.argv.length; ++i) { for (var i = 2; i < process.argv.length; ++i) {
switch (process.argv[i]) { switch (process.argv[i]) {
case '-v': case '-v':
@@ -58,9 +59,6 @@ for (var i = 2; i < process.argv.length; ++i) {
case '-V': case '-V':
verbose_interval = true; verbose_interval = true;
break; break;
case '-E':
verbose_error = true;
break;
case '-t': case '-t':
MAX_GENERATED_TOPLEVELS_PER_RUN = +process.argv[++i]; MAX_GENERATED_TOPLEVELS_PER_RUN = +process.argv[++i];
if (!MAX_GENERATED_TOPLEVELS_PER_RUN) throw new Error('Must generate at least one toplevel per run'); if (!MAX_GENERATED_TOPLEVELS_PER_RUN) throw new Error('Must generate at least one toplevel per run');
@@ -79,6 +77,12 @@ for (var i = 2; i < process.argv.length; ++i) {
STMT_SECOND_LEVEL_OVERRIDE = STMT_ARG_TO_ID[name]; STMT_SECOND_LEVEL_OVERRIDE = STMT_ARG_TO_ID[name];
if (!(STMT_SECOND_LEVEL_OVERRIDE >= 0)) throw new Error('Unknown statement name; use -? to get a list'); if (!(STMT_SECOND_LEVEL_OVERRIDE >= 0)) throw new Error('Unknown statement name; use -? to get a list');
break; break;
case '--no-catch-redef':
catch_redef = false;
break;
case '--no-directive':
generate_directive = false;
break;
case '--use-strict': case '--use-strict':
use_strict = true; use_strict = true;
break; break;
@@ -103,11 +107,12 @@ for (var i = 2; i < process.argv.length; ++i) {
console.log('<number>: generate this many cases (if used must be first arg)'); console.log('<number>: generate this many cases (if used must be first arg)');
console.log('-v: print every generated test case'); console.log('-v: print every generated test case');
console.log('-V: print every 100th generated test case'); console.log('-V: print every 100th generated test case');
console.log('-E: print generated test case with runtime error');
console.log('-t <int>: generate this many toplevels per run (more take longer)'); console.log('-t <int>: generate this many toplevels per run (more take longer)');
console.log('-r <int>: maximum recursion depth for generator (higher takes longer)'); console.log('-r <int>: maximum recursion depth for generator (higher takes longer)');
console.log('-s1 <statement name>: force the first level statement to be this one (see list below)'); console.log('-s1 <statement name>: force the first level statement to be this one (see list below)');
console.log('-s2 <statement name>: force the second level statement to be this one (see list below)'); console.log('-s2 <statement name>: force the second level statement to be this one (see list below)');
console.log('--no-catch-redef: do not redefine catch variables');
console.log('--no-directive: do not generate directives');
console.log('--use-strict: generate "use strict"'); console.log('--use-strict: generate "use strict"');
console.log('--stmt-depth-from-func: reset statement depth counter at each function, counts from global otherwise'); console.log('--stmt-depth-from-func: reset statement depth counter at each function, counts from global otherwise');
console.log('--only-stmt <statement names>: a comma delimited white list of statements that may be generated'); console.log('--only-stmt <statement names>: a comma delimited white list of statements that may be generated');
@@ -192,12 +197,33 @@ var ASSIGNMENTS = [
'=', '=',
'=', '=',
'=', '=',
'=',
'=',
'=',
'=',
'=',
'=',
'=',
'=',
'=',
'=',
'=',
'=',
'=',
'=',
'==',
'!=',
'===',
'!==',
'+=', '+=',
'+=',
'+=',
'+=',
'+=',
'+=',
'+=',
'+=',
'+=',
'+=',
'-=', '-=',
'*=', '*=',
'/=', '/=',
@@ -207,7 +233,8 @@ var ASSIGNMENTS = [
'<<=', '<<=',
'>>=', '>>=',
'>>>=', '>>>=',
'%=' ]; '%=',
];
var UNARY_SAFE = [ var UNARY_SAFE = [
'+', '+',
@@ -276,6 +303,7 @@ var TYPEOF_OUTCOMES = [
'symbol', 'symbol',
'crap' ]; 'crap' ];
var unique_vars = [];
var loops = 0; var loops = 0;
var funcs = 0; var funcs = 0;
var labels = 10000; var labels = 10000;
@@ -290,6 +318,10 @@ function strictMode() {
} }
function createTopLevelCode() { function createTopLevelCode() {
VAR_NAMES.length = INITIAL_NAMES_LEN; // prune any previous names still in the list
unique_vars.length = 0;
loops = 0;
funcs = 0;
return [ return [
strictMode(), strictMode(),
'var a = 100, b = 10, c = 0;', 'var a = 100, b = 10, c = 0;',
@@ -325,33 +357,36 @@ function createArgs() {
return args.join(', '); return args.join(', ');
} }
function filterDirective(s) {
if (!generate_directive && !s[1] && /\("/.test(s[2])) s[2] = ';' + s[2];
return s;
}
function createFunction(recurmax, inGlobal, noDecl, canThrow, stmtDepth) { function createFunction(recurmax, inGlobal, noDecl, canThrow, stmtDepth) {
if (--recurmax < 0) { return ';'; } if (--recurmax < 0) { return ';'; }
if (!STMT_COUNT_FROM_GLOBAL) stmtDepth = 0; if (!STMT_COUNT_FROM_GLOBAL) stmtDepth = 0;
var func = funcs++; var func = funcs++;
var namesLenBefore = VAR_NAMES.length; var namesLenBefore = VAR_NAMES.length;
var name = (inGlobal || rng(5) > 0) ? 'f' + func : createVarName(MANDATORY, noDecl); var name;
if (name === 'a' || name === 'b' || name === 'c') name = 'f' + func; // quick hack to prevent assignment to func names of being called if (inGlobal || rng(5) > 0) name = 'f' + func;
var s = ''; else {
unique_vars.push('a', 'b', 'c');
name = createVarName(MANDATORY, noDecl);
unique_vars.length -= 3;
}
var s = [
'function ' + name + '(' + createParams() + '){',
strictMode()
];
if (rng(5) === 0) { if (rng(5) === 0) {
// functions with functions. lower the recursion to prevent a mess. // functions with functions. lower the recursion to prevent a mess.
s = [ s.push(createFunctions(rng(5) + 1, Math.ceil(recurmax * 0.7), NOT_GLOBAL, ANY_TYPE, canThrow, stmtDepth));
'function ' + name + '(' + createParams() + '){',
strictMode(),
createFunctions(rng(5) + 1, Math.ceil(recurmax * 0.7), NOT_GLOBAL, ANY_TYPE, canThrow, stmtDepth),
'}',
''
].join('\n');
} else { } else {
// functions with statements // functions with statements
s = [ s.push(createStatements(3, recurmax, canThrow, CANNOT_BREAK, CANNOT_CONTINUE, CAN_RETURN, stmtDepth));
'function ' + name + '(' + createParams() + '){',
strictMode(),
createStatements(3, recurmax, canThrow, CANNOT_BREAK, CANNOT_CONTINUE, CAN_RETURN, stmtDepth),
'}',
''
].join('\n');
} }
s.push('}', '');
s = filterDirective(s).join('\n');
VAR_NAMES.length = namesLenBefore; VAR_NAMES.length = namesLenBefore;
@@ -359,7 +394,6 @@ function createFunction(recurmax, inGlobal, noDecl, canThrow, stmtDepth) {
// avoid "function statements" (decl inside statements) // avoid "function statements" (decl inside statements)
else if (inGlobal || rng(10) > 0) s += 'var ' + createVarName(MANDATORY) + ' = ' + name + '(' + createArgs() + ');'; else if (inGlobal || rng(10) > 0) s += 'var ' + createVarName(MANDATORY) + ' = ' + name + '(' + createArgs() + ');';
return s; return s;
} }
@@ -406,7 +440,7 @@ function getLabel(label) {
return label && " L" + label; return label && " L" + label;
} }
function createStatement(recurmax, canThrow, canBreak, canContinue, cannotReturn, stmtDepth) { function createStatement(recurmax, canThrow, canBreak, canContinue, cannotReturn, stmtDepth, target) {
++stmtDepth; ++stmtDepth;
var loop = ++loops; var loop = ++loops;
if (--recurmax < 0) { if (--recurmax < 0) {
@@ -414,10 +448,11 @@ function createStatement(recurmax, canThrow, canBreak, canContinue, cannotReturn
} }
// allow to forcefully generate certain structures at first or second recursion level // allow to forcefully generate certain structures at first or second recursion level
var target = 0; if (target === undefined) {
if (stmtDepth === 1 && STMT_FIRST_LEVEL_OVERRIDE >= 0) target = STMT_FIRST_LEVEL_OVERRIDE; if (stmtDepth === 1 && STMT_FIRST_LEVEL_OVERRIDE >= 0) target = STMT_FIRST_LEVEL_OVERRIDE;
else if (stmtDepth === 2 && STMT_SECOND_LEVEL_OVERRIDE >= 0) target = STMT_SECOND_LEVEL_OVERRIDE; else if (stmtDepth === 2 && STMT_SECOND_LEVEL_OVERRIDE >= 0) target = STMT_SECOND_LEVEL_OVERRIDE;
else target = STMTS_TO_USE[rng(STMTS_TO_USE.length)]; else target = STMTS_TO_USE[rng(STMTS_TO_USE.length)];
}
switch (target) { switch (target) {
case STMT_BLOCK: case STMT_BLOCK:
@@ -460,20 +495,22 @@ function createStatement(recurmax, canThrow, canBreak, canContinue, cannotReturn
case STMT_VAR: case STMT_VAR:
switch (rng(3)) { switch (rng(3)) {
case 0: case 0:
unique_vars.push('c');
var name = createVarName(MANDATORY); var name = createVarName(MANDATORY);
if (name === 'c') name = 'a'; unique_vars.pop();
return 'var ' + name + ';'; return 'var ' + name + ';';
case 1: case 1:
// initializer can only have one expression // initializer can only have one expression
unique_vars.push('c');
var name = createVarName(MANDATORY); var name = createVarName(MANDATORY);
if (name === 'c') name = 'b'; unique_vars.pop();
return 'var ' + name + ' = ' + createExpression(recurmax, NO_COMMA, stmtDepth, canThrow) + ';'; return 'var ' + name + ' = ' + createExpression(recurmax, NO_COMMA, stmtDepth, canThrow) + ';';
default: default:
// initializer can only have one expression // initializer can only have one expression
unique_vars.push('c');
var n1 = createVarName(MANDATORY); var n1 = createVarName(MANDATORY);
if (n1 === 'c') n1 = 'b';
var n2 = createVarName(MANDATORY); var n2 = createVarName(MANDATORY);
if (n2 === 'c') n2 = 'b'; unique_vars.pop();
return 'var ' + n1 + ' = ' + createExpression(recurmax, NO_COMMA, stmtDepth, canThrow) + ', ' + n2 + ' = ' + createExpression(recurmax, NO_COMMA, stmtDepth, canThrow) + ';'; return 'var ' + n1 + ' = ' + createExpression(recurmax, NO_COMMA, stmtDepth, canThrow) + ', ' + n2 + ' = ' + createExpression(recurmax, NO_COMMA, stmtDepth, canThrow) + ';';
} }
case STMT_RETURN_ETC: case STMT_RETURN_ETC:
@@ -514,8 +551,11 @@ function createStatement(recurmax, canThrow, canBreak, canContinue, cannotReturn
var nameLenBefore = VAR_NAMES.length; var nameLenBefore = VAR_NAMES.length;
var catchName = createVarName(MANDATORY); var catchName = createVarName(MANDATORY);
var freshCatchName = VAR_NAMES.length !== nameLenBefore; var freshCatchName = VAR_NAMES.length !== nameLenBefore;
if (!catch_redef) unique_vars.push(catchName);
s += ' catch (' + catchName + ') { ' + createStatements(3, recurmax, canThrow, canBreak, canContinue, cannotReturn, stmtDepth) + ' }'; s += ' catch (' + catchName + ') { ' + createStatements(3, recurmax, canThrow, canBreak, canContinue, cannotReturn, stmtDepth) + ' }';
if (freshCatchName) VAR_NAMES.splice(nameLenBefore, 1); // remove catch name // remove catch name
if (!catch_redef) unique_vars.pop();
if (freshCatchName) VAR_NAMES.splice(nameLenBefore, 1);
} }
if (n !== 0) s += ' finally { ' + createStatements(3, recurmax, canThrow, canBreak, canContinue, cannotReturn, stmtDepth) + ' }'; if (n !== 0) s += ' finally { ' + createStatements(3, recurmax, canThrow, canBreak, canContinue, cannotReturn, stmtDepth) + ' }';
return s; return s;
@@ -593,8 +633,9 @@ function _createExpression(recurmax, noComma, stmtDepth, canThrow) {
case p++: case p++:
case p++: case p++:
var nameLenBefore = VAR_NAMES.length; var nameLenBefore = VAR_NAMES.length;
unique_vars.push('c');
var name = createVarName(MAYBE); // note: this name is only accessible from _within_ the function. and immutable at that. var name = createVarName(MAYBE); // note: this name is only accessible from _within_ the function. and immutable at that.
if (name == 'c') name = 'a'; unique_vars.pop();
var s = []; var s = [];
switch (rng(5)) { switch (rng(5)) {
case 0: case 0:
@@ -636,7 +677,7 @@ function _createExpression(recurmax, noComma, stmtDepth, canThrow) {
strictMode() strictMode()
); );
if (instantiate) for (var i = rng(4); --i >= 0;) { if (instantiate) for (var i = rng(4); --i >= 0;) {
if (rng(2)) s.push('this.' + getDotKey() + createAssignment() + _createBinaryExpr(recurmax, noComma, stmtDepth, canThrow) + ';'); if (rng(2)) s.push('this.' + getDotKey(true) + createAssignment() + _createBinaryExpr(recurmax, noComma, stmtDepth, canThrow) + ';');
else s.push('this[' + createExpression(recurmax, COMMA_OK, stmtDepth, canThrow) + ']' + createAssignment() + _createBinaryExpr(recurmax, noComma, stmtDepth, canThrow) + ';'); else s.push('this[' + createExpression(recurmax, COMMA_OK, stmtDepth, canThrow) + ']' + createAssignment() + _createBinaryExpr(recurmax, noComma, stmtDepth, canThrow) + ';');
} }
s.push( s.push(
@@ -646,7 +687,7 @@ function _createExpression(recurmax, noComma, stmtDepth, canThrow) {
break; break;
} }
VAR_NAMES.length = nameLenBefore; VAR_NAMES.length = nameLenBefore;
return s.join('\n'); return filterDirective(s).join('\n');
case p++: case p++:
case p++: case p++:
return createTypeofExpr(recurmax, stmtDepth, canThrow); return createTypeofExpr(recurmax, stmtDepth, canThrow);
@@ -689,19 +730,19 @@ function _createExpression(recurmax, noComma, stmtDepth, canThrow) {
") || " + rng(10) + ").toString()[" + ") || " + rng(10) + ").toString()[" +
createExpression(recurmax, COMMA_OK, stmtDepth, canThrow) + "] "; createExpression(recurmax, COMMA_OK, stmtDepth, canThrow) + "] ";
case p++: case p++:
return createArrayLiteral(recurmax, COMMA_OK, stmtDepth, canThrow); return createArrayLiteral(recurmax, stmtDepth, canThrow);
case p++: case p++:
return createObjectLiteral(recurmax, COMMA_OK, stmtDepth, canThrow); return createObjectLiteral(recurmax, stmtDepth, canThrow);
case p++: case p++:
return createArrayLiteral(recurmax, COMMA_OK, stmtDepth, canThrow) + '[' + return createArrayLiteral(recurmax, stmtDepth, canThrow) + '[' +
createExpression(recurmax, COMMA_OK, stmtDepth, canThrow) + ']'; createExpression(recurmax, COMMA_OK, stmtDepth, canThrow) + ']';
case p++: case p++:
return createObjectLiteral(recurmax, COMMA_OK, stmtDepth, canThrow) + '[' + return createObjectLiteral(recurmax, stmtDepth, canThrow) + '[' +
createExpression(recurmax, COMMA_OK, stmtDepth, canThrow) + ']'; createExpression(recurmax, COMMA_OK, stmtDepth, canThrow) + ']';
case p++: case p++:
return createArrayLiteral(recurmax, COMMA_OK, stmtDepth, canThrow) + '.' + getDotKey(); return createArrayLiteral(recurmax, stmtDepth, canThrow) + '.' + getDotKey();
case p++: case p++:
return createObjectLiteral(recurmax, COMMA_OK, stmtDepth, canThrow) + '.' + getDotKey(); return createObjectLiteral(recurmax, stmtDepth, canThrow) + '.' + getDotKey();
case p++: case p++:
var name = getVarName(); var name = getVarName();
return name + ' && ' + name + '[' + createExpression(recurmax, COMMA_OK, stmtDepth, canThrow) + ']'; return name + ' && ' + name + '[' + createExpression(recurmax, COMMA_OK, stmtDepth, canThrow) + ']';
@@ -713,7 +754,7 @@ function _createExpression(recurmax, noComma, stmtDepth, canThrow) {
return _createExpression(recurmax, noComma, stmtDepth, canThrow); return _createExpression(recurmax, noComma, stmtDepth, canThrow);
} }
function createArrayLiteral(recurmax, noComma, stmtDepth, canThrow) { function createArrayLiteral(recurmax, stmtDepth, canThrow) {
recurmax--; recurmax--;
var arr = "["; var arr = "[";
for (var i = rng(6); --i >= 0;) { for (var i = rng(6); --i >= 0;) {
@@ -746,18 +787,56 @@ var KEYS = [
"3", "3",
].concat(SAFE_KEYS); ].concat(SAFE_KEYS);
function getDotKey() { function getDotKey(assign) {
return SAFE_KEYS[rng(SAFE_KEYS.length)]; var key;
do {
key = SAFE_KEYS[rng(SAFE_KEYS.length)];
} while (assign && key == "length");
return key;
} }
function createObjectLiteral(recurmax, noComma, stmtDepth, canThrow) { function createAccessor(recurmax, stmtDepth, canThrow) {
recurmax--; var namesLenBefore = VAR_NAMES.length;
var obj = "({"; var s;
for (var i = rng(6); --i >= 0;) { var prop1 = getDotKey();
var key = KEYS[rng(KEYS.length)]; if (rng(2) == 0) {
obj += key + ":(" + createExpression(recurmax, COMMA_OK, stmtDepth, canThrow) + "), "; s = [
'get ' + prop1 + '(){',
strictMode(),
createStatements(2, recurmax, canThrow, CANNOT_BREAK, CANNOT_CONTINUE, CAN_RETURN, stmtDepth),
createStatement(recurmax, canThrow, CANNOT_BREAK, CANNOT_CONTINUE, CAN_RETURN, stmtDepth, STMT_RETURN_ETC),
'},'
];
} else {
var prop2;
do {
prop2 = getDotKey();
} while (prop1 == prop2);
s = [
'set ' + prop1 + '(' + createVarName(MANDATORY) + '){',
strictMode(),
createStatements(2, recurmax, canThrow, CANNOT_BREAK, CANNOT_CONTINUE, CAN_RETURN, stmtDepth),
'this.' + prop2 + createAssignment() + _createBinaryExpr(recurmax, COMMA_OK, stmtDepth, canThrow) + ';',
'},'
];
} }
return obj + "})"; VAR_NAMES.length = namesLenBefore;
return filterDirective(s).join('\n');
}
function createObjectLiteral(recurmax, stmtDepth, canThrow) {
recurmax--;
var obj = ['({'];
for (var i = rng(6); --i >= 0;) {
if (rng(20) == 0) {
obj.push(createAccessor(recurmax, stmtDepth, canThrow));
} else {
var key = KEYS[rng(KEYS.length)];
obj.push(key + ':(' + createExpression(recurmax, COMMA_OK, stmtDepth, canThrow) + '),');
}
}
obj.push('})');
return obj.join('\n');
} }
function createNestedBinaryExpr(recurmax, noComma, stmtDepth, canThrow) { function createNestedBinaryExpr(recurmax, noComma, stmtDepth, canThrow) {
@@ -787,7 +866,7 @@ function _createSimpleBinaryExpr(recurmax, noComma, stmtDepth, canThrow) {
return canThrow && rng(10) == 0 ? expr : '(' + assignee + ' && ' + expr + ')'; return canThrow && rng(10) == 0 ? expr : '(' + assignee + ' && ' + expr + ')';
case 4: case 4:
assignee = getVarName(); assignee = getVarName();
expr = '(' + assignee + '.' + getDotKey() + createAssignment() expr = '(' + assignee + '.' + getDotKey(true) + createAssignment()
+ _createBinaryExpr(recurmax, noComma, stmtDepth, canThrow) + ')'; + _createBinaryExpr(recurmax, noComma, stmtDepth, canThrow) + ')';
return canThrow && rng(10) == 0 ? expr : '(' + assignee + ' && ' + expr + ')'; return canThrow && rng(10) == 0 ? expr : '(' + assignee + ' && ' + expr + ')';
default: default:
@@ -844,17 +923,24 @@ function getVarName() {
function createVarName(maybe, dontStore) { function createVarName(maybe, dontStore) {
if (!maybe || rng(2)) { if (!maybe || rng(2)) {
var name = VAR_NAMES[rng(VAR_NAMES.length)];
var suffix = rng(3); var suffix = rng(3);
if (suffix) { var name;
name += '_' + suffix; do {
if (!dontStore) VAR_NAMES.push(name); name = VAR_NAMES[rng(VAR_NAMES.length)];
} if (suffix) name += '_' + suffix;
} while (unique_vars.indexOf(name) >= 0);
if (suffix && !dontStore) VAR_NAMES.push(name);
return name; return name;
} }
return ''; return '';
} }
if (require.main !== module) {
exports.createTopLevelCode = createTopLevelCode;
exports.num_iterations = num_iterations;
return;
}
function try_beautify(code, result) { function try_beautify(code, result) {
var beautified = UglifyJS.minify(code, { var beautified = UglifyJS.minify(code, {
compress: false, compress: false,
@@ -876,32 +962,15 @@ function try_beautify(code, result) {
console.log(code); console.log(code);
} }
function infer_options(ctor) { var default_options = UglifyJS.default_options();
try {
ctor({ 0: 0 });
} catch (e) {
return e.defs;
}
}
var default_options = {
compress: infer_options(UglifyJS.Compressor),
mangle: {
"cache": null,
"eval": false,
"ie8": false,
"keep_fnames": false,
"toplevel": false,
},
output: infer_options(UglifyJS.OutputStream),
};
function log_suspects(minify_options, component) { function log_suspects(minify_options, component) {
var options = component in minify_options ? minify_options[component] : true; var options = component in minify_options ? minify_options[component] : true;
if (!options) return; if (!options) return;
options = UglifyJS.defaults(options, default_options[component]); if (typeof options != "object") options = {};
var suspects = Object.keys(default_options[component]).filter(function(name) { var defs = default_options[component];
if (options[name]) { var suspects = Object.keys(defs).filter(function(name) {
if ((name in options ? options : defs)[name]) {
var m = JSON.parse(JSON.stringify(minify_options)); var m = JSON.parse(JSON.stringify(minify_options));
var o = JSON.parse(JSON.stringify(options)); var o = JSON.parse(JSON.stringify(options));
o[name] = false; o[name] = false;
@@ -973,10 +1042,6 @@ var uglify_code, uglify_result, ok;
for (var round = 1; round <= num_iterations; round++) { for (var round = 1; round <= num_iterations; round++) {
process.stdout.write(round + " of " + num_iterations + "\r"); process.stdout.write(round + " of " + num_iterations + "\r");
VAR_NAMES.length = INITIAL_NAMES_LEN; // prune any previous names still in the list
loops = 0;
funcs = 0;
original_code = createTopLevelCode(); original_code = createTopLevelCode();
original_result = sandbox.run_code(original_code); original_result = sandbox.run_code(original_code);
(typeof original_result != "string" ? fallback_options : minify_options).forEach(function(options) { (typeof original_result != "string" ? fallback_options : minify_options).forEach(function(options) {
@@ -992,7 +1057,7 @@ for (var round = 1; round <= num_iterations; round++) {
} }
} }
if (verbose || (verbose_interval && !(round % INTERVAL_COUNT)) || !ok) log(options); if (verbose || (verbose_interval && !(round % INTERVAL_COUNT)) || !ok) log(options);
else if (verbose_error && typeof original_result != "string") { else if (typeof original_result != "string") {
console.log("//============================================================="); console.log("//=============================================================");
console.log("// original code"); console.log("// original code");
try_beautify(original_code, original_result); try_beautify(original_code, original_result);

View File

@@ -1,4 +1,5 @@
exports["Dictionary"] = Dictionary; exports["Dictionary"] = Dictionary;
exports["TreeWalker"] = TreeWalker; exports["TreeWalker"] = TreeWalker;
exports["TreeTransformer"] = TreeTransformer;
exports["minify"] = minify; exports["minify"] = minify;
exports["_push_uniq"] = push_uniq; exports["_push_uniq"] = push_uniq;

View File

@@ -61,5 +61,22 @@ function describe_ast() {
} }
}; };
doitem(AST_Node); doitem(AST_Node);
return out + ""; return out + "\n";
} }
function infer_options(options) {
var result = UglifyJS.minify("", options);
return result.error && result.error.defs;
}
UglifyJS.default_options = function() {
var defs = {};
Object.keys(infer_options({ 0: 0 })).forEach(function(component) {
var options = {};
options[component] = { 0: 0 };
if (options = infer_options(options)) {
defs[component] = options;
}
});
return defs;
};