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:
alexlamsl
2017-02-18 19:27:31 +08:00
parent 974247c8c0
commit e275148998
8 changed files with 324 additions and 34 deletions

View File

@@ -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;
});
})();