Compare commits

...

91 Commits

Author SHA1 Message Date
Alex Lam S.L
5b22334f3b Merge pull request #1982 from alexlamsl/harmony-v3.0.10
Merging from master for 3.0.10
2017-05-21 03:23:59 +08:00
alexlamsl
a3053c537a Merge branch 'master' into harmony-v3.0.10 2017-05-21 01:36:38 +08:00
Alex Lam S.L
d3c4a8e9e7 v3.0.10 2017-05-21 01:30:17 +08:00
kzc
d6f77a6352 update keywords in package.json (#1981) 2017-05-20 22:10:51 +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
Alex Lam S.L
a2172e1a99 fix parsing of yield as object key (#1976)
fixes #1974
2017-05-20 13:11:37 +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
9a074c2637 Merge pull request #1972 from alexlamsl/harmony-v3.0.9
Merging from master for 3.0.9
2017-05-19 10:38:45 +08:00
alexlamsl
02b14528fa Merge branch 'master' into harmony-v3.0.9 2017-05-19 09:51:00 +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
3db2001633 suppress unused on block variables (#1969)
fixes #1968
2017-05-19 00:28:19 +08:00
Alex Lam S.L
aaba482e48 Merge pull request #1967 from alexlamsl/harmony-v3.0.8
Merging from master for 3.0.8
2017-05-18 16:02:29 +08:00
alexlamsl
5f29fced0a Merge branch 'master' into harmony-v3.0.8 2017-05-18 14:54:18 +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
kzc
b1b918e6d6 better extends paren fix (#1962) 2017-05-18 02:36:29 +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
kzc
ebb469e4cd fix class extends expression (#1956) 2017-05-17 03:29:25 +08:00
kzc
c22d26b483 support export default of anonymous functions and classes (#1954) 2017-05-17 03:28:24 +08:00
Alex Lam S.L
f751e64d49 Merge pull request #1951 from alexlamsl/harmony-v3.0.7
Merging from master for 3.0.7
2017-05-17 01:03:55 +08:00
alexlamsl
60c56a24b9 Merge branch 'master' into harmony-v3.0.7 2017-05-16 20:02:30 +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
01f23cf5a1 Merge pull request #1948 from alexlamsl/harmony-v3.0.6
Merging from master for 3.0.6
2017-05-16 13:26:45 +08:00
alexlamsl
99fb3e8f0d Merge branch 'master' into harmony-v3.0.6 2017-05-16 06:48:23 +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
756c9aa7dc keep minify() options in sync (#1940) 2017-05-15 20:29:48 +08:00
Alex Lam S.L
07d6bfd707 Merge pull request #1939 from alexlamsl/harmony-v3.0.5
Merging from master for 3.0.5
2017-05-15 19:48:00 +08:00
alexlamsl
81243c4e71 Merge branch 'master' into harmony-v3.0.5 2017-05-15 18:58:54 +08:00
alexlamsl
cd6e849555 Revert "remove support for const (#1910)"
This reverts commit c391576d52.
2017-05-15 18:38:16 +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
kzc
91de285166 uglify-es: update homepage in package.json (#1933)
to point to harmony branch on github
2017-05-14 00:25:06 +08:00
kzc
4d8f289eb0 fix export default expression; (#1932) 2017-05-13 12:56:46 +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
Alex Lam S.L
945ba64160 Merge pull request #1923 from alexlamsl/harmony-v3.0.4
Merging from master for 3.0.4
2017-05-12 06:52:21 +08:00
Anthony Van de Gejuchte
c699200398 Make sure globals can be accessed from the browser (#1920)
Note: no tests as there are no integration tests
2017-05-12 05:50:35 +08:00
alexlamsl
daf44f2b21 Merge branch 'master' into harmony-v3.0.4 2017-05-12 05:13:11 +08:00
Alex Lam S.L
daaefc17b9 v3.0.4 2017-05-12 04:52:39 +08:00
Alex Lam S.L
1d407e761e fix invalid transform on const (#1919)
- preserve (re)assignment to `const` for runtime error
- suppress `cascade` on `const`, as runtime behaviour is ill-defined
2017-05-12 04:51:44 +08:00
kzc
2b44f4ae30 update README (#1918) 2017-05-12 03:36:33 +08:00
Alexis Tyler
e51c3541da fix typo (#1913) 2017-05-11 20:24:33 +08:00
Alex Lam S.L
3bf194684b update documentation (#1909)
- clarify options on `--source-map`
- fix `minify()` examples

fixes #1905
2017-05-11 17:50:50 +08:00
Gyusun Yeom
fcd90db30d fix safari syntax error - declare twice (#1851)
To avoid Safari bug, scope of for loop should enclose parent scope variables.


fixes #1753
2017-05-11 16:48:43 +08:00
Alex Lam S.L
e2888bdc43 Merge pull request #1901 from alexlamsl/harmony-v3.0.3
Merging from master for 3.0.3
2017-05-10 14:26:58 +08:00
alexlamsl
fb50b7b627 Merge branch 'master' into harmony-v3.0.3 2017-05-10 11:52:59 +08:00
Alex Lam S.L
aae7d49d0c v3.0.3 2017-05-10 11:45:03 +08:00
kzc
9d59c693c2 fix for-of loop with const iterator (#1899) 2017-05-10 11:36:03 +08:00
kzc
0459af2ecc Update issue template: change harmony to uglify-es (#1900) 2017-05-10 11:07:54 +08:00
kzc
04f2344efc Remove unnecessary git clone instructions in README (#1897) 2017-05-10 11:06:50 +08:00
kzc
6ddb5bd94d Remove incorrect git clone instructions from uglify-es README (#1896) 2017-05-10 11:06:22 +08:00
kzc
bad9d5cf88 Change harmony to uglify-es in master README (#1895) 2017-05-10 05:07:45 +08:00
kzc
eda49605c5 Have harmony docs use uglify-es package name. (#1894) 2017-05-10 04:41:09 +08:00
Alex Lam S.L
a0f5f862df gracefully handle non-Error being thrown (#1893) 2017-05-10 04:20:59 +08:00
Alex Lam S.L
1e9ef17e32 Merge pull request #1892 from alexlamsl/harmony-v3.0.2
Merging from master for 3.0.2
2017-05-10 03:14:45 +08:00
Alex Lam S.L
41996be86f extend test timeout
Travis has gone a lot slower recently, and most test failures are due to time-out on this particular test.
2017-05-10 02:43:12 +08:00
alexlamsl
222100ea4c Merge branch 'master' into harmony-v3.0.2 2017-05-10 01:57:32 +08:00
Alex Lam S.L
5fd8244a2e v3.0.2 2017-05-10 01:52:00 +08:00
Alex Lam S.L
93db48a317 rename package 2017-05-10 01:46:55 +08:00
Alex Lam S.L
2944e3df7d fix collapse_vars on destructuring declarations (#1889)
fixes #1886
2017-05-09 17:44:28 +08:00
Alex Lam S.L
c14e280585 print error stack in CLI (#1890) 2017-05-09 16:36:44 +08:00
Alex Lam S.L
bc3fa78e8c mention minify().error 2017-05-09 16:09:48 +08:00
Alex Lam S.L
8c7c107765 update minify() usage in test/ufuzz.js (#1888)
fixes #1887
2017-05-09 15:58:46 +08:00
Alex Lam S.L
e0ae8da089 Merge pull request #1885 from alexlamsl/harmony-v3.0.1
Merging from master for 3.0.1
2017-05-09 02:49:28 +08:00
alexlamsl
81f1311b24 Merge branch 'master' into harmony-v3.0.1 2017-05-09 02:10:06 +08:00
Alex Lam S.L
2433bb4e52 fix Unicode handling in parser (#1884)
There was an implicit assumption that first character within surrogate header range implies the next character must form a surrogate pair, which is not necessarily true.
2017-05-09 01:58:31 +08:00
kzc
3dd328dce3 [3.x] fix documentation for beautify options (#1882)
- use underscores rather than dashes.
2017-05-08 23:06:56 +08:00
Alex Lam S.L
014f428153 v3.0.1 2017-05-08 07:05:57 +08:00
Alex Lam S.L
a3b2eb75bd return Error from minify() (#1880)
Have `minify()` return `Error` in `result.error` rather than throwing it.
2017-05-08 07:05:19 +08:00
Alex Lam S.L
da295de82b support dumping AST (#1879)
Re-order `AST_Binary` properties to make dump more readable.

closes #769
2017-05-08 06:23:01 +08:00
Alex Lam S.L
4f8ca4626e deprecate low level API (#1877)
fixes #1872
2017-05-08 03:24:42 +08:00
Alex Lam S.L
e54748365c support minify() output as AST (#1878)
- `options.output.ast` (default `false`)
- `options.output.code` (default `true`)
2017-05-08 02:11:45 +08:00
67 changed files with 2055 additions and 1102 deletions

View File

@@ -6,15 +6,15 @@
<!-- Note: for ES6 see: https://github.com/mishoo/UglifyJS2/tree/harmony#harmony -->
**`uglify-js` version (`uglifyjs -V`)**
**Uglify version (`uglifyjs -V`)**
**JavaScript input - ideally as small as possible.**
**JavaScript input** <!-- ideally as small as possible -->
**The `uglifyjs` CLI command executed or `minify()` options used.**
**JavaScript output produced and/or the error or warning.**
**JavaScript output or error produced.**
<!--
Note: the release version of uglify-js only supports ES5. Those wishing
to minify ES6 should use the experimental harmony branch.
Note: `uglify-js` only supports ES5.
Those wishing to minify ES6 should use `uglify-es`.
-->

View File

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

915
README.md

File diff suppressed because it is too large Load Diff

View File

@@ -15,13 +15,13 @@ var path = require("path");
var program = require("commander");
var UglifyJS = require("../tools/node");
var skip_keys = [ "cname", "enclosed", "parent_scope", "scope", "thedef", "uses_eval", "uses_with" ];
var files = {};
var options = {
compress: false,
mangle: false
};
program._name = info.name;
program.version(info.version);
program.version(info.name + ' ' + info.version);
program.parseArgv = program.parse;
program.parse = undefined;
program.option("-p, --parse <options>", "Specify parser options.", parse_js("parse", true));
@@ -89,7 +89,7 @@ if (program.mangleProps) {
if (typeof program.mangleProps != "object") program.mangleProps = {};
if (!Array.isArray(program.mangleProps.reserved)) program.mangleProps.reserved = [];
require("../tools/domprops").forEach(function(name) {
UglifyJS.push_uniq(program.mangleProps.reserved, name);
UglifyJS._push_uniq(program.mangleProps.reserved, name);
});
}
if (typeof options.mangle != "object") options.mangle = {};
@@ -107,6 +107,12 @@ if (program.nameCache) {
}
}
}
if (program.output == "ast") {
options.output = {
ast: true,
code: false
};
}
if (program.parse) {
if (program.parse.acorn || program.parse.spidermonkey) {
if (program.sourceMap) fatal("ERROR: inline source map only works with built-in parser");
@@ -185,9 +191,13 @@ function run() {
});
}
}
var result = UglifyJS.minify(files, options);
} catch (ex) {
if (ex instanceof UglifyJS.JS_Parse_Error) {
fatal(ex);
}
var result = UglifyJS.minify(files, options);
if (result.error) {
var ex = result.error;
if (ex.name == "SyntaxError") {
console.error("Parse error at " + ex.filename + ":" + ex.line + "," + ex.col);
var col = ex.col;
var lines = files[ex.filename].split(/\r?\n/);
@@ -209,10 +219,32 @@ function run() {
console.error("Supported options:");
console.error(ex.defs);
}
fatal("ERROR: " + ex.message);
fatal(ex);
} else if (program.output == "ast") {
console.log(JSON.stringify(result.ast, function(key, value) {
if (skip_key(key)) return;
if (value instanceof UglifyJS.AST_Token) return;
if (value instanceof UglifyJS.Dictionary) return;
if (value instanceof UglifyJS.AST_Node) {
var result = {
_class: "AST_" + value.TYPE
};
value.CTOR.PROPS.forEach(function(prop) {
result[prop] = value[prop];
});
return result;
}
if (program.output == "spidermonkey") {
console.log(JSON.stringify(UglifyJS.parse(new Buffer(result.code).toString()).to_mozilla_ast(), null, 2));
return value;
}, 2));
} else if (program.output == "spidermonkey") {
console.log(JSON.stringify(UglifyJS.minify(result.code, {
compress: false,
mangle: false,
output: {
ast: true,
code: false
}
}).ast.to_mozilla_ast(), null, 2));
} else if (program.output) {
fs.writeFileSync(program.output, result.code);
if (result.map) {
@@ -230,6 +262,7 @@ function run() {
}
function fatal(message) {
if (message instanceof Error) message = message.stack.replace(/^\S*?Error:/, "ERROR:")
console.error(message);
process.exit(1);
}
@@ -270,7 +303,7 @@ function read_file(path, default_value) {
return fs.readFileSync(path, "utf8");
} catch (ex) {
if (ex.code == "ENOENT" && default_value != null) return default_value;
fatal("ERROR: " + ex.message);
fatal(ex);
}
}
@@ -278,9 +311,17 @@ function parse_js(flag, constants) {
return function(value, options) {
options = options || {};
try {
UglifyJS.parse(value, {
UglifyJS.minify(value, {
parse: {
expression: true
}).walk(new UglifyJS.TreeWalker(function(node) {
},
compress: false,
mangle: false,
output: {
ast: true,
code: false
}
}).ast.walk(new UglifyJS.TreeWalker(function(node) {
if (node instanceof UglifyJS.AST_Assign) {
var name = node.left.print_to_string();
var value = node.right;
@@ -337,3 +378,7 @@ function to_cache(key) {
}
return cache[key];
}
function skip_key(key) {
return skip_keys.indexOf(key) >= 0;
}

View File

@@ -857,7 +857,7 @@ var AST_UnaryPostfix = DEFNODE("UnaryPostfix", null, {
$documentation: "Unary postfix expression, i.e. `i++`"
}, AST_Unary);
var AST_Binary = DEFNODE("Binary", "left operator right", {
var AST_Binary = DEFNODE("Binary", "operator left right", {
$documentation: "Binary expression, i.e. `a + b`",
$propdoc: {
left: "[AST_Node] left-hand side expression",
@@ -932,7 +932,7 @@ var AST_ObjectProperty = DEFNODE("ObjectProperty", "key value", {
$documentation: "Base class for literal object properties",
$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",
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) {
return visitor._visit(this, function(){
@@ -1018,10 +1018,6 @@ var AST_NewTarget = DEFNODE("NewTarget", null, {
$documentation: "A reference to new.target"
});
var AST_SymbolAccessor = DEFNODE("SymbolAccessor", null, {
$documentation: "The name of a property accessor (setter/getter function)"
}, AST_Symbol);
var AST_SymbolDeclaration = DEFNODE("SymbolDeclaration", "init", {
$documentation: "A declaration symbol (symbol in var/const, function name or argument, symbol in catch)",
}, AST_Symbol);

View File

@@ -84,6 +84,7 @@ function Compressor(options, false_by_default) {
unsafe_comps : false,
unsafe_math : false,
unsafe_proto : false,
unsafe_regexp : false,
unused : !false_by_default,
warnings : false,
}, true);
@@ -361,16 +362,27 @@ merge(Compressor.prototype, {
// So existing transformation rules can work on them.
node.argnames.forEach(function(arg, i) {
var d = arg.definition();
if (!node.uses_arguments && d.fixed === undefined) {
d.fixed = function() {
return iife.args[i] || make_node(AST_Undefined, iife);
};
mark(d, true);
} else {
d.fixed = false;
}
});
}
descend();
pop();
return true;
}
if (node instanceof AST_Accessor) {
var save_ids = safe_ids;
safe_ids = Object.create(null);
descend();
safe_ids = save_ids;
return true;
}
if (node instanceof AST_Binary
&& (node.operator == "&&" || node.operator == "||")) {
node.left.walk(tw);
@@ -498,7 +510,9 @@ merge(Compressor.prototype, {
function reset_def(def) {
def.escaped = false;
if (!def.global || def.orig[0] instanceof AST_SymbolConst || compressor.toplevel(def)) {
if (def.scope.uses_eval) {
def.fixed = false;
} else if (!def.global || def.orig[0] instanceof AST_SymbolConst || compressor.toplevel(def)) {
def.fixed = undefined;
} else {
def.fixed = false;
@@ -532,6 +546,14 @@ merge(Compressor.prototype, {
return lhs instanceof AST_SymbolRef && lhs.definition().orig[0] instanceof AST_SymbolLambda;
}
function is_ref_of(ref, type) {
if (!(ref instanceof AST_SymbolRef)) return false;
var orig = ref.definition().orig;
for (var i = orig.length; --i >= 0;) {
if (orig[i] instanceof type) return true;
}
}
function find_variable(compressor, name) {
var scope, i = 0;
while (scope = compressor.parent(i++)) {
@@ -799,14 +821,15 @@ merge(Compressor.prototype, {
}
function get_lhs(expr) {
if (expr instanceof AST_VarDef) {
if (expr instanceof AST_VarDef && expr.name instanceof AST_SymbolDeclaration) {
var def = expr.name.definition();
if (def.orig.length > 1
|| def.references.length == 1 && (!def.global || compressor.toplevel(def))) {
return make_node(AST_SymbolRef, expr.name, expr.name);
}
} else {
return expr[expr instanceof AST_Assign ? "left" : "expression"];
var lhs = expr[expr instanceof AST_Assign ? "left" : "expression"];
return !is_ref_of(lhs, AST_SymbolConst) && lhs;
}
}
@@ -917,12 +940,12 @@ merge(Compressor.prototype, {
continue loop;
case stat instanceof AST_If:
if (stat.body instanceof AST_Return) {
var value = stat.body.value;
//---
// pretty silly case, but:
// if (foo()) return; return; ==> foo(); return;
if (((in_lambda && ret.length == 0)
|| (ret[0] instanceof AST_Return && !ret[0].value))
&& !stat.body.value && !stat.alternative) {
if ((in_lambda && ret.length == 0 || ret[0] instanceof AST_Return && !ret[0].value)
&& !value && !stat.alternative) {
CHANGED = true;
var cond = make_node(AST_SimpleStatement, stat.condition, {
body: stat.condition
@@ -932,7 +955,7 @@ merge(Compressor.prototype, {
}
//---
// if (foo()) return x; return y; ==> return foo() ? x : y;
if (ret[0] instanceof AST_Return && stat.body.value && ret[0].value && !stat.alternative) {
if (ret[0] instanceof AST_Return && value && ret[0].value && !stat.alternative) {
CHANGED = true;
stat = stat.clone();
stat.alternative = ret[0];
@@ -942,7 +965,7 @@ merge(Compressor.prototype, {
//---
// if (foo()) return x; [ return ; ] ==> return foo() ? x : undefined;
if (multiple_if_returns && (ret.length == 0 || ret[0] instanceof AST_Return)
&& stat.body.value && !stat.alternative && in_lambda) {
&& value && !stat.alternative && in_lambda) {
CHANGED = true;
stat = stat.clone();
stat.alternative = ret[0] || make_node(AST_Return, stat, {
@@ -952,8 +975,8 @@ merge(Compressor.prototype, {
continue loop;
}
//---
// if (foo()) return; [ else x... ]; y... ==> if (!foo()) { x...; y... }
if (!stat.body.value && in_lambda) {
// if (foo()) return [ void bar() ]; [ else x...; ] y... ==> if (!foo()) { x...; y... } else bar();
if (in_lambda && (!value || value instanceof AST_UnaryPrefix && value.operator == "void")) {
CHANGED = true;
stat = stat.clone();
stat.condition = stat.condition.negate(compressor);
@@ -962,11 +985,12 @@ merge(Compressor.prototype, {
stat.body = make_node(AST_BlockStatement, stat, {
body: body
});
stat.alternative = null;
stat.alternative = value ? make_node(AST_SimpleStatement, value, {
body: value.expression
}) : null;
ret = funs.concat([ stat.transform(compressor) ]);
continue loop;
}
//---
// if (a) return b; if (c) return d; e; ==> return a ? b : c ? d : void e;
//
@@ -1231,12 +1255,12 @@ merge(Compressor.prototype, {
&& !node.expression.has_side_effects(compressor);
}
// may_eq_null()
// returns true if this node may evaluate to null or undefined
// may_throw_on_access()
// returns true if this node may be null, undefined or contain `AST_Accessor`
(function(def) {
AST_Node.DEFMETHOD("may_eq_null", function(compressor) {
AST_Node.DEFMETHOD("may_throw_on_access", function(compressor) {
var pure_getters = compressor.option("pure_getters");
return !pure_getters || this._eq_null(pure_getters);
return !pure_getters || this._throw_on_access(pure_getters);
});
function is_strict(pure_getters) {
@@ -1248,7 +1272,12 @@ merge(Compressor.prototype, {
def(AST_Undefined, return_true);
def(AST_Constant, return_false);
def(AST_Array, return_false);
def(AST_Object, return_false);
def(AST_Object, function(pure_getters) {
if (!is_strict(pure_getters)) return false;
for (var i = this.properties.length; --i >=0;)
if (this.properties[i].value instanceof AST_Accessor) return true;
return false;
});
def(AST_Function, return_false);
def(AST_UnaryPostfix, return_false);
def(AST_UnaryPrefix, function() {
@@ -1257,33 +1286,33 @@ merge(Compressor.prototype, {
def(AST_Binary, function(pure_getters) {
switch (this.operator) {
case "&&":
return this.left._eq_null(pure_getters);
return this.left._throw_on_access(pure_getters);
case "||":
return this.left._eq_null(pure_getters)
&& this.right._eq_null(pure_getters);
return this.left._throw_on_access(pure_getters)
&& this.right._throw_on_access(pure_getters);
default:
return false;
}
})
def(AST_Assign, function(pure_getters) {
return this.operator == "="
&& this.right._eq_null(pure_getters);
&& this.right._throw_on_access(pure_getters);
})
def(AST_Conditional, function(pure_getters) {
return this.consequent._eq_null(pure_getters)
|| this.alternative._eq_null(pure_getters);
return this.consequent._throw_on_access(pure_getters)
|| this.alternative._throw_on_access(pure_getters);
})
def(AST_Sequence, function(pure_getters) {
return this.expressions[this.expressions.length - 1]._eq_null(pure_getters);
return this.expressions[this.expressions.length - 1]._throw_on_access(pure_getters);
});
def(AST_SymbolRef, function(pure_getters) {
if (this.is_undefined) return true;
if (!is_strict(pure_getters)) return false;
var fixed = this.fixed_value();
return !fixed || fixed._eq_null(pure_getters);
return !fixed || fixed._throw_on_access(pure_getters);
});
})(function(node, func) {
node.DEFMETHOD("_eq_null", func);
node.DEFMETHOD("_throw_on_access", func);
});
/* -----[ boolean/negation helpers ]----- */
@@ -1844,11 +1873,11 @@ merge(Compressor.prototype, {
return any(this.elements, compressor);
});
def(AST_Dot, function(compressor){
return this.expression.may_eq_null(compressor)
return this.expression.may_throw_on_access(compressor)
|| this.expression.has_side_effects(compressor);
});
def(AST_Sub, function(compressor){
return this.expression.may_eq_null(compressor)
return this.expression.may_throw_on_access(compressor)
|| this.expression.has_side_effects(compressor)
|| this.property.has_side_effects(compressor);
});
@@ -2029,6 +2058,7 @@ merge(Compressor.prototype, {
&& node instanceof AST_Assign
&& node.operator == "="
&& node.left instanceof AST_SymbolRef
&& !is_ref_of(node.left, AST_SymbolBlockDeclaration)
&& scope === self) {
node.right.walk(tw);
return true;
@@ -2482,6 +2512,7 @@ merge(Compressor.prototype, {
var args = trim(this.args, compressor, first_in_statement);
return args && make_sequence(this, args);
});
def(AST_Accessor, return_null);
def(AST_Function, return_null);
def(AST_Binary, function(compressor, first_in_statement){
var right = this.right.drop_side_effect_free(compressor);
@@ -2549,11 +2580,11 @@ merge(Compressor.prototype, {
return values && make_sequence(this, values);
});
def(AST_Dot, function(compressor, first_in_statement){
if (this.expression.may_eq_null(compressor)) return this;
if (this.expression.may_throw_on_access(compressor)) return this;
return this.expression.drop_side_effect_free(compressor, first_in_statement);
});
def(AST_Sub, function(compressor, first_in_statement){
if (this.expression.may_eq_null(compressor)) return this;
if (this.expression.may_throw_on_access(compressor)) return this;
var expression = this.expression.drop_side_effect_free(compressor, first_in_statement);
if (!expression) return this.property.drop_side_effect_free(compressor, first_in_statement);
var property = this.property.drop_side_effect_free(compressor);
@@ -3131,7 +3162,6 @@ merge(Compressor.prototype, {
// Symbol's argument is only used for debugging.
self.args = [];
return self;
break;
}
}
else if (exp instanceof AST_Dot && exp.property == "toString" && self.args.length == 0) {
@@ -3314,7 +3344,7 @@ merge(Compressor.prototype, {
&& (left.operator == "++" || left.operator == "--")) {
left = left.expression;
} else left = null;
if (!left || is_lhs_read_only(left)) {
if (!left || is_lhs_read_only(left) || is_ref_of(left, AST_SymbolConst)) {
expressions[++i] = cdr;
continue;
}
@@ -3883,7 +3913,7 @@ merge(Compressor.prototype, {
if (fixed) {
if (d.should_replace === undefined) {
var init = fixed.evaluate(compressor);
if (init !== fixed) {
if (init !== fixed && (compressor.option("unsafe_regexp") || !(init instanceof RegExp))) {
init = make_node_from_constant(init, fixed);
var value_length = init.optimize(compressor).print_to_string().length;
var fn;

View File

@@ -54,9 +54,11 @@ function minify(files, options) {
cache: null,
eval: false,
ie8: false,
keep_classnames: false,
keep_fnames: false,
properties: false,
reserved: [],
safari10: false,
toplevel: false,
}, true);
}
@@ -108,6 +110,11 @@ function minify(files, options) {
toplevel = mangle_properties(toplevel, options.mangle.properties);
}
}
var result = {};
if (options.output.ast) {
result.ast = toplevel;
}
if (!HOP(options.output, "code") || options.output.code) {
if (options.sourceMap) {
if (typeof options.sourceMap.content == "string") {
options.sourceMap.content = JSON.parse(options.sourceMap.content);
@@ -123,11 +130,11 @@ function minify(files, options) {
}
}
}
delete options.output.ast;
delete options.output.code;
var stream = OutputStream(options.output);
toplevel.print(stream);
var result = {
code: stream.get()
};
result.code = stream.get();
if (options.sourceMap) {
result.map = options.output.source_map.toString();
if (options.sourceMap.url == "inline") {
@@ -136,10 +143,13 @@ function minify(files, options) {
result.code += "\n//# sourceMappingURL=" + options.sourceMap.url;
}
}
}
if (warnings.length) {
result.warnings = warnings;
}
return result;
} catch (ex) {
return { error: ex };
} finally {
AST_Node.warn_function = warn_function;
}

View File

@@ -111,23 +111,19 @@
},
Property: function(M) {
var key = M.key;
var name = key.type == "Identifier" ? key.name : key.value;
var args = {
start : my_start_token(key),
end : my_end_token(M.value),
key : name,
key : key.type == "Identifier" ? key.name : key.value,
value : from_moz(M.value)
};
switch (M.kind) {
case "init":
return new AST_ObjectKeyVal(args);
case "set":
args.value.name = from_moz(key);
return new AST_ObjectSetter(args);
case "get":
args.value.name = from_moz(key);
return new AST_ObjectGetter(args);
}
if (M.kind == "init") return new AST_ObjectKeyVal(args);
args.key = new AST_SymbolMethod({
name: args.key
});
args.value = new AST_Accessor(args.value);
if (M.kind == "get") return new AST_ObjectGetter(args);
if (M.kind == "set") return new AST_ObjectSetter(args);
},
ArrayExpression: function(M) {
return new AST_Array({
@@ -260,10 +256,7 @@
map("CallExpression", AST_Call, "callee>expression, arguments@args");
def_to_moz(AST_Toplevel, function To_Moz_Program(M) {
return {
type: "Program",
body: M.body.map(to_moz)
};
return to_moz_scope("Program", M);
});
def_to_moz(AST_Defun, function To_Moz_FunctionDeclaration(M) {
@@ -271,7 +264,7 @@
type: "FunctionDeclaration",
id: to_moz(M.name),
params: M.argnames.map(to_moz),
body: to_moz_block(M)
body: to_moz_scope("BlockStatement", M)
}
});
@@ -280,7 +273,7 @@
type: "FunctionExpression",
id: to_moz(M.name),
params: M.argnames.map(to_moz),
body: to_moz_block(M)
body: to_moz_scope("BlockStatement", M)
}
});
@@ -386,11 +379,10 @@
});
def_to_moz(AST_ObjectProperty, function To_Moz_Property(M) {
var key = (
is_identifier(M.key)
? {type: "Identifier", name: M.key}
: {type: "Literal", value: M.key}
);
var key = {
type: "Literal",
value: M.key instanceof AST_SymbolMethod ? M.key.name : M.key
};
var kind;
if (M instanceof AST_ObjectKeyVal) {
kind = "init";
@@ -551,8 +543,8 @@
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
);
me_to_moz = new Function("to_moz", "to_moz_block", "return(" + me_to_moz + ")")(
to_moz, to_moz_block
me_to_moz = new Function("to_moz", "to_moz_block", "to_moz_scope", "return(" + me_to_moz + ")")(
to_moz, to_moz_block, to_moz_scope
);
MOZ_TO_ME[moztype] = moz_to_me;
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

@@ -73,8 +73,6 @@ function OutputStream(options) {
shebang : true,
shorthand : undefined,
source_map : null,
space_colon : true,
unescape_regexps : false,
width : 80,
wrap_iife : false,
}, true);
@@ -215,16 +213,6 @@ function OutputStream(options) {
var might_add_newline = 0;
var last = "";
function last_char() {
var char = last.charAt(last.length - 1);
if (is_surrogate_pair_tail(char)) {
return last.charAt(last.length - 2) + char;
}
return char;
};
var ensure_line_len = options.max_line_len ? function() {
if (current_col > options.max_line_len) {
if (might_add_newline) {
@@ -247,7 +235,7 @@ function OutputStream(options) {
function print(str) {
str = String(str);
var ch = get_full_char(str, 0);
var prev = last_char();
var prev = get_full_char(last, last.length - 1);
if (might_need_semicolon) {
might_need_semicolon = false;
@@ -394,7 +382,7 @@ function OutputStream(options) {
function colon() {
print(":");
if (options.space_colon) space();
space();
};
var add_mapping = options.source_map ? function(token, name) {
@@ -654,7 +642,6 @@ function OutputStream(options) {
* ==> 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) {}
;
});
@@ -1484,11 +1471,25 @@ function OutputStream(options) {
output.space();
}
if (self.extends) {
var parens = (
!(self.extends instanceof AST_SymbolRef)
&& !(self.extends instanceof AST_PropAccess)
&& !(self.extends instanceof AST_ClassExpression)
&& !(self.extends instanceof AST_Function)
);
output.print("extends");
if (parens) {
output.print("(");
} else {
output.space();
}
self.extends.print(output);
if (parens) {
output.print(")");
} else {
output.space();
}
}
if (self.properties.length > 0) output.with_block(function(){
self.properties.forEach(function(prop, i){
if (i) {
@@ -1627,45 +1628,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){
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")) {
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);
var p = output.parent();

View File

@@ -134,8 +134,17 @@ var UNICODE = {
function get_full_char(str, pos) {
var char = str.charAt(pos);
if (char >= "\ud800" && char <= "\udbff") {
return char + str.charAt(pos + 1);
if (is_surrogate_pair_head(char)) {
var next = str.charAt(pos + 1);
if (is_surrogate_pair_tail(next)) {
return char + next;
}
}
if (is_surrogate_pair_tail(char)) {
var prev = str.charAt(pos - 1);
if (is_surrogate_pair_head(prev)) {
return prev + char;
}
}
return char;
}
@@ -152,8 +161,8 @@ function get_full_char_length(str) {
var surrogates = 0;
for (var i = 0; i < str.length; i++) {
if (str.charCodeAt(i) >= 0xd800 && str.charCodeAt(i) <= 0xdbff) {
if (str.charCodeAt(i + 1) >= 0xdc00 && str.charCodeAt(i + 1) <= 0xdfff) {
if (is_surrogate_pair_head(str.charCodeAt(i))) {
if (is_surrogate_pair_tail(str.charCodeAt(i + 1))) {
surrogates++;
i++;
}
@@ -291,7 +300,7 @@ function tokenizer($TEXT, filename, html5_comments, shebang) {
ch = "\n";
}
} else {
if (is_surrogate_pair_head(ch)) {
if (ch.length > 1) {
++S.pos;
++S.col;
}
@@ -336,7 +345,11 @@ function tokenizer($TEXT, filename, html5_comments, shebang) {
(type == "keyword" && KEYWORDS_BEFORE_EXPRESSION(value)) ||
(type == "punc" && PUNC_BEFORE_EXPRESSION(value))) ||
(type == "arrow");
prev_was_dot = (type == "punc" && value == ".");
if (type == "punc" && value == ".") {
prev_was_dot = true;
} else if (!is_comment) {
prev_was_dot = false;
}
var ret = {
type : type,
value : value,
@@ -598,29 +611,31 @@ function tokenizer($TEXT, filename, html5_comments, shebang) {
return name;
});
var read_regexp = with_eof_error("Unterminated regular expression", function(regexp){
var read_regexp = with_eof_error("Unterminated regular expression", function(source) {
var prev_backslash = false, ch, in_class = false;
while ((ch = next(true))) if (NEWLINE_CHARS(ch)) {
parse_error("Unexpected line terminator");
} else if (prev_backslash) {
regexp += "\\" + ch;
source += "\\" + ch;
prev_backslash = false;
} else if (ch == "[") {
in_class = true;
regexp += ch;
source += ch;
} else if (ch == "]" && in_class) {
in_class = false;
regexp += ch;
source += ch;
} else if (ch == "/" && !in_class) {
break;
} else if (ch == "\\") {
prev_backslash = true;
} else {
regexp += ch;
source += ch;
}
var mods = read_name();
try {
return token("regexp", new RegExp(regexp, mods));
var regexp = new RegExp(source, mods);
regexp.raw_source = source;
return token("regexp", regexp);
} catch(e) {
parse_error(e.message);
}
@@ -963,28 +978,23 @@ function parse($TEXT, options) {
};
var statement = embed_tokens(function() {
var tmp;
handle_regexp();
switch (S.token.type) {
case "string":
var dir = false;
if (S.in_directives === true) {
if ((is_token(peek(), "punc", ";") || peek().nlb) && S.token.raw.indexOf("\\") === -1) {
if (S.in_directives) {
var token = peek();
if (S.token.raw.indexOf("\\") == -1
&& (token.nlb
|| is_token(token, "eof")
|| is_token(token, "punc", ";")
|| is_token(token, "punc", "}"))) {
S.input.add_directive(S.token.value);
} else {
S.in_directives = false;
}
}
var dir = S.in_directives, stat = simple_statement();
if (dir) {
return new AST_Directive({
start : stat.body.start,
end : stat.body.end,
quote : stat.body.quote,
value : stat.body.value,
});
}
return stat;
return dir ? new AST_Directive(stat.body) : stat;
case "template_head":
case "num":
case "regexp":
@@ -1017,90 +1027,126 @@ function parse($TEXT, options) {
}
case "keyword":
switch (tmp = S.token.value, next(), tmp) {
switch (S.token.value) {
case "break":
next();
return break_cont(AST_Break);
case "continue":
next();
return break_cont(AST_Continue);
case "debugger":
next();
semicolon();
return new AST_Debugger();
case "do":
next();
var body = in_loop(statement);
expect_token("keyword", "while");
var condition = parenthesised();
semicolon(true);
return new AST_Do({
body : in_loop(statement),
condition : (expect_token("keyword", "while"), tmp = parenthesised(), semicolon(true), tmp)
body : body,
condition : condition
});
case "while":
next();
return new AST_While({
condition : parenthesised(),
body : in_loop(statement)
});
case "for":
next();
return for_();
case "class":
next();
return class_(AST_DefClass);
case "function":
next();
return function_(AST_Defun);
case "if":
next();
return if_();
case "return":
if (S.in_function == 0 && !options.bare_returns)
croak("'return' outside of function");
next();
var value = null;
if (is("punc", ";")) {
next();
} else if (!can_insert_semicolon()) {
value = expression(true);
semicolon();
}
return new AST_Return({
value: ( is("punc", ";")
? (next(), null)
: can_insert_semicolon()
? null
: (tmp = expression(true), semicolon(), tmp) )
value: value
});
case "switch":
next();
return new AST_Switch({
expression : parenthesised(),
body : in_loop(switch_body_)
});
case "throw":
next();
if (S.token.nlb)
croak("Illegal newline after 'throw'");
var value = expression(true);
semicolon();
return new AST_Throw({
value: (tmp = expression(true), semicolon(), tmp)
value: value
});
case "try":
next();
return try_();
case "var":
return tmp = var_(), semicolon(), tmp;
next();
var node = var_();
semicolon();
return node;
case "let":
return tmp = let_(), semicolon(), tmp;
next();
var node = let_();
semicolon();
return node;
case "const":
return tmp = const_(), semicolon(), tmp;
next();
var node = const_();
semicolon();
return node;
case "with":
if (S.input.has_directive("use strict")) {
croak("Strict mode may not include a with statement");
}
next();
return new AST_With({
expression : parenthesised(),
body : statement()
});
case "import":
return tmp = import_(), semicolon(), tmp;
next();
var node = import_();
semicolon();
return node;
case "export":
next();
return export_();
}
}
@@ -1776,7 +1822,7 @@ function parse($TEXT, options) {
name : as_symbol(sym_type),
value : is("operator", "=")
? (next(), expression(false, no_in))
: kind === "const" && S.input.has_directive("use strict")
: !no_in && kind === "const" && S.input.has_directive("use strict")
? croak("Missing initializer in const declaration") : null,
end : prev()
})
@@ -1805,10 +1851,10 @@ function parse($TEXT, options) {
});
};
var const_ = function() {
var const_ = function(no_in) {
return new AST_Const({
start : prev(),
definitions : vardefs(false, "const"),
definitions : vardefs(no_in, "const"),
end : prev()
});
};
@@ -2293,8 +2339,7 @@ function parse($TEXT, options) {
if (is("keyword", "default")) {
is_default = true;
next();
}
} else {
exported_names = import_names(false);
if (exported_names) {
@@ -2328,11 +2373,9 @@ function parse($TEXT, options) {
});
}
}
}
var is_definition =
is("keyword", "var") || is("keyword", "let") || is("keyword", "const") ||
is("keyword", "class") || is("keyword", "function");
var is_definition = is("keyword", "var") || is("keyword", "let") || is("keyword", "const");
if (is_definition) {
exported_definition = statement();
} else {
@@ -2368,7 +2411,8 @@ function parse($TEXT, options) {
unexpected(tmp);
}
case "name":
if (tmp.value === "yield" && S.input.has_directive("use strict") && !is_in_generator()) {
if (tmp.value == "yield" && !is_token(peek(), "punc", ":")
&& S.input.has_directive("use strict") && !is_in_generator()) {
token_error(tmp, "Unexpected yield identifier inside strict mode");
}
case "string":

View File

@@ -48,9 +48,10 @@ 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[new_global] || new Function();
objects[new_global] = global_ref[new_global] || new Function();
});
// NaN will be included due to Number.NaN

View File

@@ -102,6 +102,7 @@ AST_Toplevel.DEFMETHOD("figure_out_scope", function(options){
options = defaults(options, {
cache: null,
ie8: false,
safari10: false,
});
// pass 1: setup scope chaining and handle definitions
@@ -112,6 +113,7 @@ AST_Toplevel.DEFMETHOD("figure_out_scope", function(options){
var in_destructuring = null;
var in_export = false;
var in_block = 0;
var for_scopes = [];
var tw = new TreeWalker(function(node, descend){
if (node.is_block_scope()) {
var save_scope = scope;
@@ -122,6 +124,11 @@ AST_Toplevel.DEFMETHOD("figure_out_scope", function(options){
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();
scope = save_scope;
return true;
@@ -303,6 +310,19 @@ 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) {
this.cname = options.cache.cname;
}
@@ -453,11 +473,6 @@ AST_Symbol.DEFMETHOD("unmangleable", function(options){
return def && def.unmangleable(options);
});
// property accessors are not mangleable
AST_SymbolAccessor.DEFMETHOD("unmangleable", function(){
return true;
});
// labels are always mangleable
AST_Label.DEFMETHOD("unmangleable", function(){
return false;

View File

@@ -239,6 +239,10 @@ TreeTransformer.prototype = new TreeWalker;
self.expression = self.expression.transform(tw);
});
_(AST_Export, function(self, tw){
if (self.exported_value) self.exported_value = self.exported_value.transform(tw);
});
_(AST_TemplateString, function(self, tw) {
for (var i = 0; i < self.segments.length; i++) {
if (!(self.segments[i] instanceof AST_TemplateSegment)) {

View File

@@ -1,10 +1,10 @@
{
"name": "uglify-js",
"name": "uglify-es",
"description": "JavaScript parser, mangler/compressor and beautifier toolkit",
"homepage": "http://lisperator.net/uglifyjs",
"homepage": "https://github.com/mishoo/UglifyJS2/tree/harmony",
"author": "Mihai Bazon <mihai.bazon@gmail.com> (http://lisperator.net/)",
"license": "BSD-2-Clause",
"version": "3.0.0",
"version": "3.0.10",
"engines": {
"node": ">=0.8.0"
},
@@ -33,22 +33,12 @@
"source-map": "~0.5.1"
},
"devDependencies": {
"acorn": "~0.6.0",
"escodegen": "~1.3.3",
"esfuzz": "~0.3.1",
"estraverse": "~1.5.1",
"mocha": "~2.3.4"
},
"optionalDependencies": {
"uglify-to-browserify": "~1.0.0"
},
"browserify": {
"transform": [
"uglify-to-browserify"
]
"acorn": "~5.0.3",
"mocha": "~2.3.4",
"semver": "~5.3.0"
},
"scripts": {
"test": "node test/run-tests.js"
},
"keywords": ["uglify", "uglify-js", "minify", "minifier"]
"keywords": ["uglify", "uglify-js", "uglify-es", "minify", "minifier", "es5", "es6", "es2015"]
}

View File

@@ -2284,3 +2284,49 @@ compound_assignment: {
}
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

@@ -278,3 +278,19 @@ try_catch_finally: {
"1",
]
}
accessor: {
options = {
side_effects: true,
}
input: {
({
get a() {},
set a(v){
this.b = 2;
},
b: 1
});
}
expect: {}
}

View File

@@ -315,3 +315,18 @@ unused: {
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

@@ -1228,3 +1228,55 @@ 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
}
issue_1968: {
options = {
unused: true,
}
input: {
function f(c) {
var a;
if (c) {
let b;
return (a = 2) + (b = 3);
}
}
console.log(f(1));
}
expect: {
function f(c) {
if (c) {
let b;
return 2 + (b = 3);
}
}
console.log(f(1));
}
expect_stdout: "5"
node_version: ">=6"
}

View File

@@ -1083,3 +1083,50 @@ 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"
}

View File

@@ -203,15 +203,66 @@ import_all_statement: {
}
export_statement: {
options = {
evaluate: true,
}
input: {
export default 1;
export default 1 + 2;
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{};"
expect_exact: "export default 3;export var foo=4;export let foo=6;export const foo=6;export function foo(){};export class foo{};"
}
export_default_object_expression: {
options = {
evaluate: true,
}
input: {
export default {
foo: 1 + 2,
bar() { return 4; },
get baz() { return this.foo; },
};
}
expect_exact: "export default{foo:3,bar(){return 4},get baz(){return this.foo}};"
}
export_default_array: {
options = {
evaluate: true,
}
input: {
export default [ 1 + 2, foo ];
}
expect_exact: "export default[3,foo];"
}
export_default_anon_function: {
options = {
evaluate: true,
}
input: {
export default function(){
console.log(1 + 2);
}
}
expect_exact: "export default function(){console.log(3)};"
}
export_default_anon_class: {
options = {
evaluate: true,
}
input: {
export default class {
foo() { console.log(1 + 2); }
}
}
expect_exact: "export default class{foo(){console.log(3)}};"
}
export_module_statement: {
@@ -392,3 +443,143 @@ format_methods: {
"}",
]
}
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: []
});
}
}
}
}
class_extends: {
options = {
evaluate: true,
}
input: {
function f() {
class foo extends bar {}
class pro extends some.prop {}
class arr extends stuff[1 - 1] {}
class bin extends (a || b) {}
class seq extends (a, b) {}
class ter extends (a ? b : c) {}
class uni extends (!0) {}
}
}
expect_exact: "function f(){class foo extends bar{}class pro extends some.prop{}class arr extends stuff[0]{}class bin extends(a||b){}class seq extends(a,b){}class ter extends(a?b:c){}class uni extends(!0){}}"
}
class_extends_class: {
options = {
}
input: {
class anon extends class {} {}
class named extends class base {} {}
}
expect_exact: "class anon extends class{}{}class named extends class base{}{}"
}
class_extends_function: {
options = {
}
input: {
class anon extends function(){} {}
class named extends function base(){} {}
}
expect_exact: "class anon extends function(){}{}class named extends function base(){}{}"
}
class_extends_regex: {
options = {
}
input: {
function f() {
class rx1 extends (/rx/) {}
// class rx2 extends /rx/ {} // FIXME - parse error
}
}
expect_exact: "function f(){class rx1 extends(/rx/){}}"
}

View File

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

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

@@ -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

@@ -119,3 +119,62 @@ chained: {
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;
(function() {
console.log(-3);
console.log(-4);
console.log(A - 5);
})();
(function f1() {
var a = 2;
console.log(-3);
console.log(a - 5);
eval("console.log(a);");
})();
(function f2(eval) {
var a = 2;
console.log(-3);
console.log(a - 5);
eval("console.log(a);");
})(eval);
"yes";
console.log(2);
console.log(A + 1);
}
expect_stdout: true
}
@@ -1749,7 +1749,10 @@ redefine_arguments_3: {
console.log(function() {
var arguments;
return typeof arguments;
}(), "number", "undefined");
}(), "number", function(x) {
var arguments = x;
return typeof arguments;
}());
}
expect_stdout: "object number undefined"
}
@@ -2461,3 +2464,76 @@ issue_1865: {
}
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,3 +710,27 @@ issue_27: {
})(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";
}
}
issue_1929: {
input: {
function f(s) {
return s.split(/[\\/]/);
}
}
expect_exact: "function f(s){return s.split(/[\\\\/]/)}"
}

View File

@@ -190,3 +190,12 @@ yield_sub: {
}
expect_exact: 'function*foo(){yield x["foo"];(yield x)["foo"];yield(yield obj.foo())["bar"]()}'
}
yield_as_ES5_property: {
input: {
"use strict";
console.log({yield: 42}.yield);
}
expect_exact: '"use strict";console.log({yield:42}.yield);'
expect_stdout: "42"
}

13
test/exports.js Normal file
View File

@@ -0,0 +1,13 @@
exports["Compressor"] = Compressor;
exports["JS_Parse_Error"] = JS_Parse_Error;
exports["OutputStream"] = OutputStream;
exports["SourceMap"] = SourceMap;
exports["TreeWalker"] = TreeWalker;
exports["base54"] = base54;
exports["defaults"] = defaults;
exports["mangle_properties"] = mangle_properties;
exports["minify"] = minify;
exports["parse"] = parse;
exports["string_template"] = string_template;
exports["tokenizer"] = tokenizer;
exports["is_identifier"] = is_identifier;

View File

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

View File

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

View File

@@ -1,4 +1,4 @@
var UglifyJS = require('../../');
var UglifyJS = require("../node");
var assert = require("assert");
describe("Accessor tokens", function() {

View File

@@ -1,4 +1,4 @@
var UglifyJS = require('../../');
var UglifyJS = require("../node");
var assert = require("assert");
describe("arguments", function() {

View File

@@ -1,5 +1,5 @@
var assert = require("assert");
var uglify = require("../../");
var uglify = require("../node");
describe("Arrow functions", function() {
it("Should not accept spread tokens on non-last parameters or without arguments parentheses", function() {

View File

@@ -1,5 +1,5 @@
var assert = require("assert");
var uglify = require("../../");
var uglify = require("../node");
describe("Class", function() {
it("Should not accept spread on non-last parameters in methods", function() {

View File

@@ -19,7 +19,9 @@ describe("bin/uglifyjs", function () {
eval(stdout);
assert.strictEqual(typeof WrappedUglifyJS, 'object');
assert.strictEqual(true, WrappedUglifyJS.parse('foo;') instanceof WrappedUglifyJS.AST_Node);
var result = WrappedUglifyJS.minify("foo([true,,2+3]);");
assert.strictEqual(result.error, undefined);
assert.strictEqual(result.code, "foo([!0,,5]);");
done();
});
@@ -185,7 +187,7 @@ describe("bin/uglifyjs", function () {
exec(command, function (err, stdout, stderr) {
assert.ok(err);
assert.strictEqual(stderr, "ERROR: inline source map only works with singular input\n");
assert.strictEqual(stderr.split(/\n/)[0], "ERROR: inline source map only works with singular input");
done();
});
});
@@ -484,6 +486,36 @@ describe("bin/uglifyjs", function () {
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) {
var command = [
uglifyjscmd,
@@ -509,4 +541,15 @@ describe("bin/uglifyjs", function () {
return JSON.stringify(map).replace(/"/g, '\\"');
}
});
it("Should dump AST as JSON", function(done) {
var command = uglifyjscmd + " test/input/global_defs/simple.js -mco ast";
exec(command, function (err, stdout) {
if (err) throw err;
var ast = JSON.parse(stdout);
assert.strictEqual(ast._class, "AST_Toplevel");
assert.ok(Array.isArray(ast.body));
done();
});
});
});

View File

@@ -1,4 +1,4 @@
var UglifyJS = require('../../');
var UglifyJS = require("../node");
var assert = require("assert");
describe("comment filters", function() {

View File

@@ -1,5 +1,5 @@
var assert = require("assert");
var uglify = require("../../");
var uglify = require("../node");
describe("Comment", function() {
it("Should recognize eol of single line comments", function() {

View File

@@ -1,5 +1,5 @@
var assert = require("assert");
var uglify = require("../../");
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() {

View File

@@ -1,5 +1,5 @@
var assert = require("assert");
var uglify = require("../../");
var uglify = require("../node");
describe("Directives", function() {
it ("Should allow tokenizer to store directives state", function() {
@@ -361,18 +361,28 @@ describe("Directives", function() {
var tests = [
[
'"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
'"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++) {
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][0]
);

View File

@@ -1,5 +1,5 @@
var assert = require("assert");
var uglify = require("../../");
var uglify = require("../node");
describe("EOF", function() {
it("Should test code for at least throwing syntax error when incomplete", function() {

View File

@@ -1,5 +1,5 @@
var assert = require("assert");
var uglify = require("../../");
var uglify = require("../node");
describe("Export", function() {
it ("Should parse export directives", function() {

View File

@@ -1,5 +1,5 @@
var assert = require("assert");
var uglify = require("../../");
var uglify = require("../node");
describe("Expression", function() {
it("Should not allow the first exponentiation operator to be prefixed with an unary operator", function() {

View File

@@ -1,5 +1,5 @@
var assert = require("assert");
var uglify = require("../../");
var uglify = require("../node");
describe("Function", function() {
it ("Should parse binding patterns correctly", function() {

View File

@@ -1,4 +1,4 @@
var UglifyJS = require('../../');
var UglifyJS = require("../node");
var assert = require("assert");
describe("Getters and setters", function() {

View File

@@ -1,5 +1,5 @@
var assert = require("assert");
var uglify = require("../../");
var uglify = require("../node");
describe("Left-hand side expressions", function () {
it("Should parse destructuring with const/let/var correctly", function () {

View File

@@ -1,4 +1,4 @@
var Uglify = require('../../');
var Uglify = require("../node");
var assert = require("assert");
describe("line-endings", function() {

View File

@@ -114,8 +114,7 @@ describe("minify", function() {
}
});
it("Should fail with multiple input and inline source map", function() {
assert.throws(function() {
Uglify.minify([
var result = Uglify.minify([
read("./test/input/issue-520/input.js"),
read("./test/input/issue-520/output.js")
], {
@@ -124,7 +123,9 @@ describe("minify", function() {
url: "inline"
}
});
});
var err = result.error;
assert.ok(err instanceof Error);
assert.strictEqual(err.stack.split(/\n/)[0], "Error: inline source map only works with singular input");
});
});
@@ -170,26 +171,14 @@ describe("minify", function() {
});
describe("JS_Parse_Error", function() {
it("should throw syntax error", function() {
assert.throws(function() {
Uglify.minify("function f(a{}");
}, function(err) {
it("should return syntax error", function() {
var result = Uglify.minify("function f(a{}");
var err = result.error;
assert.ok(err instanceof Error);
assert.strictEqual(err.stack.split(/\n/)[0], "SyntaxError: Unexpected token punc «{», expected punc «,»");
assert.strictEqual(err.filename, "0");
assert.strictEqual(err.line, 1);
assert.strictEqual(err.col, 12);
return true;
});
});
});
describe("Compressor", function() {
it("should be backward compatible with ast.transform(compressor)", function() {
var ast = Uglify.parse("function f(a){for(var i=0;i<a;i++)console.log(i)}");
ast.figure_out_scope();
ast = ast.transform(Uglify.Compressor());
assert.strictEqual(ast.print_to_string(), "function f(a){for(var i=0;i<a;i++)console.log(i)}");
});
})
});

View File

@@ -1,5 +1,5 @@
var assert = require("assert");
var uglify = require("../../");
var uglify = require("../node");
describe("New", function() {
it("Should add trailing parentheses for new expressions with zero arguments in beautify mode", function() {

View File

@@ -1,5 +1,5 @@
var assert = require("assert");
var uglify = require("../../");
var uglify = require("../node");
describe("Number literals", function () {
it("Should not allow legacy octal literals in strict mode", function() {

View File

@@ -1,4 +1,4 @@
var Uglify = require("../../");
var Uglify = require("../node");
var assert = require("assert");
describe("Object", function() {

View File

@@ -1,4 +1,4 @@
var UglifyJS = require("../../");
var UglifyJS = require("../node");
var assert = require("assert");
describe("operator", function() {

View File

@@ -1,10 +1,10 @@
var assert = require("assert");
var exec = require("child_process").exec;
var uglify = require("../../");
var uglify = require("../node");
describe("spidermonkey export/import sanity test", function() {
it("should produce a functional build when using --self with spidermonkey", function(done) {
this.timeout(20000);
this.timeout(30000);
var uglifyjs = '"' + process.argv[0] + '" bin/uglifyjs';
var command = uglifyjs + " --self -cm --wrap SpiderUglify -o spidermonkey | " +
@@ -15,18 +15,9 @@ describe("spidermonkey export/import sanity test", function() {
eval(stdout);
assert.strictEqual(typeof SpiderUglify, "object");
var ast = SpiderUglify.parse("foo([true,,2+3]);");
assert.strictEqual(true, ast instanceof SpiderUglify.AST_Node);
ast.figure_out_scope();
ast = SpiderUglify.Compressor({}).compress(ast);
assert.strictEqual(true, ast instanceof SpiderUglify.AST_Node);
var stream = SpiderUglify.OutputStream({});
ast.print(stream);
var code = stream.toString();
assert.strictEqual(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();
});

View File

@@ -1,4 +1,4 @@
var UglifyJS = require('../../');
var UglifyJS = require("../node");
var assert = require("assert");
describe("String literals", function() {

View File

@@ -1,5 +1,5 @@
var assert = require("assert");
var uglify = require("../../");
var uglify = require("../node");
describe("Template string", function() {
it("Should not accept invalid sequences", function() {

View File

@@ -1,5 +1,5 @@
var assert = require("assert");
var uglify = require("../../");
var uglify = require("../node");
describe("Try", function() {
it("Should not allow catch with an empty parameter", function() {

View File

@@ -1,5 +1,5 @@
var assert = require("assert");
var uglify = require("../../");
var uglify = require("../node");
describe("Unicode", function() {
it("Should not accept invalid code ranges in unicode escape", function() {
@@ -135,4 +135,11 @@ describe("Unicode", function() {
}).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,5 +1,5 @@
var assert = require("assert");
var uglify = require("../../");
var uglify = require("../node");
describe("With", function() {
it("Should throw syntaxError when using with statement in strict mode", function() {

View File

@@ -1,4 +1,4 @@
var UglifyJS = require("../../");
var UglifyJS = require("../node");
var assert = require("assert");
describe("Yield", function() {
@@ -65,9 +65,9 @@ describe("Yield", function() {
);
});
it("Should not allow yield to be used as symbol, identifier or property outside generators in strict mode", function() {
it("Should not allow yield to be used as symbol, identifier or shorthand property outside generators in strict mode", function() {
var tests = [
// Fail as as_symbol
// Fail in as_symbol
'"use strict"; import yield from "bar";',
'"use strict"; yield = 123;',
'"use strict"; yield: "123";',
@@ -79,13 +79,12 @@ describe("Yield", function() {
'"use strict"; var yield = "foo";',
'"use strict"; class yield {}',
// Fail as maybe_assign
// Fail in maybe_assign
'"use strict"; var foo = yield;',
'"use strict"; var foo = bar = yield',
// Fail as as_property_name
// Fail in as_property_name
'"use strict"; var foo = {yield};',
'"use strict"; var bar = {yield: "foo"};'
];
var fail = function(e) {

View File

@@ -1,103 +1,73 @@
// Testing UglifyJS <-> SpiderMonkey AST conversion
// through generative testing.
"use strict";
var UglifyJS = require(".."),
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 ufuzz = require("./ufuzz");
var UglifyJS = require("..");
fs.existsSync("tmp") || fs.mkdirSync("tmp");
jsPair.forEach(function (item) {
var fileName = "tmp/dump_" + item.name;
var ast = acorn.parse(item.value);
fs.writeFileSync(fileName + ".js", item.value);
fs.writeFileSync(fileName + ".json", JSON.stringify(ast, null, 2));
function try_beautify(code) {
var beautified = UglifyJS.minify(code, {
compress: false,
mangle: false,
output: {
beautify: true,
bracketize: true
}
});
process.stdout.write("\n");
throw new Error("Got different outputs, check out tmp/dump_*.{js,json} for codes and ASTs.");
if (beautified.error) {
console.log("// !!! beautify failed !!!");
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();

6
test/node.js Normal file
View File

@@ -0,0 +1,6 @@
var fs = require("fs");
new Function("MOZ_SourceMap", "exports", require("../tools/node").FILES.map(function(file) {
if (/exports\.js$/.test(file)) file = require.resolve("./exports");
return fs.readFileSync(file, "utf8");
}).join("\n\n"))(require("source-map"), exports);

View File

@@ -1,10 +1,11 @@
#! /usr/bin/env node
var U = require("../tools/node");
var U = require("./node");
var path = require("path");
var fs = require("fs");
var assert = require("assert");
var sandbox = require("./sandbox");
var semver = require("semver");
var tests_dir = path.dirname(module.filename);
var failures = 0;
@@ -23,12 +24,6 @@ mocha_tests();
var run_sourcemaps_tests = require('./sourcemaps');
run_sourcemaps_tests();
var run_ast_conversion_tests = require("./mozilla-ast");
run_ast_conversion_tests({
iterations: 1000
});
/* -----[ utils ]----- */
function tmpl() {
@@ -170,7 +165,8 @@ function run_compress_tests() {
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);
if (test.expect_stdout === true) {
test.expect_stdout = stdout;
@@ -280,7 +276,14 @@ function parse_test(file) {
if (node instanceof U.AST_LabeledStatement) {
var label = node.label;
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}]", {
name: label.name,
line: label.start.line,
@@ -288,7 +291,7 @@ function parse_test(file) {
})
);
var stat = node.body;
if (label.name == "expect_exact") {
if (label.name == "expect_exact" || label.name == "node_version") {
test[label.name] = read_string(stat);
} else if (label.name == "expect_stdout") {
if (stat.TYPE == "SimpleStatement" && stat.body instanceof U.AST_Boolean) {

View File

@@ -1,14 +1,17 @@
var semver = require("semver");
var vm = require("vm");
function safe_log(arg) {
function safe_log(arg, level) {
if (arg) switch (typeof arg) {
case "function":
return arg.toString();
case "object":
if (/Error$/.test(arg.name)) return arg.toString();
arg.constructor.toString();
for (var key in arg) {
arg[key] = safe_log(arg[key]);
if (level--) for (var key in arg) {
if (!Object.getOwnPropertyDescriptor(arg, key).get) {
arg[key] = safe_log(arg[key], level);
}
}
}
return arg;
@@ -48,7 +51,9 @@ exports.run_code = function(code) {
].join("\n"), {
console: {
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 });
@@ -59,7 +64,7 @@ exports.run_code = function(code) {
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 != "string") {
if (expected.name != actual.name) return false;

View File

@@ -1,4 +1,4 @@
var UglifyJS = require("..");
var UglifyJS = require("./node");
var ok = require("assert");
module.exports = function () {

View File

@@ -12,7 +12,7 @@
stream._handle.setBlocking(true);
});
var UglifyJS = require("..");
var UglifyJS = require("./node");
var randomBytes = require("crypto").randomBytes;
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 verbose = false; // log every generated test
var verbose_interval = false; // log every 100 generated tests
var verbose_error = 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) {
switch (process.argv[i]) {
case '-v':
@@ -58,9 +59,6 @@ for (var i = 2; i < process.argv.length; ++i) {
case '-V':
verbose_interval = true;
break;
case '-E':
verbose_error = true;
break;
case '-t':
MAX_GENERATED_TOPLEVELS_PER_RUN = +process.argv[++i];
if (!MAX_GENERATED_TOPLEVELS_PER_RUN) throw new Error('Must generate at least one toplevel per run');
@@ -79,6 +77,12 @@ for (var i = 2; i < process.argv.length; ++i) {
STMT_SECOND_LEVEL_OVERRIDE = STMT_ARG_TO_ID[name];
if (!(STMT_SECOND_LEVEL_OVERRIDE >= 0)) throw new Error('Unknown statement name; use -? to get a list');
break;
case '--no-catch-redef':
catch_redef = false;
break;
case '--no-directive':
generate_directive = false;
break;
case '--use-strict':
use_strict = true;
break;
@@ -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('-v: print every 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('-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('-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('--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');
@@ -192,12 +197,33 @@ var ASSIGNMENTS = [
'=',
'=',
'=',
'=',
'=',
'=',
'=',
'=',
'=',
'=',
'=',
'=',
'=',
'=',
'=',
'=',
'=',
'==',
'!=',
'===',
'!==',
'+=',
'+=',
'+=',
'+=',
'+=',
'+=',
'+=',
'+=',
'+=',
'+=',
'-=',
'*=',
'/=',
@@ -207,7 +233,8 @@ var ASSIGNMENTS = [
'<<=',
'>>=',
'>>>=',
'%=' ];
'%=',
];
var UNARY_SAFE = [
'+',
@@ -276,6 +303,7 @@ var TYPEOF_OUTCOMES = [
'symbol',
'crap' ];
var unique_vars = [];
var loops = 0;
var funcs = 0;
var labels = 10000;
@@ -290,6 +318,10 @@ function strictMode() {
}
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 [
strictMode(),
'var a = 100, b = 10, c = 0;',
@@ -325,33 +357,36 @@ function createArgs() {
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) {
if (--recurmax < 0) { return ';'; }
if (!STMT_COUNT_FROM_GLOBAL) stmtDepth = 0;
var func = funcs++;
var namesLenBefore = VAR_NAMES.length;
var name = (inGlobal || rng(5) > 0) ? 'f' + func : createVarName(MANDATORY, noDecl);
if (name === 'a' || name === 'b' || name === 'c') name = 'f' + func; // quick hack to prevent assignment to func names of being called
var s = '';
var name;
if (inGlobal || rng(5) > 0) name = 'f' + func;
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) {
// functions with functions. lower the recursion to prevent a mess.
s = [
'function ' + name + '(' + createParams() + '){',
strictMode(),
createFunctions(rng(5) + 1, Math.ceil(recurmax * 0.7), NOT_GLOBAL, ANY_TYPE, canThrow, stmtDepth),
'}',
''
].join('\n');
s.push(createFunctions(rng(5) + 1, Math.ceil(recurmax * 0.7), NOT_GLOBAL, ANY_TYPE, canThrow, stmtDepth));
} else {
// functions with statements
s = [
'function ' + name + '(' + createParams() + '){',
strictMode(),
createStatements(3, recurmax, canThrow, CANNOT_BREAK, CANNOT_CONTINUE, CAN_RETURN, stmtDepth),
'}',
''
].join('\n');
s.push(createStatements(3, recurmax, canThrow, CANNOT_BREAK, CANNOT_CONTINUE, CAN_RETURN, stmtDepth));
}
s.push('}', '');
s = filterDirective(s).join('\n');
VAR_NAMES.length = namesLenBefore;
@@ -359,7 +394,6 @@ function createFunction(recurmax, inGlobal, noDecl, canThrow, stmtDepth) {
// avoid "function statements" (decl inside statements)
else if (inGlobal || rng(10) > 0) s += 'var ' + createVarName(MANDATORY) + ' = ' + name + '(' + createArgs() + ');';
return s;
}
@@ -406,7 +440,7 @@ function getLabel(label) {
return label && " L" + label;
}
function createStatement(recurmax, canThrow, canBreak, canContinue, cannotReturn, stmtDepth) {
function createStatement(recurmax, canThrow, canBreak, canContinue, cannotReturn, stmtDepth, target) {
++stmtDepth;
var loop = ++loops;
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
var target = 0;
if (target === undefined) {
if (stmtDepth === 1 && STMT_FIRST_LEVEL_OVERRIDE >= 0) target = STMT_FIRST_LEVEL_OVERRIDE;
else if (stmtDepth === 2 && STMT_SECOND_LEVEL_OVERRIDE >= 0) target = STMT_SECOND_LEVEL_OVERRIDE;
else target = STMTS_TO_USE[rng(STMTS_TO_USE.length)];
}
switch (target) {
case STMT_BLOCK:
@@ -460,20 +495,22 @@ function createStatement(recurmax, canThrow, canBreak, canContinue, cannotReturn
case STMT_VAR:
switch (rng(3)) {
case 0:
unique_vars.push('c');
var name = createVarName(MANDATORY);
if (name === 'c') name = 'a';
unique_vars.pop();
return 'var ' + name + ';';
case 1:
// initializer can only have one expression
unique_vars.push('c');
var name = createVarName(MANDATORY);
if (name === 'c') name = 'b';
unique_vars.pop();
return 'var ' + name + ' = ' + createExpression(recurmax, NO_COMMA, stmtDepth, canThrow) + ';';
default:
// initializer can only have one expression
unique_vars.push('c');
var n1 = createVarName(MANDATORY);
if (n1 === 'c') n1 = 'b';
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) + ';';
}
case STMT_RETURN_ETC:
@@ -514,8 +551,11 @@ function createStatement(recurmax, canThrow, canBreak, canContinue, cannotReturn
var nameLenBefore = VAR_NAMES.length;
var catchName = createVarName(MANDATORY);
var freshCatchName = VAR_NAMES.length !== nameLenBefore;
if (!catch_redef) unique_vars.push(catchName);
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) + ' }';
return s;
@@ -593,8 +633,9 @@ function _createExpression(recurmax, noComma, stmtDepth, canThrow) {
case p++:
case p++:
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.
if (name == 'c') name = 'a';
unique_vars.pop();
var s = [];
switch (rng(5)) {
case 0:
@@ -636,7 +677,7 @@ function _createExpression(recurmax, noComma, stmtDepth, canThrow) {
strictMode()
);
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) + ';');
}
s.push(
@@ -646,7 +687,7 @@ function _createExpression(recurmax, noComma, stmtDepth, canThrow) {
break;
}
VAR_NAMES.length = nameLenBefore;
return s.join('\n');
return filterDirective(s).join('\n');
case p++:
case p++:
return createTypeofExpr(recurmax, stmtDepth, canThrow);
@@ -689,19 +730,19 @@ function _createExpression(recurmax, noComma, stmtDepth, canThrow) {
") || " + rng(10) + ").toString()[" +
createExpression(recurmax, COMMA_OK, stmtDepth, canThrow) + "] ";
case p++:
return createArrayLiteral(recurmax, COMMA_OK, stmtDepth, canThrow);
return createArrayLiteral(recurmax, stmtDepth, canThrow);
case p++:
return createObjectLiteral(recurmax, COMMA_OK, stmtDepth, canThrow);
return createObjectLiteral(recurmax, stmtDepth, canThrow);
case p++:
return createArrayLiteral(recurmax, COMMA_OK, stmtDepth, canThrow) + '[' +
return createArrayLiteral(recurmax, stmtDepth, canThrow) + '[' +
createExpression(recurmax, COMMA_OK, stmtDepth, canThrow) + ']';
case p++:
return createObjectLiteral(recurmax, COMMA_OK, stmtDepth, canThrow) + '[' +
return createObjectLiteral(recurmax, stmtDepth, canThrow) + '[' +
createExpression(recurmax, COMMA_OK, stmtDepth, canThrow) + ']';
case p++:
return createArrayLiteral(recurmax, COMMA_OK, stmtDepth, canThrow) + '.' + getDotKey();
return createArrayLiteral(recurmax, stmtDepth, canThrow) + '.' + getDotKey();
case p++:
return createObjectLiteral(recurmax, COMMA_OK, stmtDepth, canThrow) + '.' + getDotKey();
return createObjectLiteral(recurmax, stmtDepth, canThrow) + '.' + getDotKey();
case p++:
var name = getVarName();
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);
}
function createArrayLiteral(recurmax, noComma, stmtDepth, canThrow) {
function createArrayLiteral(recurmax, stmtDepth, canThrow) {
recurmax--;
var arr = "[";
for (var i = rng(6); --i >= 0;) {
@@ -746,18 +787,56 @@ var KEYS = [
"3",
].concat(SAFE_KEYS);
function getDotKey() {
return SAFE_KEYS[rng(SAFE_KEYS.length)];
function getDotKey(assign) {
var key;
do {
key = SAFE_KEYS[rng(SAFE_KEYS.length)];
} while (assign && key == "length");
return key;
}
function createObjectLiteral(recurmax, noComma, stmtDepth, canThrow) {
recurmax--;
var obj = "({";
for (var i = rng(6); --i >= 0;) {
var key = KEYS[rng(KEYS.length)];
obj += key + ":(" + createExpression(recurmax, COMMA_OK, stmtDepth, canThrow) + "), ";
function createAccessor(recurmax, stmtDepth, canThrow) {
var namesLenBefore = VAR_NAMES.length;
var s;
var prop1 = getDotKey();
if (rng(2) == 0) {
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) {
@@ -787,7 +866,7 @@ function _createSimpleBinaryExpr(recurmax, noComma, stmtDepth, canThrow) {
return canThrow && rng(10) == 0 ? expr : '(' + assignee + ' && ' + expr + ')';
case 4:
assignee = getVarName();
expr = '(' + assignee + '.' + getDotKey() + createAssignment()
expr = '(' + assignee + '.' + getDotKey(true) + createAssignment()
+ _createBinaryExpr(recurmax, noComma, stmtDepth, canThrow) + ')';
return canThrow && rng(10) == 0 ? expr : '(' + assignee + ' && ' + expr + ')';
default:
@@ -844,19 +923,25 @@ function getVarName() {
function createVarName(maybe, dontStore) {
if (!maybe || rng(2)) {
var name = VAR_NAMES[rng(VAR_NAMES.length)];
var suffix = rng(3);
if (suffix) {
name += '_' + suffix;
if (!dontStore) VAR_NAMES.push(name);
}
var name;
do {
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 '';
}
if (require.main !== module) {
exports.createTopLevelCode = createTopLevelCode;
exports.num_iterations = num_iterations;
return;
}
function try_beautify(code, result) {
try {
var beautified = UglifyJS.minify(code, {
compress: false,
mangle: false,
@@ -864,15 +949,14 @@ function try_beautify(code, result) {
beautify: true,
bracketize: true,
},
}).code;
if (sandbox.same_stdout(sandbox.run_code(beautified), result)) {
console.log("// (beautified)");
console.log(beautified);
return;
}
} catch (e) {
});
if (beautified.error) {
console.log("// !!! beautify failed !!!");
console.log(e.stack);
console.log(beautified.error.stack);
} else if (sandbox.same_stdout(sandbox.run_code(beautified.code), result)) {
console.log("// (beautified)");
console.log(beautified.code);
return;
}
console.log("//");
console.log(code);
@@ -908,12 +992,13 @@ function log_suspects(minify_options, component) {
var o = JSON.parse(JSON.stringify(options));
o[name] = false;
m[component] = o;
try {
var r = sandbox.run_code(UglifyJS.minify(original_code, m).code);
return sandbox.same_stdout(original_result, r);
} catch (e) {
var result = UglifyJS.minify(original_code, m);
if (result.error) {
console.log("Error testing options." + component + "." + name);
console.log(e);
console.log(result.error);
} else {
var r = sandbox.run_code(result.code);
return sandbox.same_stdout(original_result, r);
}
}
});
@@ -974,28 +1059,22 @@ var uglify_code, uglify_result, ok;
for (var round = 1; round <= num_iterations; round++) {
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_result = sandbox.run_code(original_code);
(typeof original_result != "string" ? fallback_options : minify_options).forEach(function(options) {
try {
uglify_code = UglifyJS.minify(original_code, JSON.parse(options)).code;
} catch (e) {
uglify_code = e;
}
ok = typeof uglify_code == "string";
if (ok) {
uglify_code = UglifyJS.minify(original_code, JSON.parse(options));
if (!uglify_code.error) {
uglify_code = uglify_code.code;
uglify_result = sandbox.run_code(uglify_code);
ok = sandbox.same_stdout(original_result, uglify_result);
} else if (typeof original_result != "string") {
} else {
uglify_code = uglify_code.error;
if (typeof original_result != "string") {
ok = uglify_code.name == original_result.name;
}
}
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("// original code");
try_beautify(original_code, original_result);

View File

@@ -1,15 +1,5 @@
exports["Compressor"] = Compressor;
exports["Dictionary"] = Dictionary;
exports["JS_Parse_Error"] = JS_Parse_Error;
exports["OutputStream"] = OutputStream;
exports["SourceMap"] = SourceMap;
exports["TreeWalker"] = TreeWalker;
exports["base54"] = base54;
exports["defaults"] = defaults;
exports["mangle_properties"] = mangle_properties;
exports["TreeTransformer"] = TreeTransformer;
exports["minify"] = minify;
exports["parse"] = parse;
exports["push_uniq"] = push_uniq;
exports["string_template"] = string_template;
exports["tokenizer"] = tokenizer;
exports["is_identifier"] = is_identifier;
exports["_push_uniq"] = push_uniq;

View File

@@ -18,15 +18,19 @@ var FILES = UglifyJS.FILES = [
return require.resolve(file);
});
new Function("MOZ_SourceMap", "exports", FILES.map(function(file){
new Function("MOZ_SourceMap", "exports", function() {
var code = FILES.map(function(file) {
return fs.readFileSync(file, "utf8");
}).join("\n\n"))(
});
code.push("exports.describe_ast = " + describe_ast.toString());
return code.join("\n\n");
}())(
require("source-map"),
UglifyJS
);
UglifyJS.describe_ast = function() {
var out = UglifyJS.OutputStream({ beautify: true });
function describe_ast() {
var out = OutputStream({ beautify: true });
function doitem(ctor) {
out.print("AST_" + ctor.TYPE);
var props = ctor.SELF_PROPS.filter(function(prop){
@@ -56,6 +60,6 @@ UglifyJS.describe_ast = function() {
});
}
};
doitem(UglifyJS.AST_Node);
doitem(AST_Node);
return out + "";
};
}