diff --git a/.travis.yml b/.travis.yml index c33b24d1..0324385f 100644 --- a/.travis.yml +++ b/.travis.yml @@ -4,7 +4,7 @@ node_js: - "iojs" - "0.12" - "0.10" - - "0.8" + - "4" matrix: fast_finish: true sudo: false diff --git a/bin/uglifyjs b/bin/uglifyjs index 4768f766..f82d43cb 100755 --- a/bin/uglifyjs +++ b/bin/uglifyjs @@ -254,7 +254,6 @@ if (ARGS.self) { } files = UglifyJS.FILES; if (!ARGS.wrap) ARGS.wrap = "UglifyJS"; - ARGS.export_all = true; } var ORIG_MAP = ARGS.in_source_map; @@ -402,17 +401,14 @@ async.eachLimit(files, 1, function (file, cb) { writeNameCache("props", cache); })(); - var SCOPE_IS_NEEDED = COMPRESS || MANGLE || ARGS.lint; var TL_CACHE = readNameCache("vars"); - if (SCOPE_IS_NEEDED) { - time_it("scope", function(){ - TOPLEVEL.figure_out_scope({ screw_ie8: ARGS.screw_ie8, cache: TL_CACHE }); - if (ARGS.lint) { - TOPLEVEL.scope_warnings(); - } - }); - } + time_it("scope", function(){ + TOPLEVEL.figure_out_scope({ screw_ie8: ARGS.screw_ie8, cache: TL_CACHE }); + if (ARGS.lint) { + TOPLEVEL.scope_warnings(); + } + }); if (COMPRESS) { time_it("squeeze", function(){ @@ -420,14 +416,12 @@ async.eachLimit(files, 1, function (file, cb) { }); } - if (SCOPE_IS_NEEDED) { - time_it("scope", function(){ - TOPLEVEL.figure_out_scope({ screw_ie8: ARGS.screw_ie8, cache: TL_CACHE }); - if (MANGLE && !TL_CACHE) { - TOPLEVEL.compute_char_frequency(MANGLE); - } - }); - } + time_it("scope", function(){ + TOPLEVEL.figure_out_scope({ screw_ie8: ARGS.screw_ie8, cache: TL_CACHE }); + if (MANGLE && !TL_CACHE) { + TOPLEVEL.compute_char_frequency(MANGLE); + } + }); if (MANGLE) time_it("mangle", function(){ MANGLE.cache = TL_CACHE; diff --git a/lib/ast.js b/lib/ast.js index 3eba3a71..07126e7c 100644 --- a/lib/ast.js +++ b/lib/ast.js @@ -81,6 +81,7 @@ function DEFNODE(type, props, methods, base) { ctor.DEFMETHOD = function(name, method) { this.prototype[name] = method; }; + exports["AST_" + type] = ctor; return ctor; }; @@ -333,12 +334,11 @@ var AST_Toplevel = DEFNODE("Toplevel", "globals", { } })); } - var wrapped_tl = "(function(exports, global){ global['" + name + "'] = exports; '$ORIG'; '$EXPORTS'; }({}, (function(){return this}())))"; + var wrapped_tl = "(function(exports, global){ '$ORIG'; '$EXPORTS'; global['" + name + "'] = exports; }({}, (function(){return this}())))"; wrapped_tl = parse(wrapped_tl); wrapped_tl = wrapped_tl.transform(new TreeTransformer(function before(node){ - if (node instanceof AST_SimpleStatement) { - node = node.body; - if (node instanceof AST_String) switch (node.getValue()) { + if (node instanceof AST_Directive) { + switch (node.value) { case "$ORIG": return MAP.splice(self.body); case "$EXPORTS": @@ -1027,10 +1027,11 @@ var AST_String = DEFNODE("String", "value quote", { } }, AST_Constant); -var AST_Number = DEFNODE("Number", "value", { +var AST_Number = DEFNODE("Number", "value literal", { $documentation: "A number literal", $propdoc: { - value: "[number] the numeric value" + value: "[number] the numeric value", + literal: "[string] numeric value as string (optional)" } }, AST_Constant); diff --git a/lib/compress.js b/lib/compress.js index d2f41263..7dd43e69 100644 --- a/lib/compress.js +++ b/lib/compress.js @@ -61,7 +61,7 @@ function Compressor(options, false_by_default) { loops : !false_by_default, unused : !false_by_default, hoist_funs : !false_by_default, - keep_fargs : false, + keep_fargs : true, keep_fnames : false, hoist_vars : false, if_return : !false_by_default, @@ -111,6 +111,7 @@ merge(Compressor.prototype, { node.DEFMETHOD("optimize", function(compressor){ var self = this; if (self._optimized) return self; + if (compressor.has_directive("use asm")) return self; var opt = optimizer(self, compressor); opt._optimized = true; if (opt === self) return opt; @@ -1039,6 +1040,7 @@ merge(Compressor.prototype, { AST_Scope.DEFMETHOD("drop_unused", function(compressor){ var self = this; + if (compressor.has_directive("use asm")) return self; if (compressor.option("unused") && !(self instanceof AST_Toplevel) && !self.uses_eval @@ -1101,7 +1103,7 @@ merge(Compressor.prototype, { var tt = new TreeTransformer( function before(node, descend, in_list) { if (node instanceof AST_Lambda && !(node instanceof AST_Accessor)) { - if (compressor.option("unsafe") && !compressor.option("keep_fargs")) { + if (!compressor.option("keep_fargs")) { for (var a = node.argnames, i = a.length; --i >= 0;) { if (a[i] instanceof AST_Destructuring) { // Do not drop destructuring arguments. @@ -1232,10 +1234,14 @@ merge(Compressor.prototype, { }); AST_Scope.DEFMETHOD("hoist_declarations", function(compressor){ + var self = this; + if (compressor.has_directive("use asm")) return self; + // Hoisting makes no sense in an arrow func + if (!Array.isArray(self.body)) return self; + var hoist_funs = compressor.option("hoist_funs"); var hoist_vars = compressor.option("hoist_vars"); - var self = this; - if (!(self.body instanceof Array)) { return self; } // Hoisting makes no sense in an arrow func + if (hoist_funs || hoist_vars) { var dirs = []; var hoisted = []; @@ -2072,15 +2078,14 @@ merge(Compressor.prototype, { var commutativeOperators = makePredicate("== === != !== * & | ^"); OPT(AST_Binary, function(self, compressor){ - var reverse = compressor.has_directive("use asm") ? noop - : function(op, force) { - if (force || !(self.left.has_side_effects(compressor) || self.right.has_side_effects(compressor))) { - if (op) self.operator = op; - var tmp = self.left; - self.left = self.right; - self.right = tmp; - } - }; + function reverse(op, force) { + if (force || !(self.left.has_side_effects(compressor) || self.right.has_side_effects(compressor))) { + if (op) self.operator = op; + var tmp = self.left; + self.left = self.right; + self.right = tmp; + } + } if (commutativeOperators(self.operator)) { if (self.right instanceof AST_Constant && !(self.left instanceof AST_Constant)) { @@ -2148,10 +2153,10 @@ merge(Compressor.prototype, { if (compressor.option("conditionals")) { if (self.operator == "&&") { var ll = self.left.evaluate(compressor); - var rr = self.right.evaluate(compressor); if (ll.length > 1) { if (ll[1]) { compressor.warn("Condition left of && always true [{file}:{line},{col}]", self.start); + var rr = self.right.evaluate(compressor); return rr[0]; } else { compressor.warn("Condition left of && always false [{file}:{line},{col}]", self.start); @@ -2161,13 +2166,13 @@ merge(Compressor.prototype, { } else if (self.operator == "||") { var ll = self.left.evaluate(compressor); - var rr = self.right.evaluate(compressor); if (ll.length > 1) { if (ll[1]) { compressor.warn("Condition left of || always true [{file}:{line},{col}]", self.start); return ll[0]; } else { compressor.warn("Condition left of || always false [{file}:{line},{col}]", self.start); + var rr = self.right.evaluate(compressor); return rr[0]; } } diff --git a/lib/mozilla-ast.js b/lib/mozilla-ast.js index 1deb18e2..ac53ca27 100644 --- a/lib/mozilla-ast.js +++ b/lib/mozilla-ast.js @@ -399,7 +399,7 @@ function map(moztype, mytype, propmap) { var moz_to_me = "function From_Moz_" + moztype + "(M){\n"; - moz_to_me += "return new " + mytype.name + "({\n" + + moz_to_me += "return new U2." + mytype.name + "({\n" + "start: my_start_token(M),\n" + "end: my_end_token(M)"; @@ -442,8 +442,8 @@ //me_to_moz = parse(me_to_moz).print_to_string({ beautify: true }); //console.log(moz_to_me); - moz_to_me = new Function("my_start_token", "my_end_token", "from_moz", "return(" + moz_to_me + ")")( - my_start_token, my_end_token, from_moz + 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 diff --git a/lib/output.js b/lib/output.js index 1172bd58..03cda230 100644 --- a/lib/output.js +++ b/lib/output.js @@ -60,6 +60,7 @@ function OutputStream(options) { bracketize : false, semicolons : true, comments : false, + shebang : true, preserve_line : false, screw_ie8 : false, preamble : null, @@ -403,63 +404,69 @@ function OutputStream(options) { AST_Node.DEFMETHOD("add_comments", function(output){ var c = output.option("comments"), self = this; - if (c) { - var start = self.start; - if (start && !start._comments_dumped) { - start._comments_dumped = true; - var comments = start.comments_before || []; + var start = self.start; + if (start && !start._comments_dumped) { + start._comments_dumped = true; + var comments = start.comments_before || []; - // XXX: ugly fix for https://github.com/mishoo/UglifyJS2/issues/112 - // and https://github.com/mishoo/UglifyJS2/issues/372 - if (self instanceof AST_Exit && self.value) { - self.value.walk(new TreeWalker(function(node){ - if (node.start && node.start.comments_before) { - comments = comments.concat(node.start.comments_before); - node.start.comments_before = []; - } - if (node instanceof AST_Function || - node instanceof AST_Array || - node instanceof AST_Object) - { - return true; // don't go inside. - } - })); - } - - if (c.test) { - comments = comments.filter(function(comment){ - return c.test(comment.value); - }); - } else if (typeof c == "function") { - comments = comments.filter(function(comment){ - return c(self, comment); - }); - } - - // Keep single line comments after nlb, after nlb - if (!output.option("beautify") && comments.length > 0 && - /comment[134]/.test(comments[0].type) && - output.col() !== 0 && comments[0].nlb) - { - output.print("\n"); - } - - comments.forEach(function(c){ - if (/comment[134]/.test(c.type)) { - output.print("//" + c.value + "\n"); - output.indent(); + // XXX: ugly fix for https://github.com/mishoo/UglifyJS2/issues/112 + // and https://github.com/mishoo/UglifyJS2/issues/372 + if (self instanceof AST_Exit && self.value) { + self.value.walk(new TreeWalker(function(node){ + if (node.start && node.start.comments_before) { + comments = comments.concat(node.start.comments_before); + node.start.comments_before = []; } - else if (c.type == "comment2") { - output.print("/*" + c.value + "*/"); - if (start.nlb) { - output.print("\n"); - output.indent(); - } else { - output.space(); - } + if (node instanceof AST_Function || + node instanceof AST_Array || + node instanceof AST_Object) + { + return true; // don't go inside. } + })); + } + + if (!c) { + comments = comments.filter(function(comment) { + return comment.type == "comment5"; + }); + } else if (c.test) { + comments = comments.filter(function(comment){ + return c.test(comment.value) || comment.type == "comment5"; + }); + } else if (typeof c == "function") { + comments = comments.filter(function(comment){ + return c(self, comment) || comment.type == "comment5"; }); } + + // Keep single line comments after nlb, after nlb + if (!output.option("beautify") && comments.length > 0 && + /comment[134]/.test(comments[0].type) && + output.col() !== 0 && comments[0].nlb) + { + output.print("\n"); + } + + comments.forEach(function(c){ + if (/comment[134]/.test(c.type)) { + output.print("//" + c.value + "\n"); + output.indent(); + } + else if (c.type == "comment2") { + output.print("/*" + c.value + "*/"); + if (start.nlb) { + output.print("\n"); + output.indent(); + } else { + output.space(); + } + } + else if (output.pos() === 0 && c.type == "comment5" && output.option("shebang")) { + output.print("#!" + c.value + "\n"); + output.indent(); + } + }); } }); @@ -1240,7 +1247,13 @@ function OutputStream(options) { output.print_string(self.getValue(), self.quote); }); DEFPRINT(AST_Number, function(self, output){ - output.print(make_num(self.getValue())); + if (self.literal !== undefined + && +self.literal === self.value /* paranoid check */ + && self.scope && self.scope.has_directive('use asm')) { + output.print(self.literal); + } else { + output.print(make_num(self.getValue())); + } }); function regexp_safe_literal(code) { diff --git a/lib/parse.js b/lib/parse.js index bf80d5e2..5ae2a1db 100644 --- a/lib/parse.js +++ b/lib/parse.js @@ -216,7 +216,7 @@ function is_token(token, type, val) { var EX_EOF = {}; -function tokenizer($TEXT, filename, html5_comments) { +function tokenizer($TEXT, filename, html5_comments, shebang) { var S = { text : $TEXT, @@ -341,7 +341,11 @@ function tokenizer($TEXT, filename, html5_comments) { if (prefix) num = prefix + num; var valid = parse_js_number(num); if (!isNaN(valid)) { - return token("num", valid); + var tok = token("num", valid); + if (num.indexOf('.') >= 0) { + tok.literal = num; + } + return tok; } else { parse_error("Invalid syntax: " + num); } @@ -592,6 +596,13 @@ function tokenizer($TEXT, filename, html5_comments) { if (PUNC_CHARS(ch)) return token("punc", next()); if (OPERATOR_CHARS(ch)) return read_operator(); if (code == 92 || is_identifier_start(code)) return read_word(); + + if (shebang) { + if (S.pos == 0 && looking_at("#!")) { + forward(2); + return skip_line_comment("comment5"); + } + } parse_error("Unexpected character '" + ch + "'"); }; @@ -664,12 +675,13 @@ function parse($TEXT, options) { expression : false, html5_comments : true, bare_returns : false, + shebang : true, }); var S = { input : (typeof $TEXT == "string" ? tokenizer($TEXT, options.filename, - options.html5_comments) + options.html5_comments, options.shebang) : $TEXT), token : null, prev : null, @@ -1300,7 +1312,7 @@ function parse($TEXT, options) { ret = _make_symbol(AST_SymbolRef); break; case "num": - ret = new AST_Number({ start: tok, end: tok, value: tok.value }); + ret = new AST_Number({ start: tok, end: tok, value: tok.value, literal: tok.literal }); break; case "string": ret = new AST_String({ diff --git a/lib/scope.js b/lib/scope.js index 068daa60..caaba157 100644 --- a/lib/scope.js +++ b/lib/scope.js @@ -128,6 +128,10 @@ AST_Toplevel.DEFMETHOD("figure_out_scope", function(options){ push_uniq(scope.directives, node.value); return true; } + if (node instanceof AST_Number) { + node.scope = scope; + return true; + } if (node instanceof AST_With) { for (var s = scope; s; s = s.parent_scope) s.uses_with = true; diff --git a/npm-shrinkwrap.json b/npm-shrinkwrap.json new file mode 100644 index 00000000..760575c0 --- /dev/null +++ b/npm-shrinkwrap.json @@ -0,0 +1,128 @@ +{ + "name": "uglify-js", + "version": "2.4.24", + "dependencies": { + "abbrev": { + "version": "1.0.7", + "from": "abbrev@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.0.7.tgz" + }, + "amdefine": { + "version": "1.0.0", + "from": "amdefine@>=0.0.4", + "resolved": "https://registry.npmjs.org/amdefine/-/amdefine-1.0.0.tgz" + }, + "async": { + "version": "0.2.10", + "from": "async@>=0.2.6 <0.3.0", + "resolved": "https://registry.npmjs.org/async/-/async-0.2.10.tgz" + }, + "camelcase": { + "version": "1.2.1", + "from": "camelcase@>=1.0.2 <2.0.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-1.2.1.tgz" + }, + "decamelize": { + "version": "1.0.0", + "from": "decamelize@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.0.0.tgz" + }, + "deep-is": { + "version": "0.1.3", + "from": "deep-is@>=0.1.2 <0.2.0", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.3.tgz" + }, + "esprima": { + "version": "1.1.1", + "from": "esprima@>=1.1.1 <1.2.0", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-1.1.1.tgz" + }, + "estraverse": { + "version": "1.5.1", + "from": "estraverse@>=1.5.1 <1.6.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-1.5.1.tgz" + }, + "esutils": { + "version": "1.0.0", + "from": "esutils@>=1.0.0 <1.1.0", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-1.0.0.tgz" + }, + "fast-levenshtein": { + "version": "1.0.7", + "from": "fast-levenshtein@>=1.0.0 <1.1.0", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-1.0.7.tgz" + }, + "levn": { + "version": "0.2.5", + "from": "levn@>=0.2.5 <0.3.0", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.2.5.tgz" + }, + "nopt": { + "version": "2.1.2", + "from": "nopt@>=2.1.2 <2.2.0", + "resolved": "https://registry.npmjs.org/nopt/-/nopt-2.1.2.tgz" + }, + "optionator": { + "version": "0.5.0", + "from": "optionator@>=0.5.0 <0.6.0", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.5.0.tgz" + }, + "prelude-ls": { + "version": "1.1.2", + "from": "prelude-ls@>=1.1.1 <1.2.0", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz" + }, + "reflect": { + "version": "0.1.3", + "from": "git://github.com/zaach/reflect.js.git", + "resolved": "git://github.com/zaach/reflect.js.git#286bcd79661c96ecc404357d3c0e35fdb54a6967" + }, + "source-map": { + "version": "0.5.1", + "from": "source-map@>=0.5.1 <0.6.0", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.1.tgz" + }, + "type-check": { + "version": "0.3.1", + "from": "type-check@>=0.3.1 <0.4.0", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.1.tgz" + }, + "uglify-js": { + "version": "2.4.24", + "from": "git://github.com/mishoo/UglifyJS2.git", + "resolved": "git://github.com/mishoo/UglifyJS2.git#2a06c7758e24a64740473c8031eafbb7fefa213f", + "dependencies": { + "source-map": { + "version": "0.1.34", + "from": "source-map@0.1.34", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.1.34.tgz" + } + } + }, + "uglify-to-browserify": { + "version": "1.0.2", + "from": "uglify-to-browserify@>=1.0.0 <1.1.0", + "resolved": "https://registry.npmjs.org/uglify-to-browserify/-/uglify-to-browserify-1.0.2.tgz" + }, + "window-size": { + "version": "0.1.0", + "from": "window-size@0.1.0", + "resolved": "https://registry.npmjs.org/window-size/-/window-size-0.1.0.tgz" + }, + "wordwrap": { + "version": "0.0.2", + "from": "wordwrap@0.0.2", + "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-0.0.2.tgz" + }, + "yargs": { + "version": "3.5.4", + "from": "yargs@>=3.5.4 <3.6.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-3.5.4.tgz" + }, + "zeparser": { + "version": "0.0.7", + "from": "git://github.com/qfox/ZeParser.git", + "resolved": "git://github.com/qfox/ZeParser.git#c99240c5ba7054c467733800ff38265958a2dda9" + } + } +} diff --git a/package.json b/package.json index 0bf1de09..c172be39 100644 --- a/package.json +++ b/package.json @@ -3,8 +3,8 @@ "description": "JavaScript parser, mangler/compressor and beautifier toolkit", "homepage": "http://lisperator.net/uglifyjs", "author": "Mihai Bazon (http://lisperator.net/)", - "license": "BSD", - "version": "2.4.24", + "license": "BSD-2-Clause", + "version": "2.5.0", "engines": { "node": ">=0.8.0" }, @@ -30,7 +30,7 @@ ], "dependencies": { "async": "~0.2.6", - "source-map": "0.1.34", + "source-map": "~0.5.1", "uglify-to-browserify": "~1.0.0", "yargs": "~3.5.4" }, @@ -46,6 +46,7 @@ ] }, "scripts": { + "shrinkwrap": "rm ./npm-shrinkwrap.json; rm -rf ./node_modules; npm i && npm shrinkwrap && npm outdated", "test": "node test/run-tests.js" } } diff --git a/test/compress/asm.js b/test/compress/asm.js new file mode 100644 index 00000000..c3018485 --- /dev/null +++ b/test/compress/asm.js @@ -0,0 +1,106 @@ +asm_mixed: { + options = { + sequences : true, + properties : true, + dead_code : true, + drop_debugger : true, + conditionals : true, + comparisons : true, + evaluate : true, + booleans : true, + loops : true, + unused : true, + hoist_funs : true, + keep_fargs : true, + keep_fnames : false, + hoist_vars : true, + if_return : true, + join_vars : true, + cascade : true, + side_effects : true, + negate_iife : true + }; + input: { + // adapted from http://asmjs.org/spec/latest/ + function asm_GeometricMean(stdlib, foreign, buffer) { + "use asm"; + var exp = stdlib.Math.exp; + var log = stdlib.Math.log; + var values = new stdlib.Float64Array(buffer); + function logSum(start, end) { + start = start|0; + end = end|0; + var sum = 0.0, p = 0, q = 0; + // asm.js forces byte addressing of the heap by requiring shifting by 3 + for (p = start << 3, q = end << 3; (p|0) < (q|0); p = (p + 8)|0) { + sum = sum + +log(values[p>>3]); + } + return +sum; + } + function geometricMean(start, end) { + start = start|0; + end = end|0; + return +exp(+logSum(start, end) / +((end - start)|0)); + } + return { geometricMean: geometricMean }; + } + function no_asm_GeometricMean(stdlib, foreign, buffer) { + var exp = stdlib.Math.exp; + var log = stdlib.Math.log; + var values = new stdlib.Float64Array(buffer); + function logSum(start, end) { + start = start|0; + end = end|0; + var sum = 0.0, p = 0, q = 0; + // asm.js forces byte addressing of the heap by requiring shifting by 3 + for (p = start << 3, q = end << 3; (p|0) < (q|0); p = (p + 8)|0) { + sum = sum + +log(values[p>>3]); + } + return +sum; + } + function geometricMean(start, end) { + start = start|0; + end = end|0; + return +exp(+logSum(start, end) / +((end - start)|0)); + } + return { geometricMean: geometricMean }; + } + } + expect: { + function asm_GeometricMean(stdlib, foreign, buffer) { + "use asm"; + var exp = stdlib.Math.exp; + var log = stdlib.Math.log; + var values = new stdlib.Float64Array(buffer); + function logSum(start, end) { + start = start | 0; + end = end | 0; + var sum = 0.0, p = 0, q = 0; + for (p = start << 3, q = end << 3; (p | 0) < (q | 0); p = p + 8 | 0) { + sum = sum + +log(values[p >> 3]); + } + return +sum; + } + function geometricMean(start, end) { + start = start | 0; + end = end | 0; + return +exp(+logSum(start, end) / +(end - start | 0)); + } + return { geometricMean: geometricMean }; + } + function no_asm_GeometricMean(stdlib, foreign, buffer) { + function logSum(start, end) { + start = 0 | start, end = 0 | end; + var sum = 0, p = 0, q = 0; + for (p = start << 3, q = end << 3; (0 | q) > (0 | p); p = p + 8 | 0) sum += +log(values[p >> 3]); + return +sum; + } + function geometricMean(start, end) { + return start = 0 | start, end = 0 | end, +exp(+logSum(start, end) / +(end - start | 0)); + } + var exp = stdlib.Math.exp, log = stdlib.Math.log, values = new stdlib.Float64Array(buffer); + return { geometricMean: geometricMean }; + } + } +} + diff --git a/test/compress/drop-unused.js b/test/compress/drop-unused.js index 9bee9a2b..eebb81c6 100644 --- a/test/compress/drop-unused.js +++ b/test/compress/drop-unused.js @@ -1,5 +1,5 @@ unused_funarg_1: { - options = { unused: true, unsafe: true }; + options = { unused: true, keep_fargs: false }; input: { function f(a, b, c, d, e) { return a + b; @@ -13,7 +13,7 @@ unused_funarg_1: { } unused_funarg_2: { - options = { unused: true, unsafe: true }; + options = { unused: true, keep_fargs: false }; input: { function f(a, b, c, d, e) { return a + c; @@ -188,7 +188,7 @@ keep_fnames: { } expect: { function foo() { - return function bar() {}; + return function bar(baz) {}; } } } diff --git a/test/mozilla-ast.js b/test/mozilla-ast.js index 02628676..b5c6c6ed 100644 --- a/test/mozilla-ast.js +++ b/test/mozilla-ast.js @@ -100,4 +100,4 @@ module.exports = function(options) { } process.stdout.write(prefix + "Probability of error is less than " + (100 / options.iterations) + "%, stopping.\n"); -}; \ No newline at end of file +}; diff --git a/tools/exports.js b/tools/exports.js new file mode 100644 index 00000000..5007e03b --- /dev/null +++ b/tools/exports.js @@ -0,0 +1,17 @@ +exports["Compressor"] = Compressor; +exports["DefaultsError"] = DefaultsError; +exports["Dictionary"] = Dictionary; +exports["JS_Parse_Error"] = JS_Parse_Error; +exports["MAP"] = MAP; +exports["OutputStream"] = OutputStream; +exports["SourceMap"] = SourceMap; +exports["TreeTransformer"] = TreeTransformer; +exports["TreeWalker"] = TreeWalker; +exports["base54"] = base54; +exports["defaults"] = defaults; +exports["mangle_properties"] = mangle_properties; +exports["merge"] = merge; +exports["parse"] = parse; +exports["push_uniq"] = push_uniq; +exports["string_template"] = string_template; +exports["is_identifier"] = is_identifier; diff --git a/tools/node.js b/tools/node.js index eba2bc1d..7e61d2a1 100644 --- a/tools/node.js +++ b/tools/node.js @@ -1,26 +1,5 @@ var path = require("path"); var fs = require("fs"); -var vm = require("vm"); - -var UglifyJS = vm.createContext({ - console : console, - process : process, - Buffer : Buffer, - MOZ_SourceMap : require("source-map") -}); - -function load_global(file) { - file = path.resolve(path.dirname(module.filename), file); - try { - var code = fs.readFileSync(file, "utf8"); - return vm.runInContext(code, UglifyJS, file); - } catch(ex) { - // XXX: in case of a syntax error, the message is kinda - // useless. (no location information). - console.log("ERROR in file: " + file + " / " + ex); - process.exit(1); - } -}; var FILES = exports.FILES = [ "../lib/utils.js", @@ -32,24 +11,25 @@ var FILES = exports.FILES = [ "../lib/compress.js", "../lib/sourcemap.js", "../lib/mozilla-ast.js", - "../lib/propmangle.js" + "../lib/propmangle.js", + "./exports.js", ].map(function(file){ return fs.realpathSync(path.join(path.dirname(__filename), file)); }); -FILES.forEach(load_global); +var UglifyJS = exports; + +new Function("MOZ_SourceMap", "exports", FILES.map(function(file){ + return fs.readFileSync(file, "utf8"); +}).join("\n\n"))( + require("source-map"), + UglifyJS +); UglifyJS.AST_Node.warn_function = function(txt) { console.error("WARN: %s", txt); }; -// XXX: perhaps we shouldn't export everything but heck, I'm lazy. -for (var i in UglifyJS) { - if (UglifyJS.hasOwnProperty(i)) { - exports[i] = UglifyJS[i]; - } -} - exports.minify = function(files, options) { options = UglifyJS.defaults(options, { spidermonkey : false, @@ -65,6 +45,7 @@ exports.minify = function(files, options) { UglifyJS.base54.reset(); // 1. parse + var haveScope = false; var toplevel = null, sourcesContent = {}; @@ -93,6 +74,7 @@ exports.minify = function(files, options) { var compress = { warnings: options.warnings }; UglifyJS.merge(compress, options.compress); toplevel.figure_out_scope(); + haveScope = true; var sq = UglifyJS.Compressor(compress); toplevel = toplevel.transform(sq); } @@ -100,11 +82,17 @@ exports.minify = function(files, options) { // 3. mangle if (options.mangle) { toplevel.figure_out_scope(options.mangle); + haveScope = true; toplevel.compute_char_frequency(options.mangle); toplevel.mangle_names(options.mangle); } - // 4. output + // 4. scope (if needed) + if (!haveScope) { + toplevel.figure_out_scope(); + } + + // 5. output var inMap = options.inSourceMap; var output = {}; if (typeof options.inSourceMap == "string") {