Rework has_directive

It's now available during tree walking, i.e. walker.has_directive("use
asm"), rather than as part of the scope.  It's thus no longer necessary
to call `figure_out_scope` before codegen.  Added special bits in the
code generator to overcome the fact that it doesn't inherit from
TreeWalker.

Fix #861
This commit is contained in:
Mihai Bazon
2015-11-11 22:15:25 +02:00
parent 3c4346728e
commit 7691bebea5
8 changed files with 60 additions and 59 deletions

View File

@@ -409,14 +409,17 @@ async.eachLimit(files, 1, function (file, cb) {
writeNameCache("props", cache); writeNameCache("props", cache);
})(); })();
var SCOPE_IS_NEEDED = COMPRESS || MANGLE || ARGS.lint
var TL_CACHE = readNameCache("vars"); var TL_CACHE = readNameCache("vars");
time_it("scope", function(){ if (SCOPE_IS_NEEDED) {
TOPLEVEL.figure_out_scope({ screw_ie8: ARGS.screw_ie8, cache: TL_CACHE }); time_it("scope", function(){
if (ARGS.lint) { TOPLEVEL.figure_out_scope({ screw_ie8: ARGS.screw_ie8, cache: TL_CACHE });
TOPLEVEL.scope_warnings(); if (ARGS.lint) {
} TOPLEVEL.scope_warnings();
}); }
});
}
if (COMPRESS) { if (COMPRESS) {
time_it("squeeze", function(){ time_it("squeeze", function(){
@@ -424,12 +427,14 @@ async.eachLimit(files, 1, function (file, cb) {
}); });
} }
time_it("scope", function(){ if (SCOPE_IS_NEEDED) {
TOPLEVEL.figure_out_scope({ screw_ie8: ARGS.screw_ie8, cache: TL_CACHE }); time_it("scope", function(){
if (MANGLE && !TL_CACHE) { TOPLEVEL.figure_out_scope({ screw_ie8: ARGS.screw_ie8, cache: TL_CACHE });
TOPLEVEL.compute_char_frequency(MANGLE); if (MANGLE && !TL_CACHE) {
} TOPLEVEL.compute_char_frequency(MANGLE);
}); }
});
}
if (MANGLE) time_it("mangle", function(){ if (MANGLE) time_it("mangle", function(){
MANGLE.cache = TL_CACHE; MANGLE.cache = TL_CACHE;

View File

@@ -85,7 +85,7 @@ function DEFNODE(type, props, methods, base) {
return ctor; return ctor;
}; };
var AST_Token = DEFNODE("Token", "type value line col pos endline endcol endpos nlb comments_before file", { var AST_Token = DEFNODE("Token", "type value line col pos endline endcol endpos nlb comments_before file literal", {
}, null); }, null);
var AST_Node = DEFNODE("Node", "start end", { var AST_Node = DEFNODE("Node", "start end", {
@@ -927,27 +927,36 @@ var AST_True = DEFNODE("True", null, {
function TreeWalker(callback) { function TreeWalker(callback) {
this.visit = callback; this.visit = callback;
this.stack = []; this.stack = [];
this.directives = Object.create(null);
}; };
TreeWalker.prototype = { TreeWalker.prototype = {
_visit: function(node, descend) { _visit: function(node, descend) {
this.stack.push(node); this.push(node);
var ret = this.visit(node, descend ? function(){ var ret = this.visit(node, descend ? function(){
descend.call(node); descend.call(node);
} : noop); } : noop);
if (!ret && descend) { if (!ret && descend) {
descend.call(node); descend.call(node);
} }
this.stack.pop(); this.pop(node);
return ret; return ret;
}, },
parent: function(n) { parent: function(n) {
return this.stack[this.stack.length - 2 - (n || 0)]; return this.stack[this.stack.length - 2 - (n || 0)];
}, },
push: function (node) { push: function (node) {
if (node instanceof AST_Lambda) {
this.directives = Object.create(this.directives);
} else if (node instanceof AST_Directive) {
this.directives[node.value] = this.directives[node.value] ? "up" : true;
}
this.stack.push(node); this.stack.push(node);
}, },
pop: function() { pop: function(node) {
return this.stack.pop(); this.stack.pop();
if (node instanceof AST_Lambda) {
this.directives = Object.getPrototypeOf(this.directives);
}
}, },
self: function() { self: function() {
return this.stack[this.stack.length - 1]; return this.stack[this.stack.length - 1];
@@ -960,7 +969,16 @@ TreeWalker.prototype = {
} }
}, },
has_directive: function(type) { has_directive: function(type) {
return this.find_parent(AST_Scope).has_directive(type); var dir = this.directives[type];
if (dir) return dir;
var node = this.stack[this.stack.length - 1];
if (node instanceof AST_Scope) {
for (var i = 0; i < node.body.length; ++i) {
var st = node.body[i];
if (!(st instanceof AST_Directive)) break;
if (st.value == type) return true;
}
}
}, },
in_boolean_context: function() { in_boolean_context: function() {
var stack = this.stack; var stack = this.stack;

View File

@@ -991,7 +991,7 @@ merge(Compressor.prototype, {
/* -----[ optimizers ]----- */ /* -----[ optimizers ]----- */
OPT(AST_Directive, function(self, compressor){ OPT(AST_Directive, function(self, compressor){
if (self.scope.has_directive(self.value) !== self.scope) { if (compressor.has_directive(self.value) === "up") {
return make_node(AST_EmptyStatement, self); return make_node(AST_EmptyStatement, self);
} }
return self; return self;

View File

@@ -382,8 +382,13 @@ function OutputStream(options) {
nodetype.DEFMETHOD("_codegen", generator); nodetype.DEFMETHOD("_codegen", generator);
}; };
var use_asm = false;
AST_Node.DEFMETHOD("print", function(stream, force_parens){ AST_Node.DEFMETHOD("print", function(stream, force_parens){
var self = this, generator = self._codegen; var self = this, generator = self._codegen, prev_use_asm = use_asm;
if (self instanceof AST_Directive && self.value == "use asm") {
use_asm = true;
}
function doit() { function doit() {
self.add_comments(stream); self.add_comments(stream);
self.add_source_map(stream); self.add_source_map(stream);
@@ -396,6 +401,9 @@ function OutputStream(options) {
doit(); doit();
} }
stream.pop_node(); stream.pop_node();
if (self instanceof AST_Lambda) {
use_asm = prev_use_asm;
}
}); });
AST_Node.DEFMETHOD("print_to_string", function(options){ AST_Node.DEFMETHOD("print_to_string", function(options){
@@ -1170,10 +1178,8 @@ function OutputStream(options) {
output.print_string(self.getValue(), self.quote); output.print_string(self.getValue(), self.quote);
}); });
DEFPRINT(AST_Number, function(self, output){ DEFPRINT(AST_Number, function(self, output){
if (self.literal !== undefined if (use_asm) {
&& +self.literal === self.value /* paranoid check */ output.print(self.start.literal);
&& self.scope && self.scope.has_directive('use asm')) {
output.print(self.literal);
} else { } else {
output.print(make_num(self.getValue())); output.print(make_num(self.getValue()));
} }

View File

@@ -285,6 +285,9 @@ function tokenizer($TEXT, filename, html5_comments, shebang) {
nlb : S.newline_before, nlb : S.newline_before,
file : filename file : filename
}; };
if (/^(?:num|string|regexp)$/i.test(type)) {
ret.literal = $TEXT.substring(ret.pos, ret.endpos);
}
if (!is_comment) { if (!is_comment) {
ret.comments_before = S.comments_before; ret.comments_before = S.comments_before;
S.comments_before = []; S.comments_before = [];
@@ -335,11 +338,7 @@ function tokenizer($TEXT, filename, html5_comments, shebang) {
if (prefix) num = prefix + num; if (prefix) num = prefix + num;
var valid = parse_js_number(num); var valid = parse_js_number(num);
if (!isNaN(valid)) { if (!isNaN(valid)) {
var tok = token("num", valid); return token("num", valid);
if (num.indexOf('.') >= 0) {
tok.literal = num;
}
return tok;
} else { } else {
parse_error("Invalid syntax: " + num); parse_error("Invalid syntax: " + num);
} }
@@ -1152,7 +1151,7 @@ function parse($TEXT, options) {
ret = _make_symbol(AST_SymbolRef); ret = _make_symbol(AST_SymbolRef);
break; break;
case "num": case "num":
ret = new AST_Number({ start: tok, end: tok, value: tok.value, literal: tok.literal }); ret = new AST_Number({ start: tok, end: tok, value: tok.value });
break; break;
case "string": case "string":
ret = new AST_String({ ret = new AST_String({

View File

@@ -114,15 +114,6 @@ AST_Toplevel.DEFMETHOD("figure_out_scope", function(options){
defun = save_defun; defun = save_defun;
return true; // don't descend again in TreeWalker return true; // don't descend again in TreeWalker
} }
if (node instanceof AST_Directive) {
node.scope = scope;
push_uniq(scope.directives, node.value);
return true;
}
if (node instanceof AST_Number) {
node.scope = scope;
return true;
}
if (node instanceof AST_With) { if (node instanceof AST_With) {
for (var s = scope; s; s = s.parent_scope) for (var s = scope; s; s = s.parent_scope)
s.uses_with = true; s.uses_with = true;
@@ -202,7 +193,6 @@ AST_Toplevel.DEFMETHOD("figure_out_scope", function(options){
}); });
AST_Scope.DEFMETHOD("init_scope_vars", function(nesting){ AST_Scope.DEFMETHOD("init_scope_vars", function(nesting){
this.directives = []; // contains the directives defined in this scope, i.e. "use strict"
this.variables = new Dictionary(); // map name to AST_SymbolVar (variables defined in this scope; includes functions) this.variables = new Dictionary(); // map name to AST_SymbolVar (variables defined in this scope; includes functions)
this.functions = new Dictionary(); // map name to AST_SymbolDefun (functions defined in this scope) this.functions = new Dictionary(); // map name to AST_SymbolDefun (functions defined in this scope)
this.uses_with = false; // will be set to true if this or some nested scope uses the `with` statement this.uses_with = false; // will be set to true if this or some nested scope uses the `with` statement
@@ -213,10 +203,6 @@ AST_Scope.DEFMETHOD("init_scope_vars", function(nesting){
this.nesting = nesting; // the nesting level of this scope (0 means toplevel) this.nesting = nesting; // the nesting level of this scope (0 means toplevel)
}); });
AST_Scope.DEFMETHOD("strict", function(){
return this.has_directive("use strict");
});
AST_Lambda.DEFMETHOD("init_scope_vars", function(){ AST_Lambda.DEFMETHOD("init_scope_vars", function(){
AST_Scope.prototype.init_scope_vars.apply(this, arguments); AST_Scope.prototype.init_scope_vars.apply(this, arguments);
this.uses_arguments = false; this.uses_arguments = false;
@@ -240,11 +226,6 @@ AST_Scope.DEFMETHOD("find_variable", function(name){
|| (this.parent_scope && this.parent_scope.find_variable(name)); || (this.parent_scope && this.parent_scope.find_variable(name));
}); });
AST_Scope.DEFMETHOD("has_directive", function(value){
return this.parent_scope && this.parent_scope.has_directive(value)
|| (this.directives.indexOf(value) >= 0 ? this : null);
});
AST_Scope.DEFMETHOD("def_function", function(symbol){ AST_Scope.DEFMETHOD("def_function", function(symbol){
this.functions.set(symbol.name, this.def_variable(symbol)); this.functions.set(symbol.name, this.def_variable(symbol));
}); });

View File

@@ -70,7 +70,7 @@ TreeTransformer.prototype = new TreeWalker;
if (y !== undefined) x = y; if (y !== undefined) x = y;
} }
} }
tw.pop(); tw.pop(this);
return x; return x;
}); });
}; };

View File

@@ -45,7 +45,6 @@ exports.minify = function(files, options) {
UglifyJS.base54.reset(); UglifyJS.base54.reset();
// 1. parse // 1. parse
var haveScope = false;
var toplevel = null, var toplevel = null,
sourcesContent = {}; sourcesContent = {};
@@ -74,7 +73,6 @@ exports.minify = function(files, options) {
var compress = { warnings: options.warnings }; var compress = { warnings: options.warnings };
UglifyJS.merge(compress, options.compress); UglifyJS.merge(compress, options.compress);
toplevel.figure_out_scope(); toplevel.figure_out_scope();
haveScope = true;
var sq = UglifyJS.Compressor(compress); var sq = UglifyJS.Compressor(compress);
toplevel = toplevel.transform(sq); toplevel = toplevel.transform(sq);
} }
@@ -82,17 +80,11 @@ exports.minify = function(files, options) {
// 3. mangle // 3. mangle
if (options.mangle) { if (options.mangle) {
toplevel.figure_out_scope(options.mangle); toplevel.figure_out_scope(options.mangle);
haveScope = true;
toplevel.compute_char_frequency(options.mangle); toplevel.compute_char_frequency(options.mangle);
toplevel.mangle_names(options.mangle); toplevel.mangle_names(options.mangle);
} }
// 4. scope (if needed) // 4. output
if (!haveScope) {
toplevel.figure_out_scope();
}
// 5. output
var inMap = options.inSourceMap; var inMap = options.inSourceMap;
var output = {}; var output = {};
if (typeof options.inSourceMap == "string") { if (typeof options.inSourceMap == "string") {