use a Dictionary object instead of plain object for hashes

to mitigate the `__proto__` issue

related to #30
This commit is contained in:
Mihai Bazon
2012-11-02 10:58:45 +02:00
parent dde57452aa
commit 8413787efc
3 changed files with 42 additions and 24 deletions

View File

@@ -1026,7 +1026,7 @@ merge(Compressor.prototype, {
if (hoist_funs || hoist_vars) { if (hoist_funs || hoist_vars) {
var dirs = []; var dirs = [];
var hoisted = []; 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 // let's count var_decl first, we seem to waste a lot of
// space if we hoist `var` when there's only one. // space if we hoist `var` when there's only one.
self.walk(new TreeWalker(function(node){ self.walk(new TreeWalker(function(node){
@@ -1051,7 +1051,7 @@ merge(Compressor.prototype, {
} }
if (node instanceof AST_Var && hoist_vars) { if (node instanceof AST_Var && hoist_vars) {
node.definitions.forEach(function(def){ node.definitions.forEach(function(def){
vars[def.name.name] = def; vars.set(def.name.name, def);
++vars_found; ++vars_found;
}); });
var seq = node.to_assignments(); var seq = node.to_assignments();
@@ -1075,8 +1075,8 @@ merge(Compressor.prototype, {
); );
self = self.transform(tt); self = self.transform(tt);
if (vars_found > 0) hoisted.unshift(make_node(AST_Var, self, { if (vars_found > 0) hoisted.unshift(make_node(AST_Var, self, {
definitions: Object.keys(vars).map(function(name){ definitions: vars.map(function(def){
var def = vars[name].clone(); def = def.clone();
def.value = null; def.value = null;
return def; return def;
}) })

View File

@@ -75,7 +75,7 @@ AST_Toplevel.DEFMETHOD("figure_out_scope", function(){
// pass 1: setup scope chaining and handle definitions // pass 1: setup scope chaining and handle definitions
var self = this; var self = this;
var scope = self.parent_scope = null; var scope = self.parent_scope = null;
var labels = Object.create(null); var labels = new Dictionary();
var tw = new TreeWalker(function(node, descend){ var tw = new TreeWalker(function(node, descend){
if (node instanceof AST_Scope) { if (node instanceof AST_Scope) {
node.init_scope_vars(); node.init_scope_vars();
@@ -97,11 +97,11 @@ AST_Toplevel.DEFMETHOD("figure_out_scope", function(){
} }
if (node instanceof AST_LabeledStatement) { if (node instanceof AST_LabeledStatement) {
var l = node.label; var l = node.label;
if (labels[l.name]) if (labels.has(l.name))
throw new Error(string_template("Label {name} defined twice", l)); throw new Error(string_template("Label {name} defined twice", l));
labels[l.name] = l; labels.set(l.name, l);
descend(); descend();
delete labels[l.name]; labels.del(l.name);
return true; // no descend again return true; // no descend again
} }
if (node instanceof AST_SymbolDeclaration) { if (node instanceof AST_SymbolDeclaration) {
@@ -151,7 +151,7 @@ AST_Toplevel.DEFMETHOD("figure_out_scope", function(){
scope.def_variable(node); scope.def_variable(node);
} }
if (node instanceof AST_LabelRef) { 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}]", { if (!sym) throw new Error(string_template("Undefined label {name} [{line},{col}]", {
name: node.name, name: node.name,
line: node.start.line, line: node.start.line,
@@ -164,7 +164,7 @@ AST_Toplevel.DEFMETHOD("figure_out_scope", function(){
// pass 2: find back references and eval // pass 2: find back references and eval
var func = null; var func = null;
var globals = self.globals = Object.create(null); var globals = self.globals = new Dictionary();
var tw = new TreeWalker(function(node, descend){ var tw = new TreeWalker(function(node, descend){
if (node instanceof AST_Lambda) { if (node instanceof AST_Lambda) {
var prev_func = func; var prev_func = func;
@@ -182,12 +182,12 @@ AST_Toplevel.DEFMETHOD("figure_out_scope", function(){
var sym = node.scope.find_variable(name); var sym = node.scope.find_variable(name);
if (!sym) { if (!sym) {
var g; var g;
if (globals[name]) { if (globals.has(name)) {
g = globals[name]; g = globals.get(name);
} else { } else {
g = new SymbolDef(self, node); g = new SymbolDef(self, node);
g.undeclared = true; g.undeclared = true;
globals[name] = g; globals.set(name, g);
} }
node.thedef = g; node.thedef = g;
if (name == "eval" && tw.parent() instanceof AST_Call) { 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(){ AST_Scope.DEFMETHOD("init_scope_vars", function(){
this.directives = []; // contains the directives defined in this scope, i.e. "use strict" 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.variables = new Dictionary(); // 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.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_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.uses_eval = false; // will be set to true if this or nested scope uses the global `eval`
this.parent_scope = null; // the parent scope this.parent_scope = null; // the parent scope
@@ -252,7 +252,7 @@ AST_LabelRef.DEFMETHOD("reference", function(){
AST_Scope.DEFMETHOD("find_variable", function(name){ AST_Scope.DEFMETHOD("find_variable", function(name){
if (name instanceof AST_Symbol) name = name.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)); || (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){ 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){ AST_Scope.DEFMETHOD("def_variable", function(symbol){
var def; var def;
if (!this.variables[symbol.name]) { if (!this.variables.has(symbol.name)) {
def = new SymbolDef(this, symbol); def = new SymbolDef(this, symbol);
this.variables[symbol.name] = def; this.variables.set(symbol.name, def);
def.global = !this.parent_scope; def.global = !this.parent_scope;
} else { } else {
def = this.variables[symbol.name]; def = this.variables.get(symbol.name);
def.orig.push(symbol); def.orig.push(symbol);
} }
return symbol.thedef = def; return symbol.thedef = def;
@@ -355,15 +355,13 @@ AST_Toplevel.DEFMETHOD("mangle_names", function(options){
if (node instanceof AST_Scope) { if (node instanceof AST_Scope) {
var p = tw.parent(); var p = tw.parent();
var is_setget = p instanceof AST_ObjectSetter || p instanceof AST_ObjectGetter; var is_setget = p instanceof AST_ObjectSetter || p instanceof AST_ObjectGetter;
var a = node.variables; node.variables.each(function(symbol){
for (var i in a) {
var symbol = a[i];
if (!(is_setget && symbol instanceof AST_SymbolLambda)) { if (!(is_setget && symbol instanceof AST_SymbolLambda)) {
if (options.except.indexOf(symbol.name) < 0) { if (options.except.indexOf(symbol.name) < 0) {
to_mangle.push(symbol); to_mangle.push(symbol);
} }
} }
} });
return; return;
} }
if (node instanceof AST_Label) { if (node instanceof AST_Label) {

View File

@@ -244,3 +244,23 @@ function makePredicate(words) {
} }
return new Function("str", f); 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;
}
};