};
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, {
|| 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
});
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())) {
});
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, {
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;
+ });
+
})();
--- /dev/null
+must_replace: {
+ options = {
+ global_defs: {
+ D: "foo bar",
+ }
+ }
+ input: {
+ console.log(D);
+ }
+ expect: {
+ console.log("foo bar");
+ }
+}
+
+keyword: {
+ options = {
+ global_defs: {
+ undefined: 0,
+ NaN: 1,
+ Infinity: 2,
+ },
+ }
+ input: {
+ console.log(undefined, NaN, Infinity);
+ }
+ expect: {
+ console.log(0, 1, 2);
+ }
+}
+
+object: {
+ options = {
+ evaluate: true,
+ global_defs: {
+ CONFIG: {
+ DEBUG: [ 0 ],
+ VALUE: 42,
+ },
+ },
+ unsafe: true,
+ }
+ input: {
+ function f(CONFIG) {
+ // CONFIG not global - do not replace
+ return CONFIG.VALUE;
+ }
+ function g() {
+ var CONFIG = { VALUE: 1 };
+ // CONFIG not global - do not replace
+ return CONFIG.VALUE;
+ }
+ function h() {
+ return CONFIG.VALUE;
+ }
+ if (CONFIG.DEBUG[0])
+ console.debug("foo");
+ }
+ expect: {
+ function f(CONFIG) {
+ return CONFIG.VALUE;
+ }
+ function g() {
+ var CONFIG = { VALUE: 1 };
+ return CONFIG.VALUE;
+ }
+ function h() {
+ return 42;
+ }
+ if (0)
+ console.debug("foo");
+ }
+}
+
+expanded: {
+ options = {
+ global_defs: {
+ "CONFIG.DEBUG": [ 0 ],
+ "CONFIG.VALUE": 42,
+ },
+ }
+ input: {
+ function f(CONFIG) {
+ // CONFIG not global - do not replace
+ return CONFIG.VALUE;
+ }
+ function g() {
+ var CONFIG = { VALUE: 1 };
+ // CONFIG not global - do not replace
+ return CONFIG.VALUE;
+ }
+ function h() {
+ return CONFIG.VALUE;
+ }
+ if (CONFIG.DEBUG[0])
+ console.debug("foo");
+ }
+ expect: {
+ function f(CONFIG) {
+ return CONFIG.VALUE;
+ }
+ function g() {
+ var CONFIG = { VALUE: 1 };
+ return CONFIG.VALUE;
+ }
+ function h() {
+ return 42;
+ }
+ if ([0][0])
+ console.debug("foo");
+ }
+}
+
+mixed: {
+ options = {
+ evaluate: true,
+ global_defs: {
+ "CONFIG.VALUE": 42,
+ "FOO.BAR": "moo",
+ },
+ properties: true,
+ }
+ input: {
+ const FOO = { BAR: 0 };
+ console.log(FOO.BAR);
+ console.log(++CONFIG.DEBUG);
+ console.log(++CONFIG.VALUE);
+ console.log(++CONFIG["VAL" + "UE"]);
+ console.log(++DEBUG[CONFIG.VALUE]);
+ CONFIG.VALUE.FOO = "bar";
+ console.log(CONFIG);
+ }
+ expect: {
+ const FOO = { BAR: 0 };
+ console.log("moo");
+ console.log(++CONFIG.DEBUG);
+ console.log(++CONFIG.VALUE);
+ console.log(++CONFIG.VALUE);
+ console.log(++DEBUG[42]);
+ CONFIG.VALUE.FOO = "bar";
+ console.log(CONFIG);
+ }
+ expect_warnings: [
+ 'WARN: global_defs CONFIG.VALUE redefined [test/compress/global_defs.js:126,22]',
+ 'WARN: global_defs CONFIG.VALUE redefined [test/compress/global_defs.js:127,22]',
+ 'WARN: global_defs CONFIG.VALUE redefined [test/compress/global_defs.js:129,8]',
+ ]
+}