Compare commits

...

10 Commits

Author SHA1 Message Date
Mihai Bazon
d2190c2bf3 Properly scope catch identifier when --screw-ie8
Fix #344
2013-11-28 16:43:30 +02:00
Mihai Bazon
ea10642572 v2.4.6, because npm is foobar 2013-11-28 15:05:32 +02:00
Mihai Bazon
547561a568 v2.4.5 2013-11-28 13:15:27 +02:00
Mihai Bazon
c16d538ce7 Add --noerr to turn off argument name checking
for now only used for keys passed to `-c` or `-b`.
2013-11-28 13:15:01 +02:00
Mihai Bazon
73d082df2e v2.4.4 2013-11-27 14:24:26 +02:00
Mihai Bazon
50b8d7272c Fix faulty compression
`String(x + 5)` is not always the same as `x + "5"`.  Overlooked that. :-(

Close #350
2013-11-20 21:13:16 +02:00
Mihai Bazon
7d11b96f48 Only descend twice after drop_unused if it's the same node type.
Fix #345
2013-11-08 11:57:17 +02:00
Mihai Bazon
eab99a1c3d Better fix for #343
We can in fact lift sequences, but only if the operation is assignment and
the left-hand side has no side effects nor property access -- that should
guarantee that whatever we place before it cannot affect the sense of the
assignment.

Dropped contrived test case (too hard to support it now), added a more
meaningful one.
2013-11-06 10:48:48 +02:00
Mihai Bazon
19e2fb134d v2.4.3 2013-11-06 10:21:29 +02:00
Mihai Bazon
f4919e3a25 Do not lift sequence from right-hand side of binary operation. Fix #343 2013-11-06 10:18:28 +02:00
8 changed files with 53 additions and 52 deletions

View File

@@ -62,6 +62,7 @@ You need to pass an argument to this option to specify the name that your module
.describe("lint", "Display some scope warnings")
.describe("v", "Verbose")
.describe("V", "Print version number and exit.")
.describe("noerr", "Don't throw an error for unknown options in -c, -b or -m.")
.alias("p", "prefix")
.alias("o", "output")
@@ -96,6 +97,7 @@ You need to pass an argument to this option to specify the name that your module
.boolean("spidermonkey")
.boolean("lint")
.boolean("V")
.boolean("noerr")
.wrap(80)
@@ -104,6 +106,12 @@ You need to pass an argument to this option to specify the name that your module
normalize(ARGS);
if (ARGS.noerr) {
UglifyJS.DefaultsError.croak = function(msg, defs) {
sys.error("WARN: " + msg);
};
}
if (ARGS.version || ARGS.V) {
var json = require("../package.json");
sys.puts(json.name + ' ' + json.version);

View File

@@ -498,12 +498,6 @@ var AST_Try = DEFNODE("Try", "bcatch bfinally", {
}
}, AST_Block);
// XXX: this is wrong according to ECMA-262 (12.4). the catch block
// should introduce another scope, as the argname should be visible
// only inside the catch block. However, doing it this way because of
// IE which simply introduces the name in the surrounding scope. If
// we ever want to fix this then AST_Catch should inherit from
// AST_Scope.
var AST_Catch = DEFNODE("Catch", "argname", {
$documentation: "A `catch` node; only makes sense as part of a `try` statement",
$propdoc: {
@@ -515,7 +509,7 @@ var AST_Catch = DEFNODE("Catch", "argname", {
walk_body(this, visitor);
});
}
}, AST_Block);
}, AST_Scope);
var AST_Finally = DEFNODE("Finally", null, {
$documentation: "A `finally` node; only makes sense as part of a `try` statement"

View File

@@ -85,13 +85,14 @@ merge(Compressor.prototype, {
},
before: function(node, descend, in_list) {
if (node._squeezed) return node;
var was_scope = false;
if (node instanceof AST_Scope) {
//node.drop_unused(this);
node = node.hoist_declarations(this);
was_scope = true;
}
descend(node, this);
node = node.optimize(this);
if (node instanceof AST_Scope) {
if (was_scope && node instanceof AST_Scope) {
node.drop_unused(this);
descend(node, this);
}
@@ -1668,10 +1669,10 @@ merge(Compressor.prototype, {
return arg.value;
}).join(",") + "){" + self.args[self.args.length - 1].value + "})()";
var ast = parse(code);
ast.figure_out_scope();
ast.figure_out_scope({ screw_ie8: compressor.option("screw_ie8") });
var comp = new Compressor(compressor.options);
ast = ast.transform(comp);
ast.figure_out_scope();
ast.figure_out_scope({ screw_ie8: compressor.option("screw_ie8") });
ast.mangle_names();
var fun;
try {
@@ -1865,6 +1866,14 @@ merge(Compressor.prototype, {
return self.evaluate(compressor)[0];
});
function has_side_effects_or_prop_access(node, compressor) {
var save_pure_getters = compressor.option("pure_getters");
compressor.options.pure_getters = false;
var ret = node.has_side_effects(compressor);
compressor.options.pure_getters = save_pure_getters;
return ret;
}
AST_Binary.DEFMETHOD("lift_sequences", function(compressor){
if (compressor.option("sequences")) {
if (this.left instanceof AST_Seq) {
@@ -1876,8 +1885,8 @@ merge(Compressor.prototype, {
return seq;
}
if (this.right instanceof AST_Seq
&& !(this.operator == "||" || this.operator == "&&")
&& !this.left.has_side_effects(compressor)) {
&& this instanceof AST_Assign
&& !has_side_effects_or_prop_access(this.left, compressor)) {
var seq = this.right;
var x = seq.to_array();
this.right = x.pop();
@@ -2022,16 +2031,6 @@ merge(Compressor.prototype, {
&& self.right.getValue() === "" && self.left instanceof AST_Binary
&& self.left.operator == "+" && self.left.is_string(compressor)) {
return self.left;
} else if (self.operator == "+" && self.right instanceof AST_String
&& self.right.getValue() === "" && self.left instanceof AST_Binary
&& self.left.operator == "+" && self.left.right instanceof AST_Number) {
return make_node(AST_Binary, self, {
left: self.left.left,
operator: "+",
right: make_node(AST_String, self.right, {
value: String(self.left.right.value)
})
});
}
if (compressor.option("evaluate")) {
if (self.operator == "+") {

View File

@@ -71,27 +71,28 @@ SymbolDef.prototype = {
}
};
AST_Toplevel.DEFMETHOD("figure_out_scope", function(){
// This does what ast_add_scope did in UglifyJS v1.
//
// Part of it could be done at parse time, but it would complicate
// the parser (and it's already kinda complex). It's also worth
// having it separated because we might need to call it multiple
// times on the same tree.
AST_Toplevel.DEFMETHOD("figure_out_scope", function(options){
options = defaults(options, {
screw_ie8: false
});
// pass 1: setup scope chaining and handle definitions
var self = this;
var scope = self.parent_scope = null;
var defun = null;
var nesting = 0;
var tw = new TreeWalker(function(node, descend){
if (node instanceof AST_Scope) {
node.init_scope_vars(nesting);
var save_scope = node.parent_scope = scope;
++nesting;
var save_defun = defun;
if (!(node instanceof AST_Catch)) {
defun = node;
}
scope = node;
descend();
++nesting; descend(); --nesting;
scope = save_scope;
--nesting;
defun = save_defun;
return true; // don't descend again in TreeWalker
}
if (node instanceof AST_Directive) {
@@ -108,7 +109,7 @@ AST_Toplevel.DEFMETHOD("figure_out_scope", function(){
node.scope = scope;
}
if (node instanceof AST_SymbolLambda) {
scope.def_function(node);
defun.def_function(node);
}
else if (node instanceof AST_SymbolDefun) {
// Careful here, the scope where this should be defined is
@@ -116,22 +117,17 @@ AST_Toplevel.DEFMETHOD("figure_out_scope", function(){
// scope when we encounter the AST_Defun node (which is
// instanceof AST_Scope) but we get to the symbol a bit
// later.
(node.scope = scope.parent_scope).def_function(node);
(node.scope = defun.parent_scope).def_function(node);
}
else if (node instanceof AST_SymbolVar
|| node instanceof AST_SymbolConst) {
var def = scope.def_variable(node);
var def = defun.def_variable(node);
def.constant = node instanceof AST_SymbolConst;
def.init = tw.parent().value;
}
else if (node instanceof AST_SymbolCatch) {
// XXX: this is wrong according to ECMA-262 (12.4). the
// `catch` argument name should be visible only inside the
// catch block. For a quick fix AST_Catch should inherit
// from AST_Scope. Keeping it this way because of IE,
// which doesn't obey the standard. (it introduces the
// identifier in the enclosing scope)
scope.def_variable(node);
(options.screw_ie8 ? scope : defun)
.def_variable(node);
}
});
self.walk(tw);

View File

@@ -86,12 +86,16 @@ function DefaultsError(msg, defs) {
this.defs = defs;
};
DefaultsError.croak = function(msg, defs) {
throw new DefaultsError(msg, defs);
};
function defaults(args, defs, croak) {
if (args === true)
args = {};
var ret = args || {};
if (croak) for (var i in ret) if (ret.hasOwnProperty(i) && !defs.hasOwnProperty(i))
throw new DefaultsError("`" + i + "` is not a supported option", defs);
DefaultsError.croak("`" + i + "` is not a supported option", defs);
for (var i in defs) if (defs.hasOwnProperty(i)) {
ret[i] = (args && args.hasOwnProperty(i)) ? args[i] : defs[i];
}

View File

@@ -3,7 +3,7 @@
"description": "JavaScript parser, mangler/compressor and beautifier toolkit",
"homepage": "http://lisperator.net/uglifyjs",
"main": "tools/node.js",
"version": "2.4.2",
"version": "2.4.6",
"engines": { "node" : ">=0.4.0" },
"maintainers": [{
"name": "Mihai Bazon",

View File

@@ -54,15 +54,13 @@ strings_concat: {
input: {
f(
String(x + 'str'),
String('str' + x),
String(x + 5)
String('str' + x)
);
}
expect: {
f(
x + 'str',
'str' + x,
x + '5'
'str' + x
);
}
}
}

View File

@@ -101,10 +101,12 @@ lift_sequences_1: {
lift_sequences_2: {
options = { sequences: true, evaluate: true };
input: {
q = 1 + (foo(), bar(), 5) + 7 * (5 / (3 - (a(), (QW=ER), c(), 2))) - (x(), y(), 5);
foo.x = (foo = {}, 10);
bar = (bar = {}, 10);
}
expect: {
foo(), bar(), a(), QW = ER, c(), x(), y(), q = 36
foo.x = (foo = {}, 10),
bar = {}, bar = 10;
}
}