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:
@@ -409,14 +409,17 @@ 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();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (COMPRESS) {
|
||||
time_it("squeeze", function(){
|
||||
@@ -424,12 +427,14 @@ 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);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (MANGLE) time_it("mangle", function(){
|
||||
MANGLE.cache = TL_CACHE;
|
||||
|
||||
30
lib/ast.js
30
lib/ast.js
@@ -85,7 +85,7 @@ function DEFNODE(type, props, methods, base) {
|
||||
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);
|
||||
|
||||
var AST_Node = DEFNODE("Node", "start end", {
|
||||
@@ -927,27 +927,36 @@ var AST_True = DEFNODE("True", null, {
|
||||
function TreeWalker(callback) {
|
||||
this.visit = callback;
|
||||
this.stack = [];
|
||||
this.directives = Object.create(null);
|
||||
};
|
||||
TreeWalker.prototype = {
|
||||
_visit: function(node, descend) {
|
||||
this.stack.push(node);
|
||||
this.push(node);
|
||||
var ret = this.visit(node, descend ? function(){
|
||||
descend.call(node);
|
||||
} : noop);
|
||||
if (!ret && descend) {
|
||||
descend.call(node);
|
||||
}
|
||||
this.stack.pop();
|
||||
this.pop(node);
|
||||
return ret;
|
||||
},
|
||||
parent: function(n) {
|
||||
return this.stack[this.stack.length - 2 - (n || 0)];
|
||||
},
|
||||
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);
|
||||
},
|
||||
pop: function() {
|
||||
return this.stack.pop();
|
||||
pop: function(node) {
|
||||
this.stack.pop();
|
||||
if (node instanceof AST_Lambda) {
|
||||
this.directives = Object.getPrototypeOf(this.directives);
|
||||
}
|
||||
},
|
||||
self: function() {
|
||||
return this.stack[this.stack.length - 1];
|
||||
@@ -960,7 +969,16 @@ TreeWalker.prototype = {
|
||||
}
|
||||
},
|
||||
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() {
|
||||
var stack = this.stack;
|
||||
|
||||
@@ -991,7 +991,7 @@ merge(Compressor.prototype, {
|
||||
/* -----[ optimizers ]----- */
|
||||
|
||||
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 self;
|
||||
|
||||
@@ -382,8 +382,13 @@ function OutputStream(options) {
|
||||
nodetype.DEFMETHOD("_codegen", generator);
|
||||
};
|
||||
|
||||
var use_asm = false;
|
||||
|
||||
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() {
|
||||
self.add_comments(stream);
|
||||
self.add_source_map(stream);
|
||||
@@ -396,6 +401,9 @@ function OutputStream(options) {
|
||||
doit();
|
||||
}
|
||||
stream.pop_node();
|
||||
if (self instanceof AST_Lambda) {
|
||||
use_asm = prev_use_asm;
|
||||
}
|
||||
});
|
||||
|
||||
AST_Node.DEFMETHOD("print_to_string", function(options){
|
||||
@@ -1170,10 +1178,8 @@ function OutputStream(options) {
|
||||
output.print_string(self.getValue(), self.quote);
|
||||
});
|
||||
DEFPRINT(AST_Number, function(self, output){
|
||||
if (self.literal !== undefined
|
||||
&& +self.literal === self.value /* paranoid check */
|
||||
&& self.scope && self.scope.has_directive('use asm')) {
|
||||
output.print(self.literal);
|
||||
if (use_asm) {
|
||||
output.print(self.start.literal);
|
||||
} else {
|
||||
output.print(make_num(self.getValue()));
|
||||
}
|
||||
|
||||
11
lib/parse.js
11
lib/parse.js
@@ -285,6 +285,9 @@ function tokenizer($TEXT, filename, html5_comments, shebang) {
|
||||
nlb : S.newline_before,
|
||||
file : filename
|
||||
};
|
||||
if (/^(?:num|string|regexp)$/i.test(type)) {
|
||||
ret.literal = $TEXT.substring(ret.pos, ret.endpos);
|
||||
}
|
||||
if (!is_comment) {
|
||||
ret.comments_before = S.comments_before;
|
||||
S.comments_before = [];
|
||||
@@ -335,11 +338,7 @@ function tokenizer($TEXT, filename, html5_comments, shebang) {
|
||||
if (prefix) num = prefix + num;
|
||||
var valid = parse_js_number(num);
|
||||
if (!isNaN(valid)) {
|
||||
var tok = token("num", valid);
|
||||
if (num.indexOf('.') >= 0) {
|
||||
tok.literal = num;
|
||||
}
|
||||
return tok;
|
||||
return token("num", valid);
|
||||
} else {
|
||||
parse_error("Invalid syntax: " + num);
|
||||
}
|
||||
@@ -1152,7 +1151,7 @@ function parse($TEXT, options) {
|
||||
ret = _make_symbol(AST_SymbolRef);
|
||||
break;
|
||||
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;
|
||||
case "string":
|
||||
ret = new AST_String({
|
||||
|
||||
19
lib/scope.js
19
lib/scope.js
@@ -114,15 +114,6 @@ AST_Toplevel.DEFMETHOD("figure_out_scope", function(options){
|
||||
defun = save_defun;
|
||||
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) {
|
||||
for (var s = scope; s; s = s.parent_scope)
|
||||
s.uses_with = true;
|
||||
@@ -202,7 +193,6 @@ AST_Toplevel.DEFMETHOD("figure_out_scope", function(options){
|
||||
});
|
||||
|
||||
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.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
|
||||
@@ -213,10 +203,6 @@ AST_Scope.DEFMETHOD("init_scope_vars", function(nesting){
|
||||
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_Scope.prototype.init_scope_vars.apply(this, arguments);
|
||||
this.uses_arguments = false;
|
||||
@@ -240,11 +226,6 @@ AST_Scope.DEFMETHOD("find_variable", function(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){
|
||||
this.functions.set(symbol.name, this.def_variable(symbol));
|
||||
});
|
||||
|
||||
@@ -70,7 +70,7 @@ TreeTransformer.prototype = new TreeWalker;
|
||||
if (y !== undefined) x = y;
|
||||
}
|
||||
}
|
||||
tw.pop();
|
||||
tw.pop(this);
|
||||
return x;
|
||||
});
|
||||
};
|
||||
|
||||
@@ -45,7 +45,6 @@ exports.minify = function(files, options) {
|
||||
UglifyJS.base54.reset();
|
||||
|
||||
// 1. parse
|
||||
var haveScope = false;
|
||||
var toplevel = null,
|
||||
sourcesContent = {};
|
||||
|
||||
@@ -74,7 +73,6 @@ 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);
|
||||
}
|
||||
@@ -82,17 +80,11 @@ 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. scope (if needed)
|
||||
if (!haveScope) {
|
||||
toplevel.figure_out_scope();
|
||||
}
|
||||
|
||||
// 5. output
|
||||
// 4. output
|
||||
var inMap = options.inSourceMap;
|
||||
var output = {};
|
||||
if (typeof options.inSourceMap == "string") {
|
||||
|
||||
Reference in New Issue
Block a user