From: sgjesse@chromium.org Date: Fri, 6 Aug 2010 08:03:44 +0000 (+0000) Subject: Re-apply r5165 (Added support for ES5's propertyname production) X-Git-Tag: upstream/4.7.83~21406 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=88b19a9d0cc30777630b66ed2acc483b9bb885a3;p=platform%2Fupstream%2Fv8.git 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 --- diff --git a/src/parser.cc b/src/parser.cc index dd5f9bd..bf1a348 100644 --- a/src/parser.cc +++ b/src/parser.cc @@ -265,6 +265,7 @@ class Parser { Literal* GetLiteralNumber(double value); Handle ParseIdentifier(bool* ok); + Handle ParseIdentifierName(bool* ok); Handle 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 name = ParseIdentifier(CHECK_OK); + Handle 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 name = ParseIdentifier(CHECK_OK); + Handle 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 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 name = ParseIdentifier(CHECK_OK); + Handle name; + next = peek(); + if (next == Token::IDENTIFIER || + next == Token::STRING || + next == Token::NUMBER || + Token::IsKeyword(next)) { + Consume(next); + Handle 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 = 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 Parser::ParseIdentifier(bool* ok) { scanner_.literal_length()); } + +Handle Parser::ParseIdentifierName(bool* ok) { + Token::Value next = Next(); + if (next != Token::IDENTIFIER && !Token::IsKeyword(next)) { + ReportUnexpectedToken(next); + *ok = false; + return Handle(); + } + 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 diff --git a/src/runtime.cc b/src/runtime.cc index d460e44..32c5dff 100644 --- a/src/runtime.cc +++ b/src/runtime.cc @@ -305,14 +305,13 @@ static Handle CreateObjectLiteralBoilerplate( } Handle result; uint32_t element_index = 0; - if (key->IsSymbol()) { - // If key is a symbol it is not an array element. - Handle 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 name(String::cast(*key)); + result = SetProperty(boilerplate, name, value, NONE); } else { // Non-uint32 number. ASSERT(key->IsNumber()); diff --git a/src/token.cc b/src/token.cc index 8cee99b..21fa9ee 100644 --- a/src/token.cc +++ b/src/token.cc @@ -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 diff --git a/src/token.h b/src/token.h index 2a228d6..0d8960b 100644 --- a/src/token.h +++ b/src/token.h @@ -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 diff --git a/test/mjsunit/object-literal.js b/test/mjsunit/object-literal.js index cc6f59d..3a6c009 100644 --- a/test/mjsunit/object-literal.js +++ b/test/mjsunit/object-literal.js @@ -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); + + diff --git a/test/sputnik/sputnik.status b/test/sputnik/sputnik.status index 4c6fd1e..13108c0 100644 --- a/test/sputnik/sputnik.status +++ b/test/sputnik/sputnik.status @@ -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.