support computed property name in object literal (#4268)
This commit is contained in:
30
lib/ast.js
30
lib/ast.js
@@ -1015,24 +1015,28 @@ var AST_Object = DEFNODE("Object", "properties", {
|
|||||||
var AST_ObjectProperty = DEFNODE("ObjectProperty", "key value", {
|
var AST_ObjectProperty = DEFNODE("ObjectProperty", "key value", {
|
||||||
$documentation: "Base class for literal object properties",
|
$documentation: "Base class for literal object properties",
|
||||||
$propdoc: {
|
$propdoc: {
|
||||||
key: "[string|AST_SymbolAccessor] property name. For ObjectKeyVal this is a string. For getters and setters this is an AST_SymbolAccessor.",
|
key: "[string|AST_Node] property name. For computed property this is an AST_Node.",
|
||||||
value: "[AST_Node] property value. For getters and setters this is an AST_Accessor."
|
value: "[AST_Node] property value. For getters and setters this is an AST_Accessor.",
|
||||||
},
|
},
|
||||||
walk: function(visitor) {
|
walk: function(visitor) {
|
||||||
var node = this;
|
var node = this;
|
||||||
visitor.visit(node, function() {
|
visitor.visit(node, function() {
|
||||||
|
if (node.key instanceof AST_Node) node.key.walk(visitor);
|
||||||
node.value.walk(visitor);
|
node.value.walk(visitor);
|
||||||
});
|
});
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
var AST_ObjectKeyVal = DEFNODE("ObjectKeyVal", "quote", {
|
|
||||||
$documentation: "A key: value object property",
|
|
||||||
$propdoc: {
|
|
||||||
quote: "[string] the original quote character"
|
|
||||||
},
|
},
|
||||||
_validate: function() {
|
_validate: function() {
|
||||||
if (typeof this.key != "string") throw new Error("key must be string");
|
if (typeof this.key != "string") {
|
||||||
|
if (!(this.key instanceof AST_Node)) throw new Error("key must be string or AST_Node");
|
||||||
|
must_be_expression(this, "key");
|
||||||
|
}
|
||||||
|
if (!(this.value instanceof AST_Node)) throw new Error("value must be AST_Node");
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
var AST_ObjectKeyVal = DEFNODE("ObjectKeyVal", null, {
|
||||||
|
$documentation: "A key: value object property",
|
||||||
|
_validate: function() {
|
||||||
must_be_expression(this, "value");
|
must_be_expression(this, "value");
|
||||||
},
|
},
|
||||||
}, AST_ObjectProperty);
|
}, AST_ObjectProperty);
|
||||||
@@ -1040,7 +1044,6 @@ var AST_ObjectKeyVal = DEFNODE("ObjectKeyVal", "quote", {
|
|||||||
var AST_ObjectSetter = DEFNODE("ObjectSetter", null, {
|
var AST_ObjectSetter = DEFNODE("ObjectSetter", null, {
|
||||||
$documentation: "An object setter property",
|
$documentation: "An object setter property",
|
||||||
_validate: function() {
|
_validate: function() {
|
||||||
if (!(this.key instanceof AST_SymbolAccessor)) throw new Error("key must be AST_SymbolAccessor");
|
|
||||||
if (!(this.value instanceof AST_Accessor)) throw new Error("value must be AST_Accessor");
|
if (!(this.value instanceof AST_Accessor)) throw new Error("value must be AST_Accessor");
|
||||||
},
|
},
|
||||||
}, AST_ObjectProperty);
|
}, AST_ObjectProperty);
|
||||||
@@ -1048,7 +1051,6 @@ var AST_ObjectSetter = DEFNODE("ObjectSetter", null, {
|
|||||||
var AST_ObjectGetter = DEFNODE("ObjectGetter", null, {
|
var AST_ObjectGetter = DEFNODE("ObjectGetter", null, {
|
||||||
$documentation: "An object getter property",
|
$documentation: "An object getter property",
|
||||||
_validate: function() {
|
_validate: function() {
|
||||||
if (!(this.key instanceof AST_SymbolAccessor)) throw new Error("key must be AST_SymbolAccessor");
|
|
||||||
if (!(this.value instanceof AST_Accessor)) throw new Error("value must be AST_Accessor");
|
if (!(this.value instanceof AST_Accessor)) throw new Error("value must be AST_Accessor");
|
||||||
},
|
},
|
||||||
}, AST_ObjectProperty);
|
}, AST_ObjectProperty);
|
||||||
@@ -1065,10 +1067,6 @@ var AST_Symbol = DEFNODE("Symbol", "scope name thedef", {
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
var AST_SymbolAccessor = DEFNODE("SymbolAccessor", null, {
|
|
||||||
$documentation: "The name of a property accessor (setter/getter function)"
|
|
||||||
}, AST_Symbol);
|
|
||||||
|
|
||||||
var AST_SymbolDeclaration = DEFNODE("SymbolDeclaration", "init", {
|
var AST_SymbolDeclaration = DEFNODE("SymbolDeclaration", "init", {
|
||||||
$documentation: "A declaration symbol (symbol in var, function name or argument, symbol in catch)",
|
$documentation: "A declaration symbol (symbol in var, function name or argument, symbol in catch)",
|
||||||
}, AST_Symbol);
|
}, AST_Symbol);
|
||||||
|
|||||||
106
lib/compress.js
106
lib/compress.js
@@ -1749,11 +1749,10 @@ merge(Compressor.prototype, {
|
|||||||
}
|
}
|
||||||
} else if (expr instanceof AST_Object) {
|
} else if (expr instanceof AST_Object) {
|
||||||
expr.properties.forEach(function(prop) {
|
expr.properties.forEach(function(prop) {
|
||||||
if (prop instanceof AST_ObjectKeyVal) {
|
hit_stack.push(prop);
|
||||||
hit_stack.push(prop);
|
if (prop.key instanceof AST_Node) extract_candidates(prop.key);
|
||||||
extract_candidates(prop.value);
|
if (prop instanceof AST_ObjectKeyVal) extract_candidates(prop.value);
|
||||||
hit_stack.pop();
|
hit_stack.pop();
|
||||||
}
|
|
||||||
});
|
});
|
||||||
} else if (expr instanceof AST_Sequence) {
|
} else if (expr instanceof AST_Sequence) {
|
||||||
expr.expressions.forEach(extract_candidates);
|
expr.expressions.forEach(extract_candidates);
|
||||||
@@ -1799,7 +1798,7 @@ merge(Compressor.prototype, {
|
|||||||
if (parent instanceof AST_Exit) return node;
|
if (parent instanceof AST_Exit) return node;
|
||||||
if (parent instanceof AST_If) return node;
|
if (parent instanceof AST_If) return node;
|
||||||
if (parent instanceof AST_IterationStatement) return node;
|
if (parent instanceof AST_IterationStatement) return node;
|
||||||
if (parent instanceof AST_ObjectKeyVal) return node;
|
if (parent instanceof AST_ObjectProperty) return node;
|
||||||
if (parent instanceof AST_PropAccess) return node;
|
if (parent instanceof AST_PropAccess) return node;
|
||||||
if (parent instanceof AST_Sequence) {
|
if (parent instanceof AST_Sequence) {
|
||||||
return (parent.tail_node() === node ? find_stop : find_stop_unused)(parent, level + 1);
|
return (parent.tail_node() === node ? find_stop : find_stop_unused)(parent, level + 1);
|
||||||
@@ -1857,7 +1856,7 @@ merge(Compressor.prototype, {
|
|||||||
if (parent.condition !== node) return node;
|
if (parent.condition !== node) return node;
|
||||||
return find_stop_value(parent, level + 1);
|
return find_stop_value(parent, level + 1);
|
||||||
}
|
}
|
||||||
if (parent instanceof AST_ObjectKeyVal) {
|
if (parent instanceof AST_ObjectProperty) {
|
||||||
var obj = scanner.parent(level + 1);
|
var obj = scanner.parent(level + 1);
|
||||||
return all(obj.properties, function(prop) {
|
return all(obj.properties, function(prop) {
|
||||||
return prop instanceof AST_ObjectKeyVal;
|
return prop instanceof AST_ObjectKeyVal;
|
||||||
@@ -1905,7 +1904,7 @@ merge(Compressor.prototype, {
|
|||||||
if (parent instanceof AST_Exit) return find_stop_unused(parent, level + 1);
|
if (parent instanceof AST_Exit) return find_stop_unused(parent, level + 1);
|
||||||
if (parent instanceof AST_If) return find_stop_unused(parent, level + 1);
|
if (parent instanceof AST_If) return find_stop_unused(parent, level + 1);
|
||||||
if (parent instanceof AST_IterationStatement) return node;
|
if (parent instanceof AST_IterationStatement) return node;
|
||||||
if (parent instanceof AST_ObjectKeyVal) {
|
if (parent instanceof AST_ObjectProperty) {
|
||||||
var obj = scanner.parent(level + 1);
|
var obj = scanner.parent(level + 1);
|
||||||
return all(obj.properties, function(prop) {
|
return all(obj.properties, function(prop) {
|
||||||
return prop instanceof AST_ObjectKeyVal;
|
return prop instanceof AST_ObjectKeyVal;
|
||||||
@@ -2699,9 +2698,12 @@ merge(Compressor.prototype, {
|
|||||||
if (prop instanceof AST_Node) break;
|
if (prop instanceof AST_Node) break;
|
||||||
prop = "" + prop;
|
prop = "" + prop;
|
||||||
var diff = prop == "__proto__" || compressor.has_directive("use strict") ? function(node) {
|
var diff = prop == "__proto__" || compressor.has_directive("use strict") ? function(node) {
|
||||||
return node.key != prop && node.key.name != prop;
|
return typeof node.key == "string" && node.key != prop;
|
||||||
} : function(node) {
|
} : function(node) {
|
||||||
return node.key.name != prop;
|
if (node instanceof AST_ObjectGetter || node instanceof AST_ObjectSetter) {
|
||||||
|
return typeof node.key == "string" && node.key != prop;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
};
|
};
|
||||||
if (!all(value.properties, diff)) break;
|
if (!all(value.properties, diff)) break;
|
||||||
value.properties.push(make_node(AST_ObjectKeyVal, node, {
|
value.properties.push(make_node(AST_ObjectKeyVal, node, {
|
||||||
@@ -2986,10 +2988,9 @@ merge(Compressor.prototype, {
|
|||||||
def(AST_Lambda, return_false);
|
def(AST_Lambda, return_false);
|
||||||
def(AST_Null, return_true);
|
def(AST_Null, return_true);
|
||||||
def(AST_Object, function(compressor) {
|
def(AST_Object, function(compressor) {
|
||||||
if (!is_strict(compressor)) return false;
|
return is_strict(compressor) && !all(this.properties, function(prop) {
|
||||||
for (var i = this.properties.length; --i >=0;)
|
return prop instanceof AST_ObjectKeyVal;
|
||||||
if (this.properties[i].value instanceof AST_Accessor) return true;
|
});
|
||||||
return false;
|
|
||||||
});
|
});
|
||||||
def(AST_Sequence, function(compressor) {
|
def(AST_Sequence, function(compressor) {
|
||||||
return this.tail_node()._dot_throw(compressor);
|
return this.tail_node()._dot_throw(compressor);
|
||||||
@@ -3585,8 +3586,12 @@ merge(Compressor.prototype, {
|
|||||||
var val = {};
|
var val = {};
|
||||||
for (var i = 0; i < this.properties.length; i++) {
|
for (var i = 0; i < this.properties.length; i++) {
|
||||||
var prop = this.properties[i];
|
var prop = this.properties[i];
|
||||||
|
if (!(prop instanceof AST_ObjectKeyVal)) return this;
|
||||||
var key = prop.key;
|
var key = prop.key;
|
||||||
if (key instanceof AST_Symbol) key = key.name;
|
if (key instanceof AST_Node) {
|
||||||
|
key = key._eval(compressor, ignore_side_effects, cached, depth);
|
||||||
|
if (key === prop.key) return this;
|
||||||
|
}
|
||||||
if (prop.value instanceof AST_Function) {
|
if (prop.value instanceof AST_Function) {
|
||||||
if (typeof Object.prototype[key] == "function") return this;
|
if (typeof Object.prototype[key] == "function") return this;
|
||||||
continue;
|
continue;
|
||||||
@@ -4108,7 +4113,8 @@ merge(Compressor.prototype, {
|
|||||||
return any(this.properties, compressor);
|
return any(this.properties, compressor);
|
||||||
});
|
});
|
||||||
def(AST_ObjectProperty, function(compressor) {
|
def(AST_ObjectProperty, function(compressor) {
|
||||||
return this.value.has_side_effects(compressor);
|
return this.key instanceof AST_Node && this.key.has_side_effects(compressor)
|
||||||
|
|| this.value.has_side_effects(compressor);
|
||||||
});
|
});
|
||||||
def(AST_Sub, function(compressor) {
|
def(AST_Sub, function(compressor) {
|
||||||
return this.expression.may_throw_on_access(compressor)
|
return this.expression.may_throw_on_access(compressor)
|
||||||
@@ -4220,7 +4226,8 @@ merge(Compressor.prototype, {
|
|||||||
return any(this.properties, compressor);
|
return any(this.properties, compressor);
|
||||||
});
|
});
|
||||||
def(AST_ObjectProperty, function(compressor) {
|
def(AST_ObjectProperty, function(compressor) {
|
||||||
return this.value.may_throw(compressor);
|
return this.key instanceof AST_Node && this.key.may_throw(compressor)
|
||||||
|
|| this.value.may_throw(compressor);
|
||||||
});
|
});
|
||||||
def(AST_Return, function(compressor) {
|
def(AST_Return, function(compressor) {
|
||||||
return this.value && this.value.may_throw(compressor);
|
return this.value && this.value.may_throw(compressor);
|
||||||
@@ -4316,7 +4323,7 @@ merge(Compressor.prototype, {
|
|||||||
return all(this.properties);
|
return all(this.properties);
|
||||||
});
|
});
|
||||||
def(AST_ObjectProperty, function() {
|
def(AST_ObjectProperty, function() {
|
||||||
return this.value.is_constant_expression();
|
return typeof this.key == "string" && this.value.is_constant_expression();
|
||||||
});
|
});
|
||||||
def(AST_Unary, function() {
|
def(AST_Unary, function() {
|
||||||
return this.expression.is_constant_expression();
|
return this.expression.is_constant_expression();
|
||||||
@@ -5742,7 +5749,7 @@ merge(Compressor.prototype, {
|
|||||||
return right instanceof AST_Object
|
return right instanceof AST_Object
|
||||||
&& right.properties.length > 0
|
&& right.properties.length > 0
|
||||||
&& all(right.properties, function(prop) {
|
&& all(right.properties, function(prop) {
|
||||||
return prop instanceof AST_ObjectKeyVal;
|
return prop instanceof AST_ObjectKeyVal && typeof prop.key == "string";
|
||||||
})
|
})
|
||||||
&& all(def.references, function(ref) {
|
&& all(def.references, function(ref) {
|
||||||
return ref.fixed_value() === right;
|
return ref.fixed_value() === right;
|
||||||
@@ -5932,12 +5939,14 @@ merge(Compressor.prototype, {
|
|||||||
return safe_to_drop(this, compressor) ? null : this;
|
return safe_to_drop(this, compressor) ? null : this;
|
||||||
});
|
});
|
||||||
def(AST_Object, function(compressor, first_in_statement) {
|
def(AST_Object, function(compressor, first_in_statement) {
|
||||||
var values = trim(this.properties, compressor, first_in_statement);
|
var exprs = [];
|
||||||
|
this.properties.forEach(function(prop) {
|
||||||
|
if (prop.key instanceof AST_Node) exprs.push(prop.key);
|
||||||
|
exprs.push(prop.value);
|
||||||
|
});
|
||||||
|
var values = trim(exprs, compressor, first_in_statement);
|
||||||
return values && make_sequence(this, values);
|
return values && make_sequence(this, values);
|
||||||
});
|
});
|
||||||
def(AST_ObjectProperty, function(compressor, first_in_statement) {
|
|
||||||
return this.value.drop_side_effect_free(compressor, first_in_statement);
|
|
||||||
});
|
|
||||||
def(AST_Sequence, function(compressor, first_in_statement) {
|
def(AST_Sequence, function(compressor, first_in_statement) {
|
||||||
var expressions = trim(this.expressions, compressor, first_in_statement);
|
var expressions = trim(this.expressions, compressor, first_in_statement);
|
||||||
if (!expressions) return null;
|
if (!expressions) return null;
|
||||||
@@ -9317,22 +9326,21 @@ merge(Compressor.prototype, {
|
|||||||
var props = expr.properties;
|
var props = expr.properties;
|
||||||
for (var i = props.length; --i >= 0;) {
|
for (var i = props.length; --i >= 0;) {
|
||||||
var prop = props[i];
|
var prop = props[i];
|
||||||
if ("" + prop.key == key) {
|
if (prop.key != key) continue;
|
||||||
if (!all(props, function(prop) {
|
if (!all(props, function(prop) {
|
||||||
return prop instanceof AST_ObjectKeyVal;
|
return prop instanceof AST_ObjectKeyVal && typeof prop.key == "string";
|
||||||
})) break;
|
})) break;
|
||||||
if (!safe_to_flatten(prop.value, compressor)) break;
|
if (!safe_to_flatten(prop.value, compressor)) break;
|
||||||
return make_node(AST_Sub, this, {
|
return make_node(AST_Sub, this, {
|
||||||
expression: make_node(AST_Array, expr, {
|
expression: make_node(AST_Array, expr, {
|
||||||
elements: props.map(function(prop) {
|
elements: props.map(function(prop) {
|
||||||
return prop.value;
|
return prop.value;
|
||||||
})
|
|
||||||
}),
|
|
||||||
property: make_node(AST_Number, this, {
|
|
||||||
value: i
|
|
||||||
})
|
})
|
||||||
});
|
}),
|
||||||
}
|
property: make_node(AST_Number, this, {
|
||||||
|
value: i
|
||||||
|
})
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -9402,22 +9410,22 @@ merge(Compressor.prototype, {
|
|||||||
var keys = new Dictionary();
|
var keys = new Dictionary();
|
||||||
var values = [];
|
var values = [];
|
||||||
self.properties.forEach(function(prop) {
|
self.properties.forEach(function(prop) {
|
||||||
if (typeof prop.key != "string") {
|
if (prop.key instanceof AST_Node) {
|
||||||
|
var key = prop.key.evaluate(compressor);
|
||||||
|
if (key !== prop.key) prop.key = "" + key;
|
||||||
|
}
|
||||||
|
if (prop instanceof AST_ObjectKeyVal && typeof prop.key == "string") {
|
||||||
|
if (prop.value.has_side_effects(compressor)) flush();
|
||||||
|
keys.add(prop.key, prop.value);
|
||||||
|
} else {
|
||||||
flush();
|
flush();
|
||||||
values.push(prop);
|
values.push(prop);
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
if (prop.value.has_side_effects(compressor)) {
|
|
||||||
flush();
|
|
||||||
}
|
|
||||||
keys.add(prop.key, prop.value);
|
|
||||||
});
|
});
|
||||||
flush();
|
flush();
|
||||||
if (self.properties.length != values.length) {
|
if (self.properties.length != values.length) return make_node(AST_Object, self, {
|
||||||
return make_node(AST_Object, self, {
|
properties: values
|
||||||
properties: values
|
});
|
||||||
});
|
|
||||||
}
|
|
||||||
return self;
|
return self;
|
||||||
|
|
||||||
function flush() {
|
function flush() {
|
||||||
|
|||||||
@@ -115,9 +115,6 @@
|
|||||||
value : from_moz(M.value)
|
value : from_moz(M.value)
|
||||||
};
|
};
|
||||||
if (M.kind == "init") return new AST_ObjectKeyVal(args);
|
if (M.kind == "init") return new AST_ObjectKeyVal(args);
|
||||||
args.key = new AST_SymbolAccessor({
|
|
||||||
name: args.key
|
|
||||||
});
|
|
||||||
args.value = new AST_Accessor(args.value);
|
args.value = new AST_Accessor(args.value);
|
||||||
if (M.kind == "get") return new AST_ObjectGetter(args);
|
if (M.kind == "get") return new AST_ObjectGetter(args);
|
||||||
if (M.kind == "set") return new AST_ObjectSetter(args);
|
if (M.kind == "set") return new AST_ObjectSetter(args);
|
||||||
@@ -385,7 +382,7 @@
|
|||||||
def_to_moz(AST_ObjectProperty, function To_Moz_Property(M) {
|
def_to_moz(AST_ObjectProperty, function To_Moz_Property(M) {
|
||||||
var key = {
|
var key = {
|
||||||
type: "Literal",
|
type: "Literal",
|
||||||
value: M.key instanceof AST_SymbolAccessor ? M.key.name : M.key
|
value: M.key
|
||||||
};
|
};
|
||||||
var kind;
|
var kind;
|
||||||
if (M instanceof AST_ObjectKeyVal) {
|
if (M instanceof AST_ObjectKeyVal) {
|
||||||
|
|||||||
@@ -699,6 +699,7 @@ function OutputStream(options) {
|
|||||||
// (false, true) ? (a = 10, b = 20) : (c = 30)
|
// (false, true) ? (a = 10, b = 20) : (c = 30)
|
||||||
// ==> 20 (side effect, set a := 10 and b := 20)
|
// ==> 20 (side effect, set a := 10 and b := 20)
|
||||||
|| p instanceof AST_Conditional
|
|| p instanceof AST_Conditional
|
||||||
|
// { [(1, 2)]: 3 }[2] ==> 3
|
||||||
// { foo: (1, 2) }.foo ==> 2
|
// { foo: (1, 2) }.foo ==> 2
|
||||||
|| p instanceof AST_ObjectProperty
|
|| p instanceof AST_ObjectProperty
|
||||||
// (1, {foo:2}).foo or (1, {foo:2})["foo"] ==> 2
|
// (1, {foo:2}).foo or (1, {foo:2})["foo"] ==> 2
|
||||||
@@ -1289,25 +1290,33 @@ function OutputStream(options) {
|
|||||||
else print_braced_empty(this, output);
|
else print_braced_empty(this, output);
|
||||||
});
|
});
|
||||||
|
|
||||||
function print_property_name(key, quote, output) {
|
function print_property_key(self, output) {
|
||||||
if (output.option("quote_keys")) {
|
var key = self.key;
|
||||||
|
if (key instanceof AST_Node) {
|
||||||
|
output.with_square(function() {
|
||||||
|
key.print(output);
|
||||||
|
});
|
||||||
|
} else if (output.option("quote_keys")) {
|
||||||
output.print_string(key);
|
output.print_string(key);
|
||||||
} else if ("" + +key == key && key >= 0) {
|
} else if ("" + +key == key && key >= 0) {
|
||||||
output.print(make_num(key));
|
output.print(make_num(key));
|
||||||
} else if (RESERVED_WORDS[key] ? !output.option("ie8") : is_identifier_string(key)) {
|
|
||||||
if (quote && output.option("keep_quoted_props")) {
|
|
||||||
output.print_string(key, quote);
|
|
||||||
} else {
|
|
||||||
output.print_name(key);
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
output.print_string(key, quote);
|
var quote = self.start && self.start.quote;
|
||||||
|
if (RESERVED_WORDS[key] ? !output.option("ie8") : is_identifier_string(key)) {
|
||||||
|
if (quote && output.option("keep_quoted_props")) {
|
||||||
|
output.print_string(key, quote);
|
||||||
|
} else {
|
||||||
|
output.print_name(key);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
output.print_string(key, quote);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
DEFPRINT(AST_ObjectKeyVal, function(output) {
|
DEFPRINT(AST_ObjectKeyVal, function(output) {
|
||||||
var self = this;
|
var self = this;
|
||||||
print_property_name(self.key, self.quote, output);
|
print_property_key(self, output);
|
||||||
output.colon();
|
output.colon();
|
||||||
self.value.print(output);
|
self.value.print(output);
|
||||||
});
|
});
|
||||||
@@ -1316,7 +1325,7 @@ function OutputStream(options) {
|
|||||||
var self = this;
|
var self = this;
|
||||||
output.print(type);
|
output.print(type);
|
||||||
output.space();
|
output.space();
|
||||||
print_property_name(self.key.name, self.quote, output);
|
print_property_key(self, output);
|
||||||
self.value._codegen(output, true);
|
self.value._codegen(output, true);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -1488,14 +1497,7 @@ function OutputStream(options) {
|
|||||||
output.add_mapping(this.start);
|
output.add_mapping(this.start);
|
||||||
});
|
});
|
||||||
|
|
||||||
DEFMAP([
|
|
||||||
AST_ObjectGetter,
|
|
||||||
AST_ObjectSetter,
|
|
||||||
], function(output) {
|
|
||||||
output.add_mapping(this.start, this.key.name);
|
|
||||||
});
|
|
||||||
|
|
||||||
DEFMAP([ AST_ObjectProperty ], function(output) {
|
DEFMAP([ AST_ObjectProperty ], function(output) {
|
||||||
output.add_mapping(this.start, this.key);
|
if (typeof this.key == "string") output.add_mapping(this.start, this.key);
|
||||||
});
|
});
|
||||||
})();
|
})();
|
||||||
|
|||||||
36
lib/parse.js
36
lib/parse.js
@@ -1340,8 +1340,7 @@ function parse($TEXT, options) {
|
|||||||
// allow trailing comma
|
// allow trailing comma
|
||||||
if (!options.strict && is("punc", "}")) break;
|
if (!options.strict && is("punc", "}")) break;
|
||||||
var start = S.token;
|
var start = S.token;
|
||||||
var type = start.type;
|
var key = as_property_key();
|
||||||
var name = as_property_name();
|
|
||||||
if (is("punc", "(")) {
|
if (is("punc", "(")) {
|
||||||
var func_start = S.token;
|
var func_start = S.token;
|
||||||
var func = function_(AST_Function);
|
var func = function_(AST_Function);
|
||||||
@@ -1349,22 +1348,17 @@ function parse($TEXT, options) {
|
|||||||
func.end = prev();
|
func.end = prev();
|
||||||
a.push(new AST_ObjectKeyVal({
|
a.push(new AST_ObjectKeyVal({
|
||||||
start: start,
|
start: start,
|
||||||
quote: start.quote,
|
key: key,
|
||||||
key: "" + name,
|
|
||||||
value: func,
|
value: func,
|
||||||
end: prev(),
|
end: prev(),
|
||||||
}));
|
}));
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (!is("punc", ":") && type == "name") switch (name) {
|
if (!is("punc", ":") && start.type == "name") switch (key) {
|
||||||
case "get":
|
case "get":
|
||||||
a.push(new AST_ObjectGetter({
|
a.push(new AST_ObjectGetter({
|
||||||
start: start,
|
start: start,
|
||||||
key: new AST_SymbolAccessor({
|
key: as_property_key(),
|
||||||
start: S.token,
|
|
||||||
name: "" + as_property_name(),
|
|
||||||
end: prev(),
|
|
||||||
}),
|
|
||||||
value: create_accessor(),
|
value: create_accessor(),
|
||||||
end: prev(),
|
end: prev(),
|
||||||
}));
|
}));
|
||||||
@@ -1372,11 +1366,7 @@ function parse($TEXT, options) {
|
|||||||
case "set":
|
case "set":
|
||||||
a.push(new AST_ObjectSetter({
|
a.push(new AST_ObjectSetter({
|
||||||
start: start,
|
start: start,
|
||||||
key: new AST_SymbolAccessor({
|
key: as_property_key(),
|
||||||
start: S.token,
|
|
||||||
name: "" + as_property_name(),
|
|
||||||
end: prev(),
|
|
||||||
}),
|
|
||||||
value: create_accessor(),
|
value: create_accessor(),
|
||||||
end: prev(),
|
end: prev(),
|
||||||
}));
|
}));
|
||||||
@@ -1384,8 +1374,7 @@ function parse($TEXT, options) {
|
|||||||
default:
|
default:
|
||||||
a.push(new AST_ObjectKeyVal({
|
a.push(new AST_ObjectKeyVal({
|
||||||
start: start,
|
start: start,
|
||||||
quote: start.quote,
|
key: key,
|
||||||
key: "" + name,
|
|
||||||
value: _make_symbol(AST_SymbolRef, start),
|
value: _make_symbol(AST_SymbolRef, start),
|
||||||
end: prev(),
|
end: prev(),
|
||||||
}));
|
}));
|
||||||
@@ -1394,8 +1383,7 @@ function parse($TEXT, options) {
|
|||||||
expect(":");
|
expect(":");
|
||||||
a.push(new AST_ObjectKeyVal({
|
a.push(new AST_ObjectKeyVal({
|
||||||
start: start,
|
start: start,
|
||||||
quote: start.quote,
|
key: key,
|
||||||
key: "" + name,
|
|
||||||
value: expression(false),
|
value: expression(false),
|
||||||
end: prev(),
|
end: prev(),
|
||||||
}));
|
}));
|
||||||
@@ -1404,7 +1392,7 @@ function parse($TEXT, options) {
|
|||||||
return new AST_Object({ properties: a });
|
return new AST_Object({ properties: a });
|
||||||
});
|
});
|
||||||
|
|
||||||
function as_property_name() {
|
function as_property_key() {
|
||||||
var tmp = S.token;
|
var tmp = S.token;
|
||||||
switch (tmp.type) {
|
switch (tmp.type) {
|
||||||
case "operator":
|
case "operator":
|
||||||
@@ -1415,7 +1403,13 @@ function parse($TEXT, options) {
|
|||||||
case "keyword":
|
case "keyword":
|
||||||
case "atom":
|
case "atom":
|
||||||
next();
|
next();
|
||||||
return tmp.value;
|
return "" + tmp.value;
|
||||||
|
case "punc":
|
||||||
|
if (tmp.value != "[") unexpected();
|
||||||
|
next();
|
||||||
|
var key = expression(false);
|
||||||
|
expect("]");
|
||||||
|
return key;
|
||||||
default:
|
default:
|
||||||
unexpected();
|
unexpected();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -81,8 +81,8 @@ var builtins = function() {
|
|||||||
|
|
||||||
function reserve_quoted_keys(ast, reserved) {
|
function reserve_quoted_keys(ast, reserved) {
|
||||||
ast.walk(new TreeWalker(function(node) {
|
ast.walk(new TreeWalker(function(node) {
|
||||||
if (node instanceof AST_ObjectKeyVal) {
|
if (node instanceof AST_ObjectProperty) {
|
||||||
if (node.quote) add(node.key);
|
if (node.start && node.start.quote) add(node.key);
|
||||||
} else if (node instanceof AST_Sub) {
|
} else if (node instanceof AST_Sub) {
|
||||||
addStrings(node.property, add);
|
addStrings(node.property, add);
|
||||||
}
|
}
|
||||||
@@ -165,11 +165,8 @@ function mangle_properties(ast, options) {
|
|||||||
}
|
}
|
||||||
} else if (node instanceof AST_Dot) {
|
} else if (node instanceof AST_Dot) {
|
||||||
add(node.property);
|
add(node.property);
|
||||||
} else if (node instanceof AST_ObjectKeyVal) {
|
|
||||||
add(node.key);
|
|
||||||
} else if (node instanceof AST_ObjectProperty) {
|
} else if (node instanceof AST_ObjectProperty) {
|
||||||
// setter or getter, since KeyVal is handled above
|
if (typeof node.key == "string") add(node.key);
|
||||||
add(node.key.name);
|
|
||||||
} else if (node instanceof AST_Sub) {
|
} else if (node instanceof AST_Sub) {
|
||||||
addStrings(node.property, add);
|
addStrings(node.property, add);
|
||||||
}
|
}
|
||||||
@@ -198,11 +195,8 @@ function mangle_properties(ast, options) {
|
|||||||
}
|
}
|
||||||
} else if (node instanceof AST_Dot) {
|
} else if (node instanceof AST_Dot) {
|
||||||
node.property = mangle(node.property);
|
node.property = mangle(node.property);
|
||||||
} else if (node instanceof AST_ObjectKeyVal) {
|
|
||||||
node.key = mangle(node.key);
|
|
||||||
} else if (node instanceof AST_ObjectProperty) {
|
} else if (node instanceof AST_ObjectProperty) {
|
||||||
// setter or getter
|
if (typeof node.key == "string") node.key = mangle(node.key);
|
||||||
node.key.name = mangle(node.key.name);
|
|
||||||
} else if (node instanceof AST_Sub) {
|
} else if (node instanceof AST_Sub) {
|
||||||
if (!options.keep_quoted) mangleStrings(node.property);
|
if (!options.keep_quoted) mangleStrings(node.property);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -164,6 +164,7 @@ TreeTransformer.prototype = new TreeWalker;
|
|||||||
self.properties = do_list(self.properties, tw);
|
self.properties = do_list(self.properties, tw);
|
||||||
});
|
});
|
||||||
DEF(AST_ObjectProperty, function(self, tw) {
|
DEF(AST_ObjectProperty, function(self, tw) {
|
||||||
|
if (self.key instanceof AST_Node) self.key = self.key.transform(tw);
|
||||||
self.value = self.value.transform(tw);
|
self.value = self.value.transform(tw);
|
||||||
});
|
});
|
||||||
})(function(node, descend) {
|
})(function(node, descend) {
|
||||||
|
|||||||
@@ -221,3 +221,38 @@ numeric_literal: {
|
|||||||
"8 7 8",
|
"8 7 8",
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
evaluate_computed_key: {
|
||||||
|
options = {
|
||||||
|
evaluate: true,
|
||||||
|
objects: true,
|
||||||
|
}
|
||||||
|
input: {
|
||||||
|
console.log({
|
||||||
|
["foo" + "bar"]: "PASS",
|
||||||
|
}.foobar);
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
console.log({
|
||||||
|
foobar: "PASS",
|
||||||
|
}.foobar);
|
||||||
|
}
|
||||||
|
expect_stdout: "PASS"
|
||||||
|
node_version: ">=4"
|
||||||
|
}
|
||||||
|
|
||||||
|
keep_computed_key: {
|
||||||
|
options = {
|
||||||
|
side_effects: true,
|
||||||
|
}
|
||||||
|
input: {
|
||||||
|
({
|
||||||
|
[console.log("PASS")]: 42,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
console.log("PASS");
|
||||||
|
}
|
||||||
|
expect_stdout: "PASS"
|
||||||
|
node_version: ">=4"
|
||||||
|
}
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ describe("tokens", function() {
|
|||||||
it("Should give correct positions for accessors", function() {
|
it("Should give correct positions for accessors", function() {
|
||||||
// location 0 1 2 3 4
|
// location 0 1 2 3 4
|
||||||
// 01234567890123456789012345678901234567890123456789
|
// 01234567890123456789012345678901234567890123456789
|
||||||
var ast = UglifyJS.parse("var obj = { get latest() { return undefined; } }");
|
var ast = UglifyJS.parse("var obj = { get [prop]() { return undefined; } }");
|
||||||
// test all AST_ObjectProperty tokens are set as expected
|
// test all AST_ObjectProperty tokens are set as expected
|
||||||
var found = false;
|
var found = false;
|
||||||
ast.walk(new UglifyJS.TreeWalker(function(node) {
|
ast.walk(new UglifyJS.TreeWalker(function(node) {
|
||||||
@@ -13,9 +13,9 @@ describe("tokens", function() {
|
|||||||
found = true;
|
found = true;
|
||||||
assert.equal(node.start.pos, 12);
|
assert.equal(node.start.pos, 12);
|
||||||
assert.equal(node.end.endpos, 46);
|
assert.equal(node.end.endpos, 46);
|
||||||
assert(node.key instanceof UglifyJS.AST_SymbolAccessor);
|
assert(node.key instanceof UglifyJS.AST_SymbolRef);
|
||||||
assert.equal(node.key.start.pos, 16);
|
assert.equal(node.key.start.pos, 17);
|
||||||
assert.equal(node.key.end.endpos, 22);
|
assert.equal(node.key.end.endpos, 21);
|
||||||
assert(node.value instanceof UglifyJS.AST_Accessor);
|
assert(node.value instanceof UglifyJS.AST_Accessor);
|
||||||
assert.equal(node.value.start.pos, 22);
|
assert.equal(node.value.start.pos, 22);
|
||||||
assert.equal(node.value.end.endpos, 46);
|
assert.equal(node.value.end.endpos, 46);
|
||||||
|
|||||||
@@ -913,14 +913,18 @@ function getDotKey(assign) {
|
|||||||
return key;
|
return key;
|
||||||
}
|
}
|
||||||
|
|
||||||
function createObjectFunction(type, recurmax, stmtDepth, canThrow) {
|
function createObjectKey(recurmax, stmtDepth, canThrow) {
|
||||||
|
return rng(10) ? KEYS[rng(KEYS.length)] : "[" + createExpression(recurmax, NO_COMMA, stmtDepth, canThrow) + "]";
|
||||||
|
}
|
||||||
|
|
||||||
|
function createObjectFunction(recurmax, stmtDepth, canThrow) {
|
||||||
var namesLenBefore = VAR_NAMES.length;
|
var namesLenBefore = VAR_NAMES.length;
|
||||||
var s;
|
var s;
|
||||||
createBlockVariables(recurmax, stmtDepth, canThrow, function(defns) {
|
createBlockVariables(recurmax, stmtDepth, canThrow, function(defns) {
|
||||||
switch (type) {
|
switch (rng(3)) {
|
||||||
case "get":
|
case 0:
|
||||||
s = [
|
s = [
|
||||||
"get " + getDotKey() + "(){",
|
"get " + createObjectKey(recurmax, stmtDepth, canThrow) + "(){",
|
||||||
strictMode(),
|
strictMode(),
|
||||||
defns(),
|
defns(),
|
||||||
_createStatements(2, recurmax, canThrow, CANNOT_BREAK, CANNOT_CONTINUE, CAN_RETURN, stmtDepth),
|
_createStatements(2, recurmax, canThrow, CANNOT_BREAK, CANNOT_CONTINUE, CAN_RETURN, stmtDepth),
|
||||||
@@ -928,8 +932,8 @@ function createObjectFunction(type, recurmax, stmtDepth, canThrow) {
|
|||||||
"},",
|
"},",
|
||||||
];
|
];
|
||||||
break;
|
break;
|
||||||
case "set":
|
case 1:
|
||||||
var prop1 = getDotKey();
|
var prop1 = createObjectKey(recurmax, stmtDepth, canThrow);
|
||||||
var prop2;
|
var prop2;
|
||||||
do {
|
do {
|
||||||
prop2 = getDotKey();
|
prop2 = getDotKey();
|
||||||
@@ -945,7 +949,7 @@ function createObjectFunction(type, recurmax, stmtDepth, canThrow) {
|
|||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
s = [
|
s = [
|
||||||
type + "(" + createParams(NO_DUPLICATE) + "){",
|
createObjectKey(recurmax, stmtDepth, canThrow) + "(" + createParams(NO_DUPLICATE) + "){",
|
||||||
strictMode(),
|
strictMode(),
|
||||||
defns(),
|
defns(),
|
||||||
_createStatements(3, recurmax, canThrow, CANNOT_BREAK, CANNOT_CONTINUE, CAN_RETURN, stmtDepth),
|
_createStatements(3, recurmax, canThrow, CANNOT_BREAK, CANNOT_CONTINUE, CAN_RETURN, stmtDepth),
|
||||||
@@ -961,21 +965,15 @@ function createObjectFunction(type, recurmax, stmtDepth, canThrow) {
|
|||||||
function createObjectLiteral(recurmax, stmtDepth, canThrow) {
|
function createObjectLiteral(recurmax, stmtDepth, canThrow) {
|
||||||
recurmax--;
|
recurmax--;
|
||||||
var obj = ["({"];
|
var obj = ["({"];
|
||||||
for (var i = rng(6); --i >= 0;) switch (rng(50)) {
|
for (var i = rng(6); --i >= 0;) switch (rng(30)) {
|
||||||
case 0:
|
case 0:
|
||||||
obj.push(createObjectFunction("get", recurmax, stmtDepth, canThrow));
|
obj.push(createObjectFunction(recurmax, stmtDepth, canThrow));
|
||||||
break;
|
break;
|
||||||
case 1:
|
case 1:
|
||||||
obj.push(createObjectFunction("set", recurmax, stmtDepth, canThrow));
|
|
||||||
break;
|
|
||||||
case 2:
|
|
||||||
obj.push(createObjectFunction(KEYS[rng(KEYS.length)], recurmax, stmtDepth, canThrow));
|
|
||||||
break;
|
|
||||||
case 3:
|
|
||||||
obj.push(getVarName() + ",");
|
obj.push(getVarName() + ",");
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
obj.push(KEYS[rng(KEYS.length)] + ":(" + createExpression(recurmax, COMMA_OK, stmtDepth, canThrow) + "),");
|
obj.push(createObjectKey(recurmax, stmtDepth, canThrow) + ":(" + createExpression(recurmax, COMMA_OK, stmtDepth, canThrow) + "),");
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
obj.push("})");
|
obj.push("})");
|
||||||
|
|||||||
Reference in New Issue
Block a user