Re-apply r5165 (Added support for ES5's propertyname production)
TBR=lrn@chromium.org Review URL: http://codereview.chromium.org/3073031 git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@5192 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
This commit is contained in:
parent
300580d5d8
commit
88b19a9d0c
@ -265,6 +265,7 @@ class Parser {
|
||||
Literal* GetLiteralNumber(double value);
|
||||
|
||||
Handle<String> ParseIdentifier(bool* ok);
|
||||
Handle<String> ParseIdentifierName(bool* ok);
|
||||
Handle<String> ParseIdentifierOrGetOrSet(bool* is_get,
|
||||
bool* is_set,
|
||||
bool* ok);
|
||||
@ -3121,7 +3122,7 @@ Expression* Parser::ParseLeftHandSideExpression(bool* ok) {
|
||||
case Token::PERIOD: {
|
||||
Consume(Token::PERIOD);
|
||||
int pos = scanner().location().beg_pos;
|
||||
Handle<String> name = ParseIdentifier(CHECK_OK);
|
||||
Handle<String> name = ParseIdentifierName(CHECK_OK);
|
||||
result = factory()->NewProperty(result, NEW(Literal(name)), pos);
|
||||
break;
|
||||
}
|
||||
@ -3207,7 +3208,7 @@ Expression* Parser::ParseMemberWithNewPrefixesExpression(PositionStack* stack,
|
||||
case Token::PERIOD: {
|
||||
Consume(Token::PERIOD);
|
||||
int pos = scanner().location().beg_pos;
|
||||
Handle<String> name = ParseIdentifier(CHECK_OK);
|
||||
Handle<String> name = ParseIdentifierName(CHECK_OK);
|
||||
result = factory()->NewProperty(result, NEW(Literal(name)), pos);
|
||||
break;
|
||||
}
|
||||
@ -3586,8 +3587,8 @@ void Parser::BuildObjectLiteralConstantProperties(
|
||||
Expression* Parser::ParseObjectLiteral(bool* ok) {
|
||||
// ObjectLiteral ::
|
||||
// '{' (
|
||||
// ((Identifier | String | Number) ':' AssignmentExpression)
|
||||
// | (('get' | 'set') FunctionLiteral)
|
||||
// ((IdentifierName | String | Number) ':' AssignmentExpression)
|
||||
// | (('get' | 'set') (IdentifierName | String | Number) FunctionLiteral)
|
||||
// )*[','] '}'
|
||||
|
||||
ZoneListWrapper<ObjectLiteral::Property> properties =
|
||||
@ -3597,7 +3598,8 @@ Expression* Parser::ParseObjectLiteral(bool* ok) {
|
||||
Expect(Token::LBRACE, CHECK_OK);
|
||||
while (peek() != Token::RBRACE) {
|
||||
Literal* key = NULL;
|
||||
switch (peek()) {
|
||||
Token::Value next = peek();
|
||||
switch (next) {
|
||||
case Token::IDENTIFIER: {
|
||||
// Store identifier keys as literal symbols to avoid
|
||||
// resolving them when compiling code for the object
|
||||
@ -3608,15 +3610,26 @@ Expression* Parser::ParseObjectLiteral(bool* ok) {
|
||||
ParseIdentifierOrGetOrSet(&is_getter, &is_setter, CHECK_OK);
|
||||
if (is_getter || is_setter) {
|
||||
// Special handling of getter and setter syntax.
|
||||
if (peek() == Token::IDENTIFIER) {
|
||||
Handle<String> name = ParseIdentifier(CHECK_OK);
|
||||
Handle<String> name;
|
||||
next = peek();
|
||||
if (next == Token::IDENTIFIER ||
|
||||
next == Token::STRING ||
|
||||
next == Token::NUMBER ||
|
||||
Token::IsKeyword(next)) {
|
||||
Consume(next);
|
||||
Handle<String> name =
|
||||
factory()->LookupSymbol(scanner_.literal_string(),
|
||||
scanner_.literal_length());
|
||||
FunctionLiteral* value =
|
||||
ParseFunctionLiteral(name, RelocInfo::kNoPosition,
|
||||
DECLARATION, CHECK_OK);
|
||||
ParseFunctionLiteral(name,
|
||||
RelocInfo::kNoPosition,
|
||||
DECLARATION,
|
||||
CHECK_OK);
|
||||
ObjectLiteral::Property* property =
|
||||
NEW(ObjectLiteral::Property(is_getter, value));
|
||||
if (IsBoilerplateProperty(property))
|
||||
if (IsBoilerplateProperty(property)) {
|
||||
number_of_boilerplate_properties++;
|
||||
}
|
||||
properties.Add(property);
|
||||
if (peek() != Token::RBRACE) Expect(Token::COMMA, CHECK_OK);
|
||||
continue; // restart the while
|
||||
@ -3625,14 +3638,20 @@ Expression* Parser::ParseObjectLiteral(bool* ok) {
|
||||
key = NEW(Literal(id));
|
||||
break;
|
||||
}
|
||||
|
||||
#define CASE_KEYWORD(name, ignore1, ignore2) \
|
||||
case Token::name:
|
||||
TOKEN_LIST(IGNORE_TOKEN, CASE_KEYWORD, IGNORE_TOKEN)
|
||||
#undef CASE_KEYWORD
|
||||
// FALLTHROUGH - keyword tokens fall through to the same code as strings.
|
||||
case Token::STRING: {
|
||||
Consume(Token::STRING);
|
||||
Consume(next);
|
||||
Handle<String> string =
|
||||
factory()->LookupSymbol(scanner_.literal_string(),
|
||||
scanner_.literal_length());
|
||||
uint32_t index;
|
||||
if (!string.is_null() && string->AsArrayIndex(&index)) {
|
||||
if (next == Token::STRING &&
|
||||
!string.is_null() &&
|
||||
string->AsArrayIndex(&index)) {
|
||||
key = NewNumberLiteral(index);
|
||||
} else {
|
||||
key = NEW(Literal(string));
|
||||
@ -4008,6 +4027,19 @@ Handle<String> Parser::ParseIdentifier(bool* ok) {
|
||||
scanner_.literal_length());
|
||||
}
|
||||
|
||||
|
||||
Handle<String> Parser::ParseIdentifierName(bool* ok) {
|
||||
Token::Value next = Next();
|
||||
if (next != Token::IDENTIFIER && !Token::IsKeyword(next)) {
|
||||
ReportUnexpectedToken(next);
|
||||
*ok = false;
|
||||
return Handle<String>();
|
||||
}
|
||||
return factory()->LookupSymbol(scanner_.literal_string(),
|
||||
scanner_.literal_length());
|
||||
}
|
||||
|
||||
|
||||
// This function reads an identifier and determines whether or not it
|
||||
// is 'get' or 'set'. The reason for not using ParseIdentifier and
|
||||
// checking on the output is that this involves heap allocation which
|
||||
|
@ -305,14 +305,13 @@ static Handle<Object> CreateObjectLiteralBoilerplate(
|
||||
}
|
||||
Handle<Object> result;
|
||||
uint32_t element_index = 0;
|
||||
if (key->IsSymbol()) {
|
||||
// If key is a symbol it is not an array element.
|
||||
Handle<String> name(String::cast(*key));
|
||||
ASSERT(!name->AsArrayIndex(&element_index));
|
||||
result = SetProperty(boilerplate, name, value, NONE);
|
||||
} else if (key->ToArrayIndex(&element_index)) {
|
||||
if (key->ToArrayIndex(&element_index)) {
|
||||
// Array index (uint32).
|
||||
result = SetElement(boilerplate, element_index, value);
|
||||
} else if (key->IsSymbol()) {
|
||||
// The key is not an array index.
|
||||
Handle<String> name(String::cast(*key));
|
||||
result = SetProperty(boilerplate, name, value, NONE);
|
||||
} else {
|
||||
// Non-uint32 number.
|
||||
ASSERT(key->IsNumber());
|
||||
|
@ -53,4 +53,12 @@ int8_t Token::precedence_[NUM_TOKENS] = {
|
||||
#undef T
|
||||
|
||||
|
||||
#define KT(a, b, c) 'T',
|
||||
#define KK(a, b, c) 'K',
|
||||
const char Token::token_type[] = {
|
||||
TOKEN_LIST(KT, KK, IGNORE_TOKEN)
|
||||
};
|
||||
#undef KT
|
||||
#undef KK
|
||||
|
||||
} } // namespace v8::internal
|
||||
|
@ -220,6 +220,10 @@ class Token {
|
||||
}
|
||||
|
||||
// Predicates
|
||||
static bool IsKeyword(Value tok) {
|
||||
return token_type[tok] == 'K';
|
||||
}
|
||||
|
||||
static bool IsAssignmentOp(Value tok) {
|
||||
return INIT_VAR <= tok && tok <= ASSIGN_MOD;
|
||||
}
|
||||
@ -263,6 +267,7 @@ class Token {
|
||||
static const char* name_[NUM_TOKENS];
|
||||
static const char* string_[NUM_TOKENS];
|
||||
static int8_t precedence_[NUM_TOKENS];
|
||||
static const char token_type[NUM_TOKENS];
|
||||
};
|
||||
|
||||
} } // namespace v8::internal
|
||||
|
@ -103,3 +103,110 @@ a = makeRegexpInObject();
|
||||
b = makeRegexpInObject();
|
||||
assertTrue(a.a.b === b.a.b);
|
||||
assertFalse(a.a.c === b.a.c);
|
||||
|
||||
|
||||
// Test keywords valid as property names in initializers and dot-access.
|
||||
var keywords = [
|
||||
"break",
|
||||
"case",
|
||||
"catch",
|
||||
"const",
|
||||
"continue",
|
||||
"debugger",
|
||||
"default",
|
||||
"delete",
|
||||
"do",
|
||||
"else",
|
||||
"false",
|
||||
"finally",
|
||||
"for",
|
||||
"function",
|
||||
"if",
|
||||
"in",
|
||||
"instanceof",
|
||||
"native",
|
||||
"new",
|
||||
"null",
|
||||
"return",
|
||||
"switch",
|
||||
"this",
|
||||
"throw",
|
||||
"true",
|
||||
"try",
|
||||
"typeof",
|
||||
"var",
|
||||
"void",
|
||||
"while",
|
||||
"with",
|
||||
];
|
||||
|
||||
function testKeywordProperty(keyword) {
|
||||
try {
|
||||
// Sanity check that what we get is a keyword.
|
||||
eval("var " + keyword + " = 42;");
|
||||
assertUnreachable("Not a keyword: " + keyword);
|
||||
} catch (e) { }
|
||||
|
||||
// Simple property, read and write.
|
||||
var x = eval("({" + keyword + ": 42})");
|
||||
assertEquals(42, x[keyword]);
|
||||
assertEquals(42, eval("x." + keyword));
|
||||
eval("x." + keyword + " = 37");
|
||||
assertEquals(37, x[keyword]);
|
||||
assertEquals(37, eval("x." + keyword));
|
||||
|
||||
// Getter/setter property, read and write.
|
||||
var y = eval("({value : 42, get " + keyword + "(){return this.value}," +
|
||||
" set " + keyword + "(v) { this.value = v; }})");
|
||||
assertEquals(42, y[keyword]);
|
||||
assertEquals(42, eval("y." + keyword));
|
||||
eval("y." + keyword + " = 37");
|
||||
assertEquals(37, y[keyword]);
|
||||
assertEquals(37, eval("y." + keyword));
|
||||
|
||||
// Quoted keyword works is read back by unquoted as well.
|
||||
var z = eval("({\"" + keyword + "\": 42})");
|
||||
assertEquals(42, z[keyword]);
|
||||
assertEquals(42, eval("z." + keyword));
|
||||
|
||||
// Function property, called.
|
||||
var was_called;
|
||||
function test_call() { this.was_called = true; was_called = true; }
|
||||
var w = eval("({" + keyword + ": test_call, was_called: false})");
|
||||
eval("w." + keyword + "();");
|
||||
assertTrue(was_called);
|
||||
assertTrue(w.was_called);
|
||||
|
||||
// Function property, constructed.
|
||||
function construct() { this.constructed = true; }
|
||||
var v = eval("({" + keyword + ": construct})");
|
||||
var vo = eval("new v." + keyword + "()");
|
||||
assertTrue(vo instanceof construct);
|
||||
assertTrue(vo.constructed);
|
||||
}
|
||||
|
||||
for (var i = 0; i < keywords.length; i++) {
|
||||
testKeywordProperty(keywords[i]);
|
||||
}
|
||||
|
||||
// Test getter and setter properties with string/number literal names.
|
||||
|
||||
var obj = {get 42() { return 42; },
|
||||
get 3.14() { return "PI"; },
|
||||
get "PI"() { return 3.14; },
|
||||
readback: 0,
|
||||
set 37(v) { this.readback = v; },
|
||||
set 1.44(v) { this.readback = v; },
|
||||
set "Poo"(v) { this.readback = v; }}
|
||||
|
||||
assertEquals(42, obj[42]);
|
||||
assertEquals("PI", obj[3.14]);
|
||||
assertEquals(3.14, obj["PI"]);
|
||||
obj[37] = "t1";
|
||||
assertEquals("t1", obj.readback);
|
||||
obj[1.44] = "t2";
|
||||
assertEquals("t2", obj.readback);
|
||||
obj["Poo"] = "t3";
|
||||
assertEquals("t3", obj.readback);
|
||||
|
||||
|
||||
|
@ -158,11 +158,6 @@ S15.5.4.11_D1.1_T1: PASS || FAIL_OK
|
||||
S15.5.4.11_D1.1_T3: PASS || FAIL_OK
|
||||
S12.6.4_D1: PASS || FAIL_OK
|
||||
|
||||
# We deliberately don't throw type errors when iterating through the
|
||||
# undefined object
|
||||
S9.9_A1: FAIL_OK
|
||||
S9.9_A2: FAIL_OK
|
||||
|
||||
# We allow function declarations within statements
|
||||
S12.5_A9_T1: FAIL_OK
|
||||
S12.5_A9_T2: FAIL_OK
|
||||
@ -184,6 +179,21 @@ S15.3.4.2_A1_T1: FAIL_OK
|
||||
S8.5_A2.2: PASS, FAIL if $system == linux, FAIL if $system == macos
|
||||
S8.5_A2.1: PASS, FAIL if $system == linux, FAIL if $system == macos
|
||||
|
||||
##################### ES3 TESTS #########################
|
||||
# These tests check for ES3 semantics, and differ from ES5.
|
||||
# When we follow ES5 semantics, it's ok to fail the test.
|
||||
|
||||
# Allow keywords as names of properties in object initialisers and
|
||||
# in dot-notation property access.
|
||||
S11.1.5_A4.1: FAIL_OK
|
||||
S11.1.5_A4.2: FAIL_OK
|
||||
|
||||
# Don't throw type errors when iterating through the undefined object.
|
||||
S9.9_A1: FAIL_OK
|
||||
S9.9_A2: FAIL_OK
|
||||
|
||||
|
||||
|
||||
##################### SKIPPED TESTS #####################
|
||||
|
||||
# These tests take a looong time to run in debug mode.
|
||||
|
Loading…
Reference in New Issue
Block a user