use a Dictionary object instead of plain object for hashes
authorMihai Bazon <mihai@bazon.net>
Fri, 2 Nov 2012 08:58:45 +0000 (10:58 +0200)
committerMihai Bazon <mihai@bazon.net>
Fri, 2 Nov 2012 08:58:45 +0000 (10:58 +0200)
to mitigate the `__proto__` issue

related to #30

lib/compress.js
lib/scope.js
lib/utils.js

index 5ceef20..fe212b4 100644 (file)
@@ -1026,7 +1026,7 @@ merge(Compressor.prototype, {
         if (hoist_funs || hoist_vars) {
             var dirs = [];
             var hoisted = [];
-            var vars = {}, vars_found = 0, var_decl = 0;
+            var vars = new Dictionary(), vars_found = 0, var_decl = 0;
             // let's count var_decl first, we seem to waste a lot of
             // space if we hoist `var` when there's only one.
             self.walk(new TreeWalker(function(node){
@@ -1051,7 +1051,7 @@ merge(Compressor.prototype, {
                         }
                         if (node instanceof AST_Var && hoist_vars) {
                             node.definitions.forEach(function(def){
-                                vars[def.name.name] = def;
+                                vars.set(def.name.name, def);
                                 ++vars_found;
                             });
                             var seq = node.to_assignments();
@@ -1075,8 +1075,8 @@ merge(Compressor.prototype, {
             );
             self = self.transform(tt);
             if (vars_found > 0) hoisted.unshift(make_node(AST_Var, self, {
-                definitions: Object.keys(vars).map(function(name){
-                    var def = vars[name].clone();
+                definitions: vars.map(function(def){
+                    def = def.clone();
                     def.value = null;
                     return def;
                 })
index 910ce5a..fcd9de1 100644 (file)
@@ -75,7 +75,7 @@ AST_Toplevel.DEFMETHOD("figure_out_scope", function(){
     // pass 1: setup scope chaining and handle definitions
     var self = this;
     var scope = self.parent_scope = null;
-    var labels = Object.create(null);
+    var labels = new Dictionary();
     var tw = new TreeWalker(function(node, descend){
         if (node instanceof AST_Scope) {
             node.init_scope_vars();
@@ -97,11 +97,11 @@ AST_Toplevel.DEFMETHOD("figure_out_scope", function(){
         }
         if (node instanceof AST_LabeledStatement) {
             var l = node.label;
-            if (labels[l.name])
+            if (labels.has(l.name))
                 throw new Error(string_template("Label {name} defined twice", l));
-            labels[l.name] = l;
+            labels.set(l.name, l);
             descend();
-            delete labels[l.name];
+            labels.del(l.name);
             return true;        // no descend again
         }
         if (node instanceof AST_SymbolDeclaration) {
@@ -151,7 +151,7 @@ AST_Toplevel.DEFMETHOD("figure_out_scope", function(){
             scope.def_variable(node);
         }
         if (node instanceof AST_LabelRef) {
-            var sym = labels[node.name];
+            var sym = labels.get(node.name);
             if (!sym) throw new Error(string_template("Undefined label {name} [{line},{col}]", {
                 name: node.name,
                 line: node.start.line,
@@ -164,7 +164,7 @@ AST_Toplevel.DEFMETHOD("figure_out_scope", function(){
 
     // pass 2: find back references and eval
     var func = null;
-    var globals = self.globals = Object.create(null);
+    var globals = self.globals = new Dictionary();
     var tw = new TreeWalker(function(node, descend){
         if (node instanceof AST_Lambda) {
             var prev_func = func;
@@ -182,12 +182,12 @@ AST_Toplevel.DEFMETHOD("figure_out_scope", function(){
             var sym = node.scope.find_variable(name);
             if (!sym) {
                 var g;
-                if (globals[name]) {
-                    g = globals[name];
+                if (globals.has(name)) {
+                    g = globals.get(name);
                 } else {
                     g = new SymbolDef(self, node);
                     g.undeclared = true;
-                    globals[name] = g;
+                    globals.set(name, g);
                 }
                 node.thedef = g;
                 if (name == "eval" && tw.parent() instanceof AST_Call) {
@@ -209,8 +209,8 @@ AST_Toplevel.DEFMETHOD("figure_out_scope", function(){
 
 AST_Scope.DEFMETHOD("init_scope_vars", function(){
     this.directives = [];     // contains the directives defined in this scope, i.e. "use strict"
-    this.variables = Object.create(null); // map name to AST_SymbolVar (variables defined in this scope; includes functions)
-    this.functions = Object.create(null); // map name to AST_SymbolDefun (functions defined in this scope)
+    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
     this.uses_eval = false;   // will be set to true if this or nested scope uses the global `eval`
     this.parent_scope = null; // the parent scope
@@ -252,7 +252,7 @@ AST_LabelRef.DEFMETHOD("reference", function(){
 
 AST_Scope.DEFMETHOD("find_variable", function(name){
     if (name instanceof AST_Symbol) name = name.name;
-    return this.variables[name]
+    return this.variables.get(name)
         || (this.parent_scope && this.parent_scope.find_variable(name));
 });
 
@@ -262,17 +262,17 @@ AST_Scope.DEFMETHOD("has_directive", function(value){
 });
 
 AST_Scope.DEFMETHOD("def_function", function(symbol){
-    this.functions[symbol.name] = this.def_variable(symbol);
+    this.functions.set(symbol.name, this.def_variable(symbol));
 });
 
 AST_Scope.DEFMETHOD("def_variable", function(symbol){
     var def;
-    if (!this.variables[symbol.name]) {
+    if (!this.variables.has(symbol.name)) {
         def = new SymbolDef(this, symbol);
-        this.variables[symbol.name] = def;
+        this.variables.set(symbol.name, def);
         def.global = !this.parent_scope;
     } else {
-        def = this.variables[symbol.name];
+        def = this.variables.get(symbol.name);
         def.orig.push(symbol);
     }
     return symbol.thedef = def;
@@ -355,15 +355,13 @@ AST_Toplevel.DEFMETHOD("mangle_names", function(options){
         if (node instanceof AST_Scope) {
             var p = tw.parent();
             var is_setget = p instanceof AST_ObjectSetter || p instanceof AST_ObjectGetter;
-            var a = node.variables;
-            for (var i in a) {
-                var symbol = a[i];
+            node.variables.each(function(symbol){
                 if (!(is_setget && symbol instanceof AST_SymbolLambda)) {
                     if (options.except.indexOf(symbol.name) < 0) {
                         to_mangle.push(symbol);
                     }
                 }
-            }
+            });
             return;
         }
         if (node instanceof AST_Label) {
index 4d3d60f..27b7975 100644 (file)
@@ -244,3 +244,23 @@ function makePredicate(words) {
     }
     return new Function("str", f);
 };
+
+function Dictionary() {
+    this._values = Object.create(null);
+};
+Dictionary.prototype = {
+    set: function(key, val) { return this._values["$" + key] = val, this },
+    get: function(key) { return this._values["$" + key] },
+    del: function(key) { return delete this._values["$" + key], this },
+    has: function(key) { return ("$" + key) in this._values },
+    each: function(f) {
+        for (var i in this._values)
+            f(this._values[i], i.substr(1));
+    },
+    map: function(f) {
+        var ret = [];
+        for (var i in this._values)
+            ret.push(f(this._values[i], i.substr(1)));
+        return ret;
+    }
+};