From 81f5ed5c654c7b06e11563c36c79024265bcb7e7 Mon Sep 17 00:00:00 2001 From: Ryan Dahl Date: Wed, 4 Aug 2010 11:46:42 -0700 Subject: [PATCH] Upgrade V8 to 2.3.5 --- deps/v8/ChangeLog | 9 +++ deps/v8/src/parser.cc | 58 +++++++++++--- deps/v8/src/platform-linux.cc | 1 + deps/v8/src/platform-win32.cc | 28 ++++++- deps/v8/src/runtime.cc | 11 ++- deps/v8/src/token.cc | 8 ++ deps/v8/src/token.h | 5 ++ deps/v8/src/version.cc | 4 +- deps/v8/test/mjsunit/object-literal.js | 107 +++++++++++++++++++++++++ deps/v8/test/sputnik/sputnik.status | 20 +++-- 10 files changed, 224 insertions(+), 27 deletions(-) diff --git a/deps/v8/ChangeLog b/deps/v8/ChangeLog index 2e76b8a3f1..f7e015f6cd 100644 --- a/deps/v8/ChangeLog +++ b/deps/v8/ChangeLog @@ -1,3 +1,12 @@ +2010-08-04: Version 2.3.5 + + Added support for ES5 property names. Object initialisers and + dot-notation property access now allows keywords. Also allowed + non-identifiers after "get" or "set" in an object initialiser. + + Randomize the addresses of allocated executable memory on Windows. + + 2010-08-02: Version 2.3.4 Fixed problems in implementation of ES5 function.prototype.bind. diff --git a/deps/v8/src/parser.cc b/deps/v8/src/parser.cc index dd5f9bd0d0..bf1a348211 100644 --- a/deps/v8/src/parser.cc +++ b/deps/v8/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/deps/v8/src/platform-linux.cc b/deps/v8/src/platform-linux.cc index 7e8a5586f8..d3a44982f0 100644 --- a/deps/v8/src/platform-linux.cc +++ b/deps/v8/src/platform-linux.cc @@ -236,6 +236,7 @@ size_t OS::AllocateAlignment() { void* OS::Allocate(const size_t requested, size_t* allocated, bool is_executable) { + // TODO(805): Port randomization of allocated executable memory to Linux. const size_t msize = RoundUp(requested, sysconf(_SC_PAGESIZE)); int prot = PROT_READ | PROT_WRITE | (is_executable ? PROT_EXEC : 0); void* mbase = mmap(NULL, msize, prot, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); diff --git a/deps/v8/src/platform-win32.cc b/deps/v8/src/platform-win32.cc index e2d123cd1b..af3e9b2f30 100644 --- a/deps/v8/src/platform-win32.cc +++ b/deps/v8/src/platform-win32.cc @@ -838,12 +838,38 @@ size_t OS::AllocateAlignment() { void* OS::Allocate(const size_t requested, size_t* allocated, bool is_executable) { + // The address range used to randomize RWX allocations in OS::Allocate + // Try not to map pages into the default range that windows loads DLLs + // Note: This does not guarantee RWX regions will be within the + // range kAllocationRandomAddressMin to kAllocationRandomAddressMax +#ifdef V8_HOST_ARCH_64_BIT + static const intptr_t kAllocationRandomAddressMin = 0x0000000080000000; + static const intptr_t kAllocationRandomAddressMax = 0x000004FFFFFFFFFF; +#else + static const intptr_t kAllocationRandomAddressMin = 0x04000000; + static const intptr_t kAllocationRandomAddressMax = 0x4FFFFFFF; +#endif + // VirtualAlloc rounds allocated size to page size automatically. size_t msize = RoundUp(requested, static_cast(GetPageSize())); + intptr_t address = NULL; // Windows XP SP2 allows Data Excution Prevention (DEP). int prot = is_executable ? PAGE_EXECUTE_READWRITE : PAGE_READWRITE; - LPVOID mbase = VirtualAlloc(NULL, msize, MEM_COMMIT | MEM_RESERVE, prot); + + // For exectutable pages try and randomize the allocation address + if (prot == PAGE_EXECUTE_READWRITE && msize >= Page::kPageSize) { + address = (V8::Random() << kPageSizeBits) | kAllocationRandomAddressMin; + address &= kAllocationRandomAddressMax; + } + + LPVOID mbase = VirtualAlloc(reinterpret_cast(address), + msize, + MEM_COMMIT | MEM_RESERVE, + prot); + if (mbase == NULL && address != NULL) + mbase = VirtualAlloc(NULL, msize, MEM_COMMIT | MEM_RESERVE, prot); + if (mbase == NULL) { LOG(StringEvent("OS::Allocate", "VirtualAlloc failed")); return NULL; diff --git a/deps/v8/src/runtime.cc b/deps/v8/src/runtime.cc index d460e44691..32c5dff78c 100644 --- a/deps/v8/src/runtime.cc +++ b/deps/v8/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/deps/v8/src/token.cc b/deps/v8/src/token.cc index 8cee99bb8b..21fa9ee4d7 100644 --- a/deps/v8/src/token.cc +++ b/deps/v8/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/deps/v8/src/token.h b/deps/v8/src/token.h index 2a228d67cf..0d8960b8ee 100644 --- a/deps/v8/src/token.h +++ b/deps/v8/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/deps/v8/src/version.cc b/deps/v8/src/version.cc index 6717215b1a..87d5ad26e4 100644 --- a/deps/v8/src/version.cc +++ b/deps/v8/src/version.cc @@ -34,8 +34,8 @@ // cannot be changed without changing the SCons build script. #define MAJOR_VERSION 2 #define MINOR_VERSION 3 -#define BUILD_NUMBER 4 -#define PATCH_LEVEL 1 +#define BUILD_NUMBER 5 +#define PATCH_LEVEL 0 #define CANDIDATE_VERSION false // Define SONAME to have the SCons build the put a specific SONAME into the diff --git a/deps/v8/test/mjsunit/object-literal.js b/deps/v8/test/mjsunit/object-literal.js index cc6f59dce6..3a6c0096bb 100644 --- a/deps/v8/test/mjsunit/object-literal.js +++ b/deps/v8/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/deps/v8/test/sputnik/sputnik.status b/deps/v8/test/sputnik/sputnik.status index 4c6fd1e637..13108c0fb3 100644 --- a/deps/v8/test/sputnik/sputnik.status +++ b/deps/v8/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.