Revision: 5172
Author: [email protected]
Date: Wed Aug 4 02:46:24 2010
Log: 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.
http://code.google.com/p/v8/source/detail?r=5172
Modified:
/trunk
/trunk/ChangeLog
/trunk/src/parser.cc
/trunk/src/platform-linux.cc
/trunk/src/platform-win32.cc
/trunk/src/regexp-macro-assembler-irregexp-inl.h
/trunk/src/runtime.cc
/trunk/src/token.cc
/trunk/src/token.h
/trunk/src/version.cc
/trunk/test/mjsunit/array-constructor.js
/trunk/test/mjsunit/array-functions-prototype-misc.js
/trunk/test/mjsunit/global-load-from-eval-in-with.js
/trunk/test/mjsunit/local-load-from-eval.js
/trunk/test/mjsunit/object-literal.js
/trunk/test/mjsunit/property-load-across-eval.js
/trunk/test/mjsunit/regress/regress-269.js
/trunk/test/mjsunit/regress/regress-334.js
/trunk/test/sputnik/sputnik.status
=======================================
--- /trunk/ChangeLog Mon Aug 2 04:52:17 2010
+++ /trunk/ChangeLog Wed Aug 4 02:46:24 2010
@@ -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.
=======================================
--- /trunk/src/parser.cc Thu Jul 15 02:29:43 2010
+++ /trunk/src/parser.cc Wed Aug 4 02:46:24 2010
@@ -265,6 +265,7 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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));
@@ -4007,6 +4026,19 @@
return factory()->LookupSymbol(scanner_.literal_string(),
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
=======================================
--- /trunk/src/platform-linux.cc Mon May 31 03:38:25 2010
+++ /trunk/src/platform-linux.cc Wed Aug 4 02:46:24 2010
@@ -236,6 +236,7 @@
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);
=======================================
--- /trunk/src/platform-win32.cc Wed May 26 06:27:57 2010
+++ /trunk/src/platform-win32.cc Wed Aug 4 02:46:24 2010
@@ -838,12 +838,38 @@
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<int>(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<void *>(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;
=======================================
--- /trunk/src/runtime.cc Mon Aug 2 04:52:17 2010
+++ /trunk/src/runtime.cc Wed Aug 4 02:46:24 2010
@@ -305,14 +305,13 @@
}
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());
=======================================
--- /trunk/src/token.cc Wed Dec 16 07:36:05 2009
+++ /trunk/src/token.cc Wed Aug 4 02:46:24 2010
@@ -53,4 +53,12 @@
#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
=======================================
--- /trunk/src/token.h Wed Dec 16 07:36:05 2009
+++ /trunk/src/token.h Wed Aug 4 02:46:24 2010
@@ -220,6 +220,10 @@
}
// 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 @@
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
=======================================
--- /trunk/src/version.cc Mon Aug 2 08:15:56 2010
+++ /trunk/src/version.cc Wed Aug 4 02:46:24 2010
@@ -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
=======================================
--- /trunk/test/mjsunit/object-literal.js Tue Mar 24 06:25:23 2009
+++ /trunk/test/mjsunit/object-literal.js Wed Aug 4 02:46:24 2010
@@ -103,3 +103,110 @@
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);
+
+
=======================================
--- /trunk/test/sputnik/sputnik.status Mon May 3 03:34:42 2010
+++ /trunk/test/sputnik/sputnik.status Wed Aug 4 02:46:24 2010
@@ -158,11 +158,6 @@
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 @@
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.
--
v8-dev mailing list
[email protected]
http://groups.google.com/group/v8-dev