Fix quoting of properties

- Make AST_ConciseMethod child of AST_ObjectProperty.
- Fix some typos.
This commit is contained in:
Anthony Van de Gejuchte
2016-07-29 03:18:21 +02:00
committed by Richard van Velzen
parent 67461666dc
commit 1c15d0db45
7 changed files with 257 additions and 58 deletions

View File

@@ -448,8 +448,8 @@ var AST_ArrowParametersOrSeq = DEFNODE("ArrowParametersOrSeq", "expressions", {
var AST_Lambda = DEFNODE("Lambda", "name argnames uses_arguments is_generator", { var AST_Lambda = DEFNODE("Lambda", "name argnames uses_arguments is_generator", {
$documentation: "Base class for functions", $documentation: "Base class for functions",
$propdoc: { $propdoc: {
is_generator: "is generatorFn or not", is_generator: "[boolean] is generatorFn or not",
name: "[AST_SymbolDeclaration?|AST_Node] the name of this function or computed expression", name: "[AST_SymbolDeclaration?] the name of this function",
argnames: "[AST_SymbolFunarg|AST_Destructuring|AST_Expansion*] array of function arguments, destructurings, or expanding arguments", argnames: "[AST_SymbolFunarg|AST_Destructuring|AST_Expansion*] array of function arguments, destructurings, or expanding arguments",
uses_arguments: "[boolean/S] tells whether this function accesses the arguments array" uses_arguments: "[boolean/S] tells whether this function accesses the arguments array"
}, },
@@ -487,14 +487,6 @@ var AST_Arrow = DEFNODE("Arrow", null, {
$documentation: "An ES6 Arrow function ((a) => b)" $documentation: "An ES6 Arrow function ((a) => b)"
}, AST_Lambda); }, AST_Lambda);
var AST_ConciseMethod = DEFNODE("ConciseMethod", "is_generator static", {
$propdoc: {
is_generator: "is generatorFn or not",
static: "[boolean] whether this method is static (classes only)",
},
$documentation: "An ES6 concise method inside an object or class"
}, AST_Lambda);
var AST_Defun = DEFNODE("Defun", null, { var AST_Defun = DEFNODE("Defun", null, {
$documentation: "A function definition" $documentation: "A function definition"
}, AST_Lambda); }, AST_Lambda);
@@ -996,11 +988,13 @@ 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_Node] the property name converted to a string for ObjectKeyVal. For setters and getters this is an arbitrary AST_Node.", key: "[string|AST_Node] the property name converted to a string for ObjectKeyVal. For setters, getters and computed property this is an arbitrary AST_Node",
value: "[AST_Node] property value. For setters and getters this is an AST_Function." value: "[AST_Node] property value. For setters and getters this is an AST_Function."
}, },
_walk: function(visitor) { _walk: function(visitor) {
return visitor._visit(this, function(){ return visitor._visit(this, function(){
if (this.key instanceof AST_Node)
this.key._walk(visitor);
this.value._walk(visitor); this.value._walk(visitor);
}); });
} }
@@ -1016,28 +1010,33 @@ var AST_ObjectKeyVal = DEFNODE("ObjectKeyVal", "quote shorthand", {
var AST_ObjectComputedKeyVal = DEFNODE("ObjectComputedKeyVal", null, { var AST_ObjectComputedKeyVal = DEFNODE("ObjectComputedKeyVal", null, {
$documentation: "An object property whose key is computed. Like `[Symbol.iterator]: function...` or `[routes.homepage]: renderHomepage`", $documentation: "An object property whose key is computed. Like `[Symbol.iterator]: function...` or `[routes.homepage]: renderHomepage`",
_walk: function(visitor) {
return visitor._visit(this, function(){
this.key._walk(visitor);
this.value._walk(visitor);
});
}
}, AST_ObjectProperty); }, AST_ObjectProperty);
var AST_ObjectSetter = DEFNODE("ObjectSetter", "static", { var AST_ObjectSetter = DEFNODE("ObjectSetter", "quote static", {
$propdoc: { $propdoc: {
quote: "[string|undefined] the original quote character, if any",
static: "[boolean] whether this is a static setter (classes only)" static: "[boolean] whether this is a static setter (classes only)"
}, },
$documentation: "An object setter property", $documentation: "An object setter property",
}, AST_ObjectProperty); }, AST_ObjectProperty);
var AST_ObjectGetter = DEFNODE("ObjectGetter", "static", { var AST_ObjectGetter = DEFNODE("ObjectGetter", "quote static", {
$propdoc: { $propdoc: {
quote: "[string|undefined] the original quote character, if any",
static: "[boolean] whether this is a static getter (classes only)" static: "[boolean] whether this is a static getter (classes only)"
}, },
$documentation: "An object getter property", $documentation: "An object getter property",
}, AST_ObjectProperty); }, AST_ObjectProperty);
var AST_ConciseMethod = DEFNODE("ConciseMethod", "quote static is_generator", {
$propdoc: {
quote: "[string|undefined] the original quote character, if any",
static: "[boolean] whether this method is static (classes only)",
is_generator: "[boolean] is generatorFn or not",
},
$documentation: "An ES6 concise method inside an object or class"
}, AST_ObjectProperty);
var AST_Class = DEFNODE("Class", "name extends properties", { var AST_Class = DEFNODE("Class", "name extends properties", {
$propdoc: { $propdoc: {
name: "[AST_SymbolClass|AST_SymbolDefClass?] optional class name.", name: "[AST_SymbolClass|AST_SymbolDefClass?] optional class name.",

View File

@@ -966,16 +966,6 @@ function OutputStream(options) {
} }
if (needs_parens) { output.print(")") } if (needs_parens) { output.print(")") }
}); });
DEFPRINT(AST_ConciseMethod, function(self, output){
if (self.static) {
output.print("static");
output.space();
}
if (self.is_generator) {
output.print("*");
}
self._do_print(output, true /* do not print "function" */);
});
/* -----[ exits ]----- */ /* -----[ exits ]----- */
AST_Exit.DEFMETHOD("_do_print", function(output, kind){ AST_Exit.DEFMETHOD("_do_print", function(output, kind){
@@ -1430,17 +1420,7 @@ function OutputStream(options) {
DEFPRINT(AST_NewTarget, function(self, output) { DEFPRINT(AST_NewTarget, function(self, output) {
output.print("new.target"); output.print("new.target");
}); });
DEFPRINT(AST_ObjectKeyVal, function(self, output){ AST_ObjectProperty.DEFMETHOD("print_property_name", function(key, quote, output) {
var key = self.key;
var quote = self.quote;
var print_as_shorthand = self.shorthand &&
self.value instanceof AST_Symbol &&
self.key == self.value.print_to_string();
if (print_as_shorthand) {
output.print_name(key);
return;
}
if (output.option("quote_keys")) { if (output.option("quote_keys")) {
output.print_string(key + ""); output.print_string(key + "");
} else if ((typeof key == "number" } else if ((typeof key == "number"
@@ -1457,6 +1437,17 @@ function OutputStream(options) {
} else { } else {
output.print_string(key, quote); output.print_string(key, quote);
} }
});
DEFPRINT(AST_ObjectKeyVal, function(self, output){
var print_as_shorthand = self.shorthand &&
self.value instanceof AST_Symbol &&
self.key == self.value.print_to_string();
if (print_as_shorthand) {
output.print_name(self.key);
return;
}
self.print_property_name(self.key, self.quote, output);
output.colon(); output.colon();
self.value.print(output); self.value.print(output);
}); });
@@ -1468,11 +1459,7 @@ function OutputStream(options) {
output.print(type); output.print(type);
output.space(); output.space();
if (self.key instanceof AST_SymbolMethod) { if (self.key instanceof AST_SymbolMethod) {
if (typeof self.key.name === "string" && !is_identifier_string(self.key.name)) { self.print_property_name(self.key.name, self.quote, output);
output.print_string(self.key.name);
} else {
self.key.print(output);
}
} else { } else {
output.with_square(function() { output.with_square(function() {
self.key.print(output); self.key.print(output);
@@ -1486,6 +1473,24 @@ function OutputStream(options) {
DEFPRINT(AST_ObjectGetter, function(self, output){ DEFPRINT(AST_ObjectGetter, function(self, output){
self._print_getter_setter("get", self, output); self._print_getter_setter("get", self, output);
}); });
DEFPRINT(AST_ConciseMethod, function(self, output){
if (self.static) {
output.print("static");
output.space();
}
if (self.is_generator) {
output.print("*");
}
output.space();
if (self.key instanceof AST_SymbolMethod) {
self.print_property_name(self.key.name, self.quote, output);
} else {
output.with_square(function() {
self.key.print(output);
});
}
self.value._do_print(output, true);
});
DEFPRINT(AST_ObjectComputedKeyVal, function(self, output) { DEFPRINT(AST_ObjectComputedKeyVal, function(self, output) {
output.print("["); output.print("[");
self.key.print(output); self.key.print(output);

View File

@@ -1253,7 +1253,7 @@ function parse($TEXT, options) {
}); });
}; };
var function_ = function(ctor) { var function_ = function(ctor, is_generator_property) {
var start = S.token var start = S.token
var in_statement = ctor === AST_Defun; var in_statement = ctor === AST_Defun;
@@ -1267,7 +1267,7 @@ function parse($TEXT, options) {
unexpected(); unexpected();
var args = params_or_seq_().as_params(croak); var args = params_or_seq_().as_params(croak);
var body = _function_body(true, is_generator); var body = _function_body(true, is_generator || is_generator_property);
return new ctor({ return new ctor({
start : args.start, start : args.start,
end : body.end, end : body.end,
@@ -1744,7 +1744,7 @@ function parse($TEXT, options) {
start = S.token; start = S.token;
var type = start.type; var type = start.type;
var name = as_property_name(); var name = as_property_name();
if (type != "string" && type != "num" && !is("punc", ":")) { if (!is("punc", ":")) {
var concise = concise_method_or_getset(name, start); var concise = concise_method_or_getset(name, start);
if (concise) { if (concise) {
a.push(concise); a.push(concise);
@@ -1757,8 +1757,10 @@ function parse($TEXT, options) {
if (type == "punc" && start.value == "[") { if (type == "punc" && start.value == "[") {
expect(":"); expect(":");
a.push(new AST_ObjectComputedKeyVal({ a.push(new AST_ObjectComputedKeyVal({
start: start,
key: name, key: name,
value: expression(false) value: expression(false),
end: prev()
})); }));
continue; continue;
} }
@@ -1859,12 +1861,15 @@ function parse($TEXT, options) {
} }
var is_static = false; var is_static = false;
var is_generator = false; var is_generator = false;
var property_token = start;
if (is_class && name === "static" && !is("punc", "(")) { if (is_class && name === "static" && !is("punc", "(")) {
is_static = true; is_static = true;
property_token = S.token;
name = as_property_name(); name = as_property_name();
} }
if (name === null) { if (name === null) {
is_generator = true; is_generator = true;
property_token = S.token;
name = as_property_name(); name = as_property_name();
if (name === null) { if (name === null) {
unexpected(); unexpected();
@@ -1872,23 +1877,28 @@ function parse($TEXT, options) {
} }
if (is("punc", "(")) { if (is("punc", "(")) {
name = get_ast(name, start); name = get_ast(name, start);
return new AST_ConciseMethod({ var node = new AST_ConciseMethod({
is_generator: is_generator,
start : start, start : start,
static : is_static, static : is_static,
name : name, is_generator: is_generator,
argnames : params_or_seq_().as_params(croak), key : name,
body : _function_body(true, is_generator), quote : name instanceof AST_SymbolMethod ?
property_token.quote : undefined,
value : function_(AST_Accessor, is_generator),
end : prev() end : prev()
}); });
return node;
} }
property_token = S.token;
if (name == "get") { if (name == "get") {
if (!is("punc") || is("punc", "[")) { if (!is("punc") || is("punc", "[")) {
name = get_ast(as_property_name(), prev()); name = get_ast(as_property_name(), start);
return new AST_ObjectGetter({ return new AST_ObjectGetter({
start : start, start : start,
static: is_static, static: is_static,
key : name, key : name,
quote : name instanceof AST_SymbolMethod ?
property_token.quote : undefined,
value : function_(AST_Accessor), value : function_(AST_Accessor),
end : prev() end : prev()
}); });
@@ -1896,11 +1906,13 @@ function parse($TEXT, options) {
} }
else if (name == "set") { else if (name == "set") {
if (!is("punc") || is("punc", "[")) { if (!is("punc") || is("punc", "[")) {
name = get_ast(as_property_name(), prev()); name = get_ast(as_property_name(), start);
return new AST_ObjectSetter({ return new AST_ObjectSetter({
start : start, start : start,
static: is_static, static: is_static,
key : name, key : name,
quote : name instanceof AST_SymbolMethod ?
property_token.quote : undefined,
value : function_(AST_Accessor), value : function_(AST_Accessor),
end : prev() end : prev()
}); });

View File

@@ -224,6 +224,9 @@ TreeTransformer.prototype = new TreeWalker;
}); });
_(AST_ObjectProperty, function(self, tw){ _(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);
}); });

View File

@@ -248,6 +248,26 @@ classes_can_have_computed_static: {
} }
} }
class_methods_and_getters_with_keep_quoted_props_enabled: {
beautify = {
quote_style: 3,
keep_quoted_props: true,
}
input: {
class clss {
a() {}
"b"() {}
get c() { return "c"}
get "d"() { return "d"}
set e(a) { doSomething(a); }
set 'f'(a) { doSomething(b); }
static g() {}
static "h"() {}
}
}
expect_exact: 'class clss{a(){}"b"(){}get c(){return"c"}get"d"(){return"d"}set e(a){doSomething(a)}set\'f\'(a){doSomething(b)}static g(){}static"h"(){}}'
}
new_target: { new_target: {
input: { input: {
new.target; new.target;

View File

@@ -112,6 +112,35 @@ computed_property_names: {
expect_exact: 'obj({["x"+"x"]:6});' expect_exact: 'obj({["x"+"x"]:6});'
} }
computed_property_names_evaluated_1: {
options = {
evaluate: true
}
input: {
obj({
[1 + 1]: 2,
["x" + "x"]: 6
});
}
expect_exact: 'obj({[2]:2,["xx"]:6});'
}
computed_property_names_evaluated_2: {
options = {
evaluate: true
}
input: {
var foo = something();
var obj = {
[foo]() {
return "blah";
}
}
}
expect_exact: 'var foo=something();var obj={[foo](){return"blah"}};'
}
shorthand_properties: { shorthand_properties: {
mangle = true; mangle = true;
input: (function() { input: (function() {
@@ -154,6 +183,9 @@ concise_methods_with_computed_property: {
}, },
[1 + 2]() { [1 + 2]() {
return 3; return 3;
},
["1" + "4"]() {
return 14;
} }
} }
} }
@@ -164,11 +196,74 @@ concise_methods_with_computed_property: {
}, },
[3]() { [3]() {
return 3; return 3;
},
["14"]() {
return 14;
} }
} }
} }
} }
concise_methods_with_computed_property2: {
options = {
evaluate: true
}
input: {
var foo = {
[[1]](){
return "success";
}
};
doSomething(foo[[1]]());
}
expect_exact: {
'var foo={[[1]](){return"success"}};doSomething(foo[[1]]());'
}
}
concise_methods_with_various_property_names: {
input: {
var get = "bar";
var a = {
bar() {
return this.get;
},
5() {
return "five";
},
0xf55() {
return "f five five";
},
"five"() {
return 5;
},
0b1010(value) {
this._ten = value;
}
};
}
expect: {
var get = "bar";
var a = {
bar() {
return this.get;
},
5() {
return "five";
},
0xf55() {
return "f five five";
},
"five"() {
return 5;
},
0b1010(value) {
this._ten = value;
}
};
}
}
concise_methods_and_mangle_props: { concise_methods_and_mangle_props: {
mangle_props = { mangle_props = {
regex: /_/ regex: /_/
@@ -218,7 +313,6 @@ concise_methods_and_keyword_names: {
} }
} }
getter_setter_with_computed_value: { getter_setter_with_computed_value: {
input: { input: {
class C { class C {
@@ -339,3 +433,38 @@ property_with_unprintable_ascii_only: {
} }
expect_exact: 'var foo={"\\0\\x01":"foo",get"\\0\\x01"(){return"bar"},set"\\0\\x01"(foo){save(foo)},*"\\0\\x01"(){return"foobar"}};class bar{get"\\0\\x01"(){return"bar"}set"\\0\\x01"(foo){save(foo)}*"\\0\\x01"(){return"foobar"}}' expect_exact: 'var foo={"\\0\\x01":"foo",get"\\0\\x01"(){return"bar"},set"\\0\\x01"(foo){save(foo)},*"\\0\\x01"(){return"foobar"}};class bar{get"\\0\\x01"(){return"bar"}set"\\0\\x01"(foo){save(foo)}*"\\0\\x01"(){return"foobar"}}'
} }
property_with_unprintable_ascii_only_static: {
beautify = {
ascii_only: true
}
input: {
class foo {
static get "\x02\x03"() {
return "bar";
}
static set "\x04\x05"(foo) {
save(foo);
}
}
}
expect_exact: 'class foo{static get"\\x02\\x03"(){return"bar"}static set"\\x04\\x05"(foo){save(foo)}}'
}
methods_and_getters_with_keep_quoted_props_enabled: {
beautify = {
quote_style: 3,
keep_quoted_props: true,
}
input: {
var obj = {
a() {},
"b"() {},
get c() { return "c"},
get "d"() { return "d"},
set e(a) { doSomething(a); },
set f(a) { doSomething(b); }
}
}
expect_exact: 'var obj={a(){},"b"(){},get c(){return"c"},get"d"(){return"d"},set e(a){doSomething(a)},set f(a){doSomething(b)}};'
}

View File

@@ -23,4 +23,35 @@ describe("Class", function() {
assert.throws(test(tests[i]), error); assert.throws(test(tests[i]), error);
} }
}); });
it("Should return the correct token for class methods", function() {
var tests = [
{
code: "class foo{static test(){}}",
token_value_start: "static",
token_value_end: "}"
},
{
code: "class bar{*procedural(){}}",
token_value_start: "*",
token_value_end: "}"
},
{
code: "class foobar{aMethod(){}}",
token_value_start: "aMethod",
token_value_end: "}"
},
{
code: "class foobaz{get something(){}}",
token_value_start: "get",
token_value_end: "}"
}
];
for (var i = 0; i < tests.length; i++) {
var ast = uglify.parse(tests[i].code);
assert.strictEqual(ast.body[0].properties[0].start.value, tests[i].token_value_start);
assert.strictEqual(ast.body[0].properties[0].end.value, tests[i].token_value_end);
}
});
}); });