From 8413787efc6dd570b5cc48dec65e5ffe1a32084a Mon Sep 17 00:00:00 2001 From: Mihai Bazon Date: Fri, 2 Nov 2012 10:58:45 +0200 Subject: [PATCH] use a Dictionary object instead of plain object for hashes to mitigate the `__proto__` issue related to #30 --- lib/compress.js | 8 ++++---- lib/scope.js | 38 ++++++++++++++++++-------------------- lib/utils.js | 20 ++++++++++++++++++++ 3 files changed, 42 insertions(+), 24 deletions(-) diff --git a/lib/compress.js b/lib/compress.js index 5ceef200..fe212b42 100644 --- a/lib/compress.js +++ b/lib/compress.js @@ -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; }) diff --git a/lib/scope.js b/lib/scope.js index 910ce5ad..fcd9de18 100644 --- a/lib/scope.js +++ b/lib/scope.js @@ -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) { diff --git a/lib/utils.js b/lib/utils.js index 4d3d60f6..27b79753 100644 --- a/lib/utils.js +++ b/lib/utils.js @@ -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; + } +}; -- 2.34.1