aboutsummaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
authoralexlamsl <alexlamsl@gmail.com>2017-02-18 19:27:31 +0800
committeralexlamsl <alexlamsl@gmail.com>2017-02-21 13:29:58 +0800
commite275148998638bdcf795257ed03941ca34e33018 (patch)
treec9019dec5f4a26a2ecfa47161cada4deff1c1bf7 /lib
parent974247c8c0e57901ef776e86784c8c9a1b87b5de (diff)
downloadtracifyjs-e275148998638bdcf795257ed03941ca34e33018.tar.gz
tracifyjs-e275148998638bdcf795257ed03941ca34e33018.zip
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
Diffstat (limited to 'lib')
-rw-r--r--lib/compress.js114
-rw-r--r--lib/scope.js22
2 files changed, 102 insertions, 34 deletions
diff --git a/lib/compress.js b/lib/compress.js
index a60ba1a1..cb99a173 100644
--- a/lib/compress.js
+++ b/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;
+ });
+
})();
diff --git a/lib/scope.js b/lib/scope.js
index 6ad12616..b0d92d7d 100644
--- a/lib/scope.js
+++ b/lib/scope.js
@@ -206,14 +206,7 @@ AST_Toplevel.DEFMETHOD("figure_out_scope", function(options){
node.scope.uses_arguments = true;
}
if (!sym) {
- if (globals.has(name)) {
- sym = globals.get(name);
- } else {
- sym = new SymbolDef(self, globals.size(), node);
- sym.undeclared = true;
- sym.global = true;
- globals.set(name, sym);
- }
+ sym = self.def_global(node);
}
node.thedef = sym;
node.reference(options);
@@ -227,6 +220,19 @@ AST_Toplevel.DEFMETHOD("figure_out_scope", function(options){
}
});
+AST_Toplevel.DEFMETHOD("def_global", function(node){
+ var globals = this.globals, name = node.name;
+ if (globals.has(name)) {
+ return globals.get(name);
+ } else {
+ var g = new SymbolDef(this, globals.size(), node);
+ g.undeclared = true;
+ g.global = true;
+ globals.set(name, g);
+ return g;
+ }
+});
+
AST_Scope.DEFMETHOD("init_scope_vars", function(){
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)