enhance global_defs
- support arrays, objects & AST_Node - support `"a.b":1` on both cli & API - emit warning if variable is modified - override top-level variables fixes #1416 closes #1198 closes #1469
This commit is contained in:
114
lib/compress.js
114
lib/compress.js
@@ -219,17 +219,6 @@ merge(Compressor.prototype, {
|
||||
};
|
||||
|
||||
function make_node_from_constant(compressor, val, orig) {
|
||||
// XXX: WIP.
|
||||
// if (val instanceof AST_Node) return val.transform(new TreeTransformer(null, function(node){
|
||||
// if (node instanceof AST_SymbolRef) {
|
||||
// var scope = compressor.find_parent(AST_Scope);
|
||||
// var def = scope.find_variable(node);
|
||||
// node.thedef = def;
|
||||
// return node;
|
||||
// }
|
||||
// })).transform(compressor);
|
||||
|
||||
if (val instanceof AST_Node) return val.transform(compressor);
|
||||
switch (typeof val) {
|
||||
case "string":
|
||||
return make_node(AST_String, orig, {
|
||||
@@ -991,6 +980,68 @@ merge(Compressor.prototype, {
|
||||
|| parent instanceof AST_Assign && parent.left === node;
|
||||
}
|
||||
|
||||
(function (def){
|
||||
AST_Node.DEFMETHOD("resolve_defines", function(compressor) {
|
||||
if (!compressor.option("global_defs")) return;
|
||||
var def = this._find_defs(compressor, "");
|
||||
if (def) {
|
||||
var node, parent = this, level = 0;
|
||||
do {
|
||||
node = parent;
|
||||
parent = compressor.parent(level++);
|
||||
} while (parent instanceof AST_PropAccess && parent.expression === node);
|
||||
if (isLHS(node, parent)) {
|
||||
compressor.warn('global_defs ' + this.print_to_string() + ' redefined [{file}:{line},{col}]', this.start);
|
||||
} else {
|
||||
return def;
|
||||
}
|
||||
}
|
||||
});
|
||||
function to_node(compressor, value, orig) {
|
||||
if (value instanceof AST_Node) return make_node(value.CTOR, orig, value);
|
||||
if (Array.isArray(value)) return make_node(AST_Array, orig, {
|
||||
elements: value.map(function(value) {
|
||||
return to_node(compressor, value, orig);
|
||||
})
|
||||
});
|
||||
if (value && typeof value == "object") {
|
||||
var props = [];
|
||||
for (var key in value) {
|
||||
props.push(make_node(AST_ObjectKeyVal, orig, {
|
||||
key: key,
|
||||
value: to_node(compressor, value[key], orig)
|
||||
}));
|
||||
}
|
||||
return make_node(AST_Object, orig, {
|
||||
properties: props
|
||||
});
|
||||
}
|
||||
return make_node_from_constant(compressor, value, orig);
|
||||
}
|
||||
def(AST_Node, noop);
|
||||
def(AST_Dot, function(compressor, suffix){
|
||||
return this.expression._find_defs(compressor, suffix + "." + this.property);
|
||||
});
|
||||
def(AST_SymbolRef, function(compressor, suffix){
|
||||
if (!this.global()) return;
|
||||
var name;
|
||||
var defines = compressor.option("global_defs");
|
||||
if (defines && HOP(defines, (name = this.name + suffix))) {
|
||||
var node = to_node(compressor, defines[name], this);
|
||||
var top = compressor.find_parent(AST_Toplevel);
|
||||
node.walk(new TreeWalker(function(node) {
|
||||
if (node instanceof AST_SymbolRef) {
|
||||
node.scope = top;
|
||||
node.thedef = top.def_global(node);
|
||||
}
|
||||
}));
|
||||
return node;
|
||||
}
|
||||
});
|
||||
})(function(node, func){
|
||||
node.DEFMETHOD("_find_defs", func);
|
||||
});
|
||||
|
||||
function best_of(ast1, ast2) {
|
||||
return ast1.print_to_string().length >
|
||||
ast2.print_to_string().length
|
||||
@@ -2793,21 +2844,20 @@ merge(Compressor.prototype, {
|
||||
});
|
||||
|
||||
OPT(AST_SymbolRef, function(self, compressor){
|
||||
if (self.undeclared() && !isLHS(self, compressor.parent())) {
|
||||
var defines = compressor.option("global_defs");
|
||||
if (defines && HOP(defines, self.name)) {
|
||||
return make_node_from_constant(compressor, defines[self.name], self);
|
||||
}
|
||||
// testing against !self.scope.uses_with first is an optimization
|
||||
if (!self.scope.uses_with || !compressor.find_parent(AST_With)) {
|
||||
switch (self.name) {
|
||||
case "undefined":
|
||||
return make_node(AST_Undefined, self);
|
||||
case "NaN":
|
||||
return make_node(AST_NaN, self).transform(compressor);
|
||||
case "Infinity":
|
||||
return make_node(AST_Infinity, self).transform(compressor);
|
||||
}
|
||||
var def = self.resolve_defines(compressor);
|
||||
if (def) {
|
||||
return def;
|
||||
}
|
||||
// testing against !self.scope.uses_with first is an optimization
|
||||
if (self.undeclared() && !isLHS(self, compressor.parent())
|
||||
&& (!self.scope.uses_with || !compressor.find_parent(AST_With))) {
|
||||
switch (self.name) {
|
||||
case "undefined":
|
||||
return make_node(AST_Undefined, self);
|
||||
case "NaN":
|
||||
return make_node(AST_NaN, self).transform(compressor);
|
||||
case "Infinity":
|
||||
return make_node(AST_Infinity, self).transform(compressor);
|
||||
}
|
||||
}
|
||||
if (compressor.option("evaluate") && !isLHS(self, compressor.parent())) {
|
||||
@@ -3085,6 +3135,10 @@ merge(Compressor.prototype, {
|
||||
});
|
||||
|
||||
OPT(AST_Dot, function(self, compressor){
|
||||
var def = self.resolve_defines(compressor);
|
||||
if (def) {
|
||||
return def;
|
||||
}
|
||||
var prop = self.property;
|
||||
if (RESERVED_WORDS(prop) && !compressor.option("screw_ie8")) {
|
||||
return make_node(AST_Sub, self, {
|
||||
@@ -3114,4 +3168,12 @@ merge(Compressor.prototype, {
|
||||
return self;
|
||||
});
|
||||
|
||||
OPT(AST_VarDef, function(self, compressor){
|
||||
var defines = compressor.option("global_defs");
|
||||
if (defines && HOP(defines, self.name.name)) {
|
||||
compressor.warn('global_defs ' + self.name.name + ' redefined [{file}:{line},{col}]', self.start);
|
||||
}
|
||||
return self;
|
||||
});
|
||||
|
||||
})();
|
||||
|
||||
Reference in New Issue
Block a user