Skip to content

Commit

Permalink
fixes jquery#1037: ES6: support computed property names
Browse files Browse the repository at this point in the history
  • Loading branch information
ikarienator committed Feb 15, 2015
1 parent 5372ada commit 254a3a7
Show file tree
Hide file tree
Showing 11 changed files with 706 additions and 181 deletions.
173 changes: 93 additions & 80 deletions esprima.js
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,6 @@
FnExprTokens,
Syntax,
PlaceHolders,
PropertyKind,
Messages,
Regex,
source,
Expand Down Expand Up @@ -158,12 +157,6 @@
}
};

PropertyKind = {
Data: 1,
Get: 2,
Set: 4
};

// Error messages should be identical to V8.
Messages = {
UnexpectedToken: 'Unexpected token %0',
Expand Down Expand Up @@ -192,13 +185,11 @@
StrictFunctionName: 'Function name may not be eval or arguments in strict mode',
StrictOctalLiteral: 'Octal literals are not allowed in strict mode.',
StrictDelete: 'Delete of an unqualified identifier in strict mode.',
StrictDuplicateProperty: 'Duplicate data property in object literal not allowed in strict mode',
AccessorDataProperty: 'Object literal may not have data and accessor property with the same name',
AccessorGetSet: 'Object literal may not have multiple get/set accessors with the same name',
StrictLHSAssignment: 'Assignment to eval or arguments is not allowed in strict mode',
StrictLHSPostfix: 'Postfix increment/decrement may not have eval or arguments operand in strict mode',
StrictLHSPrefix: 'Prefix increment/decrement may not have eval or arguments operand in strict mode',
StrictReservedWord: 'Use of future reserved word in strict mode'
StrictReservedWord: 'Use of future reserved word in strict mode',
DuplicateProtoProperty: 'Duplicate __proto__ fields are not allowed in object literals'
};

// See also tools/generate-unicode-regex.py.
Expand Down Expand Up @@ -1894,9 +1885,10 @@
return this;
},

finishProperty: function (kind, key, value, method, shorthand) {
finishProperty: function (kind, key, computed, value, method, shorthand) {
this.type = Syntax.Property;
this.key = key;
this.computed = computed;
this.value = value;
this.kind = kind;
this.method = method;
Expand Down Expand Up @@ -2245,42 +2237,63 @@
}

function parseObjectPropertyKey() {
var token, node = new Node();
var token, node = new Node(), expr;

token = lex();

// Note: This function is called only from parseObjectProperty(), where
// EOF and Punctuator tokens are already filtered out.

if (token.type === Token.StringLiteral || token.type === Token.NumericLiteral) {
switch (token.type) {
case Token.StringLiteral:
case Token.NumericLiteral:
if (strict && token.octal) {
tolerateUnexpectedToken(token, Messages.StrictOctalLiteral);
}
return node.finishLiteral(token);
return [node.finishLiteral(token), false];
case Token.Identifier:
case Token.BooleanLiteral:
case Token.NullLiteral:
case Token.Keyword:
return [node.finishIdentifier(token.value), false];
case Token.Punctuator:
if (token.value === '[') {
expr = parseAssignmentExpression();
expect(']');
return [expr, true];
}
break;
}

return node.finishIdentifier(token.value);
throwUnexpectedToken(token);
}

function parseObjectProperty() {
var token, key, id, value, param, node = new Node();
function lookaheadPropertyName() {
switch (lookahead.type) {
case Token.Identifier:
case Token.StringLiteral:
case Token.BooleanLiteral:
case Token.NullLiteral:
case Token.NumericLiteral:
case Token.Keyword:
return true;
case Token.Punctuator:
return lookahead.value === '[';
}
return false;
}

token = lookahead;
function parseMethodOrPropertyName(node) {
var token = lookahead, key = parseObjectPropertyKey(), value, param;

if (token.type === Token.Identifier) {
// check for `get` and `set`;

id = parseObjectPropertyKey();

// Property Assignment: Getter and Setter.

if (token.value === 'get' && !(match(':') || match('('))) {
if (token.value === 'get' && lookaheadPropertyName()) {
key = parseObjectPropertyKey();
expect('(');
expect(')');
value = parsePropertyFunction([]);
return node.finishProperty('get', key, value, false, false);
}
if (token.value === 'set' && !(match(':') || match('('))) {
return node.finishProperty('get', key[0], key[1], parsePropertyFunction([]), false, false);
} else if (token.value === 'set' && lookaheadPropertyName()) {
key = parseObjectPropertyKey();
expect('(');
token = lookahead;
Expand All @@ -2293,74 +2306,74 @@
expect(')');
value = parsePropertyFunction(param, token);
}
return node.finishProperty('set', key, value, false, false);
return node.finishProperty('set', key[0], key[1], value, false, false);
}
if (match(':')) {
lex();
value = parseAssignmentExpression();
return node.finishProperty('init', id, value, false, false);
}

if (match('(')) {
value = parsePropertyMethodFunction();
return node.finishProperty('init', key[0], key[1], value, true, false);
}

return key;
}

function isProto(key, computed) {
return computed === false && (key.type === Syntax.Identifier && key.name === '__proto__' ||
key.type === Syntax.Literal && key.value === '__proto__');
}

function parseObjectProperty(hasProto) {
var token = lookahead, node = new Node(), methodOrPropertyName = parseMethodOrPropertyName(node), value;

switch (methodOrPropertyName.type) {
case Syntax.Property:
if (isProto(methodOrPropertyName.key, methodOrPropertyName.computed)) {
if (hasProto.value) {
tolerateError(Messages.DuplicateProtoProperty);
} else {
hasProto.value = true;
}
}
if (match('(')) {
value = parsePropertyMethodFunction();
return node.finishProperty('init', id, value, true, false);
// finished
return methodOrPropertyName;

default:
// init property or short hand property.
if (isProto(methodOrPropertyName[0], methodOrPropertyName[1])) {
if (hasProto.value) {
tolerateError(Messages.DuplicateProtoProperty);
} else {
hasProto.value = true;
}
}

value = id;
return node.finishProperty('init', id, value, false, true);
}
if (token.type === Token.EOF || token.type === Token.Punctuator) {
throwUnexpectedToken(token);
} else {
key = parseObjectPropertyKey();
if (match(':')) {
lex();
value = parseAssignmentExpression();
return node.finishProperty('init', key, value, false, false);
return node.finishProperty('init', methodOrPropertyName[0], methodOrPropertyName[1], value, false, false);
}
if (match('(')) {
value = parsePropertyMethodFunction();
return node.finishProperty('init', key, value, true, false);

if (token.type === Token.Identifier) {
return node.finishProperty('init',
methodOrPropertyName[0],
methodOrPropertyName[1],
methodOrPropertyName[0],
false,
true);
}
throwUnexpectedToken(lex());

throwUnexpectedToken(token);
}
}

function parseObjectInitialiser() {
var properties = [], property, name, key, kind, map = {}, toString = String, node = new Node();
var properties = [], hasProto = {value: false}, node = new Node();

expect('{');

while (!match('}')) {
property = parseObjectProperty();

if (property.key.type === Syntax.Identifier) {
name = property.key.name;
} else {
name = toString(property.key.value);
}
kind = (property.kind === 'init') ? PropertyKind.Data : (property.kind === 'get') ? PropertyKind.Get : PropertyKind.Set;

key = '$' + name;
if (Object.prototype.hasOwnProperty.call(map, key)) {
if (map[key] === PropertyKind.Data) {
if (strict && kind === PropertyKind.Data) {
tolerateError(Messages.StrictDuplicateProperty);
} else if (kind !== PropertyKind.Data) {
tolerateError(Messages.AccessorDataProperty);
}
} else {
if (kind === PropertyKind.Data) {
tolerateError(Messages.AccessorDataProperty);
} else if (map[key] & kind) {
tolerateError(Messages.AccessorGetSet);
}
}
map[key] |= kind;
} else {
map[key] = kind;
}

properties.push(property);
properties.push(parseObjectProperty(hasProto));

if (!match('}')) {
expectCommaSeparator();
Expand Down
1 change: 0 additions & 1 deletion test/3rdparty/syntax/README

This file was deleted.

2 changes: 1 addition & 1 deletion test/3rdparty/syntax/angular-1.2.5.json

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion test/3rdparty/syntax/backbone-1.1.0.json

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion test/3rdparty/syntax/jquery-1.9.1.json

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion test/3rdparty/syntax/jquery.mobile-1.4.2.json

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion test/3rdparty/syntax/mootools-1.4.5.json

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion test/3rdparty/syntax/underscore-1.5.2.json

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion test/3rdparty/syntax/yui-3.12.0.json

Large diffs are not rendered by default.

40 changes: 18 additions & 22 deletions test/reflect.js
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ function newExpr(callee, args) { return Pattern({ type: "NewExpression", callee:
function callExpr(callee, args) { return Pattern({ type: "CallExpression", callee: callee, arguments: args }); }
function arrExpr(elts) { return Pattern({ type: "ArrayExpression", elements: elts }); }
function objExpr(elts) { return Pattern({ type: "ObjectExpression", properties: elts }); }
function objProp(key, value, kind) { return Pattern({ type: "Property", key: key, value: value, kind: kind, method: false, shorthand: false }); }
function objProp(key, computed, value, kind) { return Pattern({ type: "Property", key: key, computed: computed, value: value, kind: kind, method: false, shorthand: false }); }

function arrPatt(elts) { return Pattern({ type: "ArrayPattern", elements: elts }); }
function objPatt(elts) { return Pattern({ type: "ObjectPattern", properties: elts }); }
Expand Down Expand Up @@ -299,21 +299,21 @@ assertExpr("[,,,1,2,3,,]", arrExpr([,,,lit(1),lit(2),lit(3),undefined]));
assertExpr("[,,,1,2,3,,,]", arrExpr([,,,lit(1),lit(2),lit(3),undefined,undefined]));
assertExpr("[,,,,,]", arrExpr([undefined,undefined,undefined,undefined,undefined]));
assertExpr("({})", objExpr([]));
assertExpr("({x:1})", objExpr([objProp(ident("x"), lit(1), "init")]));
assertExpr("({x:1, y:2})", objExpr([objProp(ident("x"), lit(1), "init"),
objProp(ident("y"), lit(2), "init")]));
assertExpr("({x:1, y:2, z:3})", objExpr([objProp(ident("x"), lit(1), "init"),
objProp(ident("y"), lit(2), "init"),
objProp(ident("z"), lit(3), "init") ]));
assertExpr("({x:1, 'y':2, z:3})", objExpr([objProp(ident("x"), lit(1), "init"),
objProp(lit("y"), lit(2), "init"),
objProp(ident("z"), lit(3), "init") ]));
assertExpr("({'x':1, 'y':2, z:3})", objExpr([objProp(lit("x"), lit(1), "init"),
objProp(lit("y"), lit(2), "init"),
objProp(ident("z"), lit(3), "init") ]));
assertExpr("({'x':1, 'y':2, 3:3})", objExpr([objProp(lit("x"), lit(1), "init"),
objProp(lit("y"), lit(2), "init"),
objProp(lit(3), lit(3), "init") ]));
assertExpr("({x:1})", objExpr([objProp(ident("x"), false, lit(1), "init")]));
assertExpr("({x:1, y:2})", objExpr([objProp(ident("x"), false, lit(1), "init"),
objProp(ident("y"), false, lit(2), "init")]));
assertExpr("({x:1, y:2, z:3})", objExpr([objProp(ident("x"), false, lit(1), "init"),
objProp(ident("y"), false, lit(2), "init"),
objProp(ident("z"), false, lit(3), "init") ]));
assertExpr("({x:1, 'y':2, z:3})", objExpr([objProp(ident("x"), false, lit(1), "init"),
objProp(lit("y"), false, lit(2), "init"),
objProp(ident("z"), false, lit(3), "init") ]));
assertExpr("({'x':1, 'y':2, z:3})", objExpr([objProp(lit("x"), false, lit(1), "init"),
objProp(lit("y"), false, lit(2), "init"),
objProp(ident("z"), false, lit(3), "init") ]));
assertExpr("({'x':1, 'y':2, 3:3})", objExpr([objProp(lit("x"), false, lit(1), "init"),
objProp(lit("y"), false, lit(2), "init"),
objProp(lit(3), false, lit(3), "init") ]));

// Bug 571617: eliminate constant-folding
assertExpr("2 + 3", binExpr("+", lit(2), lit(3)));
Expand Down Expand Up @@ -407,13 +407,9 @@ assertStmt("function f() { var x = 42; var x = 43; }",
// getters and setters

assertExpr("({ get x() { return 42 } })",
objExpr([ objProp(ident("x"),
funExpr(null, [], blockStmt([returnStmt(lit(42))])),
"get" ) ]));
objExpr([ objProp(ident("x"), false, funExpr(null, [], blockStmt([returnStmt(lit(42))])), "get") ]));
assertExpr("({ set x(v) { return 42 } })",
objExpr([ objProp(ident("x"),
funExpr(null, [ident("v")], blockStmt([returnStmt(lit(42))])),
"set" ) ]));
objExpr([ objProp(ident("x"), false, funExpr(null, [ident("v")], blockStmt([returnStmt(lit(42))])), "set") ]));

}

Expand Down
Loading

0 comments on commit 254a3a7

Please sign in to comment.