ES6: Allow duplicate property names
authorcommit-queue@webkit.org <commit-queue@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Thu, 14 May 2015 01:32:25 +0000 (01:32 +0000)
committercommit-queue@webkit.org <commit-queue@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Thu, 14 May 2015 01:32:25 +0000 (01:32 +0000)
https://bugs.webkit.org/show_bug.cgi?id=142895

Patch by Joseph Pecoraro <pecoraro@apple.com> on 2015-05-13
Reviewed by Geoffrey Garen.

Source/JavaScriptCore:

Introduce new `op_put_getter_by_id` and `op_put_setter_by_id` opcodes
that will define a single getter or setter property on an object.

The existing `op_put_getter_setter` opcode is still preferred for
putting both a getter and setter at the same time but cannot be used
for putting an individual getter or setter which is needed in
some cases.

Add a new slow path when generating bytecodes for a property list
with computed properties, as computed properties are the only time
the list of properties cannot be determined statically.

* bytecompiler/NodesCodegen.cpp:
(JSC::PropertyListNode::emitBytecode):
- fast path for all constant properties
- slow but paired getter/setter path if there are no computed properties
- slow path, individual put operation for every property, if there are computed properties

* parser/Nodes.h:
Distinguish a Computed property from a Constant property.

* parser/Parser.cpp:
(JSC::Parser<LexerType>::parseProperty):
(JSC::Parser<LexerType>::parsePropertyMethod):
Distingish Computed and Constant properties.

(JSC::Parser<LexerType>::parseObjectLiteral):
When we drop into strict mode it is because we saw a getter
or setter, so be more explicit.

(JSC::Parser<LexerType>::parseStrictObjectLiteral):
Eliminate duplicate property syntax error exception.

* parser/SyntaxChecker.h:
(JSC::SyntaxChecker::getName):
* parser/ASTBuilder.h:
(JSC::ASTBuilder::getName): Deleted.
No longer used.

* runtime/JSObject.h:
(JSC::JSObject::putDirectInternal):
When updating a property. If the Accessor attribute changed
update the Structure.

* runtime/JSObject.cpp:
(JSC::JSObject::putGetter):
(JSC::JSObject::putSetter):
Called by the opcodes, just perform the same operation that
__defineGetter__ or __defineSetter__ would do.

(JSC::JSObject::putDirectNonIndexAccessor):
This transition is now handled in putDirectInternal.

* runtime/Structure.h:
Add needed export.

* bytecode/BytecodeList.json:
* bytecode/BytecodeUseDef.h:
(JSC::computeUsesForBytecodeOffset):
(JSC::computeDefsForBytecodeOffset):
* bytecode/CodeBlock.cpp:
(JSC::CodeBlock::dumpBytecode):
* bytecompiler/BytecodeGenerator.cpp:
(JSC::BytecodeGenerator::emitPutGetterById):
(JSC::BytecodeGenerator::emitPutSetterById):
* bytecompiler/BytecodeGenerator.h:
* jit/JIT.cpp:
(JSC::JIT::privateCompileMainPass):
* jit/JIT.h:
* jit/JITInlines.h:
(JSC::JIT::callOperation):
* jit/JITOperations.cpp:
* jit/JITOperations.h:
* jit/JITPropertyAccess.cpp:
(JSC::JIT::emit_op_put_getter_by_id):
(JSC::JIT::emit_op_put_setter_by_id):
* jit/JITPropertyAccess32_64.cpp:
(JSC::JIT::emit_op_put_getter_by_id):
(JSC::JIT::emit_op_put_setter_by_id):
* llint/LLIntSlowPaths.cpp:
(JSC::LLInt::LLINT_SLOW_PATH_DECL):
* llint/LLIntSlowPaths.h:
* llint/LowLevelInterpreter.asm:
New bytecodes. Modelled after existing op_put_getter_setter.

LayoutTests:

* js/object-literal-duplicate-properties-expected.txt: Added.
* js/object-literal-duplicate-properties.html: Added.
* js/script-tests/object-literal-duplicate-properties.js: Added.
Include a new test all about testing duplicate property names
and their expected cascading results.

* ietestcenter/Javascript/11.1.5_4-4-b-1-expected.txt:
* ietestcenter/Javascript/11.1.5_4-4-b-2-expected.txt:
* ietestcenter/Javascript/11.1.5_4-4-c-1-expected.txt:
* ietestcenter/Javascript/11.1.5_4-4-c-2-expected.txt:
* ietestcenter/Javascript/11.1.5_4-4-d-1-expected.txt:
* ietestcenter/Javascript/11.1.5_4-4-d-2-expected.txt:
* ietestcenter/Javascript/11.1.5_4-4-d-3-expected.txt:
* ietestcenter/Javascript/11.1.5_4-4-d-4-expected.txt:
ES5 behavior for duplciate properties has changed.

* js/mozilla/strict/11.1.5-expected.txt:
* js/object-literal-syntax-expected.txt:
* js/script-tests/object-literal-syntax.js:
Update other tests and values now that duplicate properties
are allowed, and their cascade order behaves correctly.

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@184324 268f45cc-cd09-0410-ab3c-d52691b4dbfc

40 files changed:
LayoutTests/ChangeLog
LayoutTests/ietestcenter/Javascript/11.1.5_4-4-b-1-expected.txt
LayoutTests/ietestcenter/Javascript/11.1.5_4-4-b-2-expected.txt
LayoutTests/ietestcenter/Javascript/11.1.5_4-4-c-1-expected.txt
LayoutTests/ietestcenter/Javascript/11.1.5_4-4-c-2-expected.txt
LayoutTests/ietestcenter/Javascript/11.1.5_4-4-d-1-expected.txt
LayoutTests/ietestcenter/Javascript/11.1.5_4-4-d-2-expected.txt
LayoutTests/ietestcenter/Javascript/11.1.5_4-4-d-3-expected.txt
LayoutTests/ietestcenter/Javascript/11.1.5_4-4-d-4-expected.txt
LayoutTests/js/mozilla/strict/11.1.5-expected.txt
LayoutTests/js/object-literal-duplicate-properties-expected.txt [new file with mode: 0644]
LayoutTests/js/object-literal-duplicate-properties.html [new file with mode: 0644]
LayoutTests/js/object-literal-syntax-expected.txt
LayoutTests/js/script-tests/object-literal-duplicate-properties.js [new file with mode: 0644]
LayoutTests/js/script-tests/object-literal-syntax.js
Source/JavaScriptCore/ChangeLog
Source/JavaScriptCore/bytecode/BytecodeList.json
Source/JavaScriptCore/bytecode/BytecodeUseDef.h
Source/JavaScriptCore/bytecode/CodeBlock.cpp
Source/JavaScriptCore/bytecompiler/BytecodeGenerator.cpp
Source/JavaScriptCore/bytecompiler/BytecodeGenerator.h
Source/JavaScriptCore/bytecompiler/NodesCodegen.cpp
Source/JavaScriptCore/jit/CCallHelpers.h
Source/JavaScriptCore/jit/JIT.cpp
Source/JavaScriptCore/jit/JIT.h
Source/JavaScriptCore/jit/JITInlines.h
Source/JavaScriptCore/jit/JITOperations.cpp
Source/JavaScriptCore/jit/JITOperations.h
Source/JavaScriptCore/jit/JITPropertyAccess.cpp
Source/JavaScriptCore/jit/JITPropertyAccess32_64.cpp
Source/JavaScriptCore/llint/LLIntSlowPaths.cpp
Source/JavaScriptCore/llint/LLIntSlowPaths.h
Source/JavaScriptCore/llint/LowLevelInterpreter.asm
Source/JavaScriptCore/parser/ASTBuilder.h
Source/JavaScriptCore/parser/Nodes.h
Source/JavaScriptCore/parser/Parser.cpp
Source/JavaScriptCore/parser/SyntaxChecker.h
Source/JavaScriptCore/runtime/JSObject.cpp
Source/JavaScriptCore/runtime/JSObject.h
Source/JavaScriptCore/runtime/Structure.h

index 7561a37..4068ff7 100644 (file)
@@ -1,3 +1,32 @@
+2015-05-13  Joseph Pecoraro  <pecoraro@apple.com>
+
+        ES6: Allow duplicate property names
+        https://bugs.webkit.org/show_bug.cgi?id=142895
+
+        Reviewed by Geoffrey Garen.
+
+        * js/object-literal-duplicate-properties-expected.txt: Added.
+        * js/object-literal-duplicate-properties.html: Added.
+        * js/script-tests/object-literal-duplicate-properties.js: Added.
+        Include a new test all about testing duplicate property names
+        and their expected cascading results.
+
+        * ietestcenter/Javascript/11.1.5_4-4-b-1-expected.txt:
+        * ietestcenter/Javascript/11.1.5_4-4-b-2-expected.txt:
+        * ietestcenter/Javascript/11.1.5_4-4-c-1-expected.txt:
+        * ietestcenter/Javascript/11.1.5_4-4-c-2-expected.txt:
+        * ietestcenter/Javascript/11.1.5_4-4-d-1-expected.txt:
+        * ietestcenter/Javascript/11.1.5_4-4-d-2-expected.txt:
+        * ietestcenter/Javascript/11.1.5_4-4-d-3-expected.txt:
+        * ietestcenter/Javascript/11.1.5_4-4-d-4-expected.txt:
+        ES5 behavior for duplciate properties has changed.
+
+        * js/mozilla/strict/11.1.5-expected.txt:
+        * js/object-literal-syntax-expected.txt:
+        * js/script-tests/object-literal-syntax.js:
+        Update other tests and values now that duplicate properties
+        are allowed, and their cascade order behaves correctly.
+
 2015-05-13  Antti Koivisto  <antti@apple.com>
 
         Cached CSS image resources don't show up after reloading <http://nightly.webkit.org/start/>
index 62434ea..a2dc586 100644 (file)
@@ -4,7 +4,7 @@ On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE
 
 
 PASS ES5Harness.preconditionPassed is true
-PASS ES5Harness.testPassed is true
+FAIL ES5Harness.testPassed should be true (of type boolean). Was undefined (of type undefined).
 PASS successfullyParsed is true
 
 TEST COMPLETE
index 3b79107..f47d059 100644 (file)
@@ -4,7 +4,7 @@ On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE
 
 
 PASS ES5Harness.preconditionPassed is true
-PASS ES5Harness.testPassed is true
+FAIL ES5Harness.testPassed should be true (of type boolean). Was undefined (of type undefined).
 PASS successfullyParsed is true
 
 TEST COMPLETE
index 245f4ac..386186a 100644 (file)
@@ -4,7 +4,7 @@ On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE
 
 
 PASS ES5Harness.preconditionPassed is true
-PASS ES5Harness.testPassed is true
+FAIL ES5Harness.testPassed should be true (of type boolean). Was undefined (of type undefined).
 PASS successfullyParsed is true
 
 TEST COMPLETE
index 979274b..a1b54f5 100644 (file)
@@ -4,7 +4,7 @@ On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE
 
 
 PASS ES5Harness.preconditionPassed is true
-PASS ES5Harness.testPassed is true
+FAIL ES5Harness.testPassed should be true (of type boolean). Was undefined (of type undefined).
 PASS successfullyParsed is true
 
 TEST COMPLETE
index 4d0e4c4..3e70ea5 100644 (file)
@@ -4,7 +4,7 @@ On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE
 
 
 PASS ES5Harness.preconditionPassed is true
-PASS ES5Harness.testPassed is true
+FAIL ES5Harness.testPassed should be true (of type boolean). Was undefined (of type undefined).
 PASS successfullyParsed is true
 
 TEST COMPLETE
index f97b2cf..c391c52 100644 (file)
@@ -4,7 +4,7 @@ On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE
 
 
 PASS ES5Harness.preconditionPassed is true
-PASS ES5Harness.testPassed is true
+FAIL ES5Harness.testPassed should be true (of type boolean). Was undefined (of type undefined).
 PASS successfullyParsed is true
 
 TEST COMPLETE
index e06f766..a163742 100644 (file)
@@ -4,7 +4,7 @@ On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE
 
 
 PASS ES5Harness.preconditionPassed is true
-PASS ES5Harness.testPassed is true
+FAIL ES5Harness.testPassed should be true (of type boolean). Was undefined (of type undefined).
 PASS successfullyParsed is true
 
 TEST COMPLETE
index 8d5bf21..8dab8ec 100644 (file)
@@ -4,7 +4,7 @@ On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE
 
 
 PASS ES5Harness.preconditionPassed is true
-PASS ES5Harness.testPassed is true
+FAIL ES5Harness.testPassed should be true (of type boolean). Was undefined (of type undefined).
 PASS successfullyParsed is true
 
 TEST COMPLETE
index 3b7de6e..32af22e 100644 (file)
@@ -1,74 +1,74 @@
-PASS Function("'use strict'; ({x:1, x:1})") threw exception of type SyntaxError.
+FAIL Function("'use strict'; ({x:1, x:1})") should throw an instance of SyntaxError
 PASS !!Function("({x:1, x:1})") is true
 PASS true === true
 PASS !!Function("'use strict'; ({x:1, y:1})") is true
 PASS !!Function("({x:1, y:1})") is true
 PASS true === true
-PASS Function("'use strict'; ({x:1, y:1, x:1})") threw exception of type SyntaxError.
+FAIL Function("'use strict'; ({x:1, y:1, x:1})") should throw an instance of SyntaxError
 PASS !!Function("({x:1, y:1, x:1})") is true
 PASS true === true
-PASS Function("'use strict'; ({x:1,   \"x\":1})") threw exception of type SyntaxError.
+FAIL Function("'use strict'; ({x:1,   \"x\":1})") should throw an instance of SyntaxError
 PASS !!Function("({x:1,   \"x\":1})") is true
 PASS true === true
-PASS Function("'use strict'; ({\"x\":1, x:1})") threw exception of type SyntaxError.
+FAIL Function("'use strict'; ({\"x\":1, x:1})") should throw an instance of SyntaxError
 PASS !!Function("({\"x\":1, x:1})") is true
 PASS true === true
-PASS Function("'use strict'; ({\"x\":1, \"x\":1})") threw exception of type SyntaxError.
+FAIL Function("'use strict'; ({\"x\":1, \"x\":1})") should throw an instance of SyntaxError
 PASS !!Function("({\"x\":1, \"x\":1})") is true
 PASS true === true
-PASS Function("'use strict'; ({1.5:1, 1.5:1})") threw exception of type SyntaxError.
+FAIL Function("'use strict'; ({1.5:1, 1.5:1})") should throw an instance of SyntaxError
 PASS !!Function("({1.5:1, 1.5:1})") is true
 PASS true === true
-PASS Function("'use strict'; ({1.5:1, 15e-1:1})") threw exception of type SyntaxError.
+FAIL Function("'use strict'; ({1.5:1, 15e-1:1})") should throw an instance of SyntaxError
 PASS !!Function("({1.5:1, 15e-1:1})") is true
 PASS true === true
-PASS Function("'use strict'; ({6.02214179e23:1, 6.02214179e23:1})") threw exception of type SyntaxError.
+FAIL Function("'use strict'; ({6.02214179e23:1, 6.02214179e23:1})") should throw an instance of SyntaxError
 PASS !!Function("({6.02214179e23:1, 6.02214179e23:1})") is true
 PASS true === true
 PASS !!Function("'use strict'; ({6.02214179e23:1, 3.1415926535:1})") is true
 PASS !!Function("({6.02214179e23:1, 3.1415926535:1})") is true
 PASS true === true
-PASS Function("'use strict'; ({ 1: 1, \"1\": 2 })") threw exception of type SyntaxError.
+FAIL Function("'use strict'; ({ 1: 1, \"1\": 2 })") should throw an instance of SyntaxError
 PASS !!Function("({ 1: 1, \"1\": 2 })") is true
 PASS true === true
-PASS Function("'use strict'; ({ \"1\": 1, 1: 2 })") threw exception of type SyntaxError.
+FAIL Function("'use strict'; ({ \"1\": 1, 1: 2 })") should throw an instance of SyntaxError
 PASS !!Function("({ \"1\": 1, 1: 2 })") is true
 PASS true === true
-PASS Function("'use strict'; ({ 2.5: 1, \"2.5\": 2 })") threw exception of type SyntaxError.
+FAIL Function("'use strict'; ({ 2.5: 1, \"2.5\": 2 })") should throw an instance of SyntaxError
 PASS !!Function("({ 2.5: 1, \"2.5\": 2 })") is true
 PASS true === true
-PASS Function("'use strict'; ({ \"2.5\": 1, 2.5: 2 })") threw exception of type SyntaxError.
+FAIL Function("'use strict'; ({ \"2.5\": 1, 2.5: 2 })") should throw an instance of SyntaxError
 PASS !!Function("({ \"2.5\": 1, 2.5: 2 })") is true
 PASS true === true
 PASS !!Function("'use strict'; ({a:1, b:1, c:1, d:1, e:1, f:1, g:1, h:1, i:1, j:1, k:1, l:1, m:1, n:1, o:1, p:1, q:1, r:1, s:1, t:1, u:1, v:1, w:1, x:1, y:1, z:1})") is true
 PASS !!Function("({a:1, b:1, c:1, d:1, e:1, f:1, g:1, h:1, i:1, j:1, k:1, l:1, m:1, n:1, o:1, p:1, q:1, r:1, s:1, t:1, u:1, v:1, w:1, x:1, y:1, z:1})") is true
 PASS true === true
-PASS Function("'use strict'; ({a:1, b:1, c:1, d:1, e:1, f:1, g:1, h:1, i:1, j:1, k:1, l:1, m:1, n:1, o:1, p:1, q:1, r:1, s:1, t:1, u:1, v:1, w:1, x:1, y:1, a:1})") threw exception of type SyntaxError.
+FAIL Function("'use strict'; ({a:1, b:1, c:1, d:1, e:1, f:1, g:1, h:1, i:1, j:1, k:1, l:1, m:1, n:1, o:1, p:1, q:1, r:1, s:1, t:1, u:1, v:1, w:1, x:1, y:1, a:1})") should throw an instance of SyntaxError
 PASS !!Function("({a:1, b:1, c:1, d:1, e:1, f:1, g:1, h:1, i:1, j:1, k:1, l:1, m:1, n:1, o:1, p:1, q:1, r:1, s:1, t:1, u:1, v:1, w:1, x:1, y:1, a:1})") is true
 PASS true === true
-PASS Function("'use strict'; ({get x() {}, x:1})") threw exception of type SyntaxError.
-PASS Function("({get x() {}, x:1})") threw exception of type SyntaxError.
+FAIL Function("'use strict'; ({get x() {}, x:1})") should throw an instance of SyntaxError
+FAIL Function("({get x() {}, x:1})") should throw an instance of SyntaxError
 PASS true === true
-PASS Function("'use strict'; ({x:1, get x() {}})") threw exception of type SyntaxError.
-PASS Function("({x:1, get x() {}})") threw exception of type SyntaxError.
+FAIL Function("'use strict'; ({x:1, get x() {}})") should throw an instance of SyntaxError
+FAIL Function("({x:1, get x() {}})") should throw an instance of SyntaxError
 PASS true === true
-PASS Function("'use strict'; ({set x(q) {}, x:1})") threw exception of type SyntaxError.
-PASS Function("({set x(q) {}, x:1})") threw exception of type SyntaxError.
+FAIL Function("'use strict'; ({set x(q) {}, x:1})") should throw an instance of SyntaxError
+FAIL Function("({set x(q) {}, x:1})") should throw an instance of SyntaxError
 PASS true === true
-PASS Function("'use strict'; ({x:1, set x(q) {}})") threw exception of type SyntaxError.
-PASS Function("({x:1, set x(q) {}})") threw exception of type SyntaxError.
+FAIL Function("'use strict'; ({x:1, set x(q) {}})") should throw an instance of SyntaxError
+FAIL Function("({x:1, set x(q) {}})") should throw an instance of SyntaxError
 PASS true === true
-PASS Function("'use strict'; ({1:1, set 1(q) {}})") threw exception of type SyntaxError.
-PASS Function("({1:1, set 1(q) {}})") threw exception of type SyntaxError.
+FAIL Function("'use strict'; ({1:1, set 1(q) {}})") should throw an instance of SyntaxError
+FAIL Function("({1:1, set 1(q) {}})") should throw an instance of SyntaxError
 PASS true === true
-PASS Function("'use strict'; ({set 1(q) {}, 1:1})") threw exception of type SyntaxError.
-PASS Function("({set 1(q) {}, 1:1})") threw exception of type SyntaxError.
+FAIL Function("'use strict'; ({set 1(q) {}, 1:1})") should throw an instance of SyntaxError
+FAIL Function("({set 1(q) {}, 1:1})") should throw an instance of SyntaxError
 PASS true === true
-PASS Function("'use strict'; ({\"1\":1, set 1(q) {}})") threw exception of type SyntaxError.
-PASS Function("({\"1\":1, set 1(q) {}})") threw exception of type SyntaxError.
+FAIL Function("'use strict'; ({\"1\":1, set 1(q) {}})") should throw an instance of SyntaxError
+FAIL Function("({\"1\":1, set 1(q) {}})") should throw an instance of SyntaxError
 PASS true === true
-PASS Function("'use strict'; ({set 1(q) {}, \"1\":1})") threw exception of type SyntaxError.
-PASS Function("({set 1(q) {}, \"1\":1})") threw exception of type SyntaxError.
+FAIL Function("'use strict'; ({set 1(q) {}, \"1\":1})") should throw an instance of SyntaxError
+FAIL Function("({set 1(q) {}, \"1\":1})") should throw an instance of SyntaxError
 PASS true === true
 PASS !!Function("'use strict'; ({get x() {}, set x(q) {}})") is true
 PASS !!Function("({get x() {}, set x(q) {}})") is true
@@ -76,14 +76,14 @@ PASS true === true
 PASS !!Function("'use strict'; ({set x(q) {}, get x() {}})") is true
 PASS !!Function("({set x(q) {}, get x() {}})") is true
 PASS true === true
-PASS Function("'use strict'; ({get x() {}, set x(q) {}, x:1})") threw exception of type SyntaxError.
-PASS Function("({get x() {}, set x(q) {}, x:1})") threw exception of type SyntaxError.
+FAIL Function("'use strict'; ({get x() {}, set x(q) {}, x:1})") should throw an instance of SyntaxError
+FAIL Function("({get x() {}, set x(q) {}, x:1})") should throw an instance of SyntaxError
 PASS true === true
-PASS Function("'use strict'; ({set x(q) {}, get x() {}, x:1})") threw exception of type SyntaxError.
-PASS Function("({set x(q) {}, get x() {}, x:1})") threw exception of type SyntaxError.
+FAIL Function("'use strict'; ({set x(q) {}, get x() {}, x:1})") should throw an instance of SyntaxError
+FAIL Function("({set x(q) {}, get x() {}, x:1})") should throw an instance of SyntaxError
 PASS true === true
-PASS Function("'use strict'; ({get x() {}, get x() {}})") threw exception of type SyntaxError.
-PASS Function("({get x() {}, get x() {}})") threw exception of type SyntaxError.
+FAIL Function("'use strict'; ({get x() {}, get x() {}})") should throw an instance of SyntaxError
+FAIL Function("({get x() {}, get x() {}})") should throw an instance of SyntaxError
 PASS true === true
 PASS Function("'use strict'; ({set x() {}, set x() {}})") threw exception of type SyntaxError.
 PASS Function("({set x() {}, set x() {}})") threw exception of type SyntaxError.
diff --git a/LayoutTests/js/object-literal-duplicate-properties-expected.txt b/LayoutTests/js/object-literal-duplicate-properties-expected.txt
new file mode 100644 (file)
index 0000000..e07f273
--- /dev/null
@@ -0,0 +1,211 @@
+basic tests for object literal duplicate properties
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+PASS x is 1
+PASS o.foo is 'getter'
+PASS x is 1
+PASS o.foo is 0
+PASS x is 1
+PASS o.foo is 'getter'
+PASS x is 1
+PASS o.foo is 0
+PASS x is 4
+PASS o.foo is 3
+PASS o.bar is undefined
+FAIL Object.keys(o).join() should be foo,test1,bar,test2,test3,nest. Was test1,test2,test3,foo,bar,nest.
+
+Basic
+PASS o = {foo:1}; descriptionString(o, 'foo'); is 'value:1 keys:1 [ECW][Extensible]'
+PASS 'use strict';o = {foo:1}; descriptionString(o, 'foo'); is 'value:1 keys:1 [ECW][Extensible]'
+PASS (function(){o = {foo:1}; descriptionString(o, 'foo');})() did not throw exception.
+PASS o = {foo:1}; o.foo = 2; descriptionString(o, 'foo'); is 'value:2 keys:1 [ECW][Extensible]'
+PASS 'use strict';o = {foo:1}; o.foo = 2; descriptionString(o, 'foo'); is 'value:2 keys:1 [ECW][Extensible]'
+PASS (function(){o = {foo:1}; o.foo = 2; descriptionString(o, 'foo');})() did not throw exception.
+PASS o = {foo:1, foo:3}; descriptionString(o, 'foo'); is 'value:3 keys:1 [ECW][Extensible]'
+PASS 'use strict';o = {foo:1, foo:3}; descriptionString(o, 'foo'); is 'value:3 keys:1 [ECW][Extensible]'
+PASS (function(){o = {foo:1, foo:3}; descriptionString(o, 'foo');})() did not throw exception.
+PASS o = {foo:1, fooo:2, foo:3}; descriptionString(o, 'foo'); is 'value:3 keys:2 [ECW][Extensible]'
+PASS 'use strict';o = {foo:1, fooo:2, foo:3}; descriptionString(o, 'foo'); is 'value:3 keys:2 [ECW][Extensible]'
+PASS (function(){o = {foo:1, fooo:2, foo:3}; descriptionString(o, 'foo');})() did not throw exception.
+PASS o = {foo:1, fooo:2, foo:3}; o.foo = 4; descriptionString(o, 'foo'); is 'value:4 keys:2 [ECW][Extensible]'
+PASS 'use strict';o = {foo:1, fooo:2, foo:3}; o.foo = 4; descriptionString(o, 'foo'); is 'value:4 keys:2 [ECW][Extensible]'
+PASS (function(){o = {foo:1, fooo:2, foo:3}; o.foo = 4; descriptionString(o, 'foo');})() did not throw exception.
+PASS o = {foo:1, fooo:2, foo:3, bar: 9}; descriptionString(o, 'foo'); is 'value:3 keys:3 [ECW][Extensible]'
+PASS 'use strict';o = {foo:1, fooo:2, foo:3, bar: 9}; descriptionString(o, 'foo'); is 'value:3 keys:3 [ECW][Extensible]'
+PASS (function(){o = {foo:1, fooo:2, foo:3, bar: 9}; descriptionString(o, 'foo');})() did not throw exception.
+PASS o = {foo:1, foo:3}; Object.defineProperty(o, 'foo', {value:5}); descriptionString(o, 'foo'); is 'value:5 keys:1 [ECW][Extensible]'
+PASS 'use strict';o = {foo:1, foo:3}; Object.defineProperty(o, 'foo', {value:5}); descriptionString(o, 'foo'); is 'value:5 keys:1 [ECW][Extensible]'
+PASS (function(){o = {foo:1, foo:3}; Object.defineProperty(o, 'foo', {value:5}); descriptionString(o, 'foo');})() did not throw exception.
+PASS o = {foo:1, foo:3}; Object.defineProperty(o, 'foo', {get(){return 5}}); descriptionString(o, 'foo'); is 'getter:function value:(5) keys:1 [EC][Extensible]'
+PASS 'use strict';o = {foo:1, foo:3}; Object.defineProperty(o, 'foo', {get(){return 5}}); descriptionString(o, 'foo'); is 'getter:function value:(5) keys:1 [EC][Extensible]'
+PASS (function(){o = {foo:1, foo:3}; Object.defineProperty(o, 'foo', {get(){return 5}}); descriptionString(o, 'foo');})() did not throw exception.
+PASS o = {foo:1, foo:3}; o.__defineGetter__('foo', function(){return 5}); descriptionString(o, 'foo'); is 'getter:function value:(5) keys:1 [EC][Extensible]'
+PASS 'use strict';o = {foo:1, foo:3}; o.__defineGetter__('foo', function(){return 5}); descriptionString(o, 'foo'); is 'getter:function value:(5) keys:1 [EC][Extensible]'
+PASS (function(){o = {foo:1, foo:3}; o.__defineGetter__('foo', function(){return 5}); descriptionString(o, 'foo');})() did not throw exception.
+PASS o = {foo:1, foo:3}; Object.seal(o);; descriptionString(o, 'foo'); is 'value:3 keys:1 [EW][Sealed]'
+PASS 'use strict';o = {foo:1, foo:3}; Object.seal(o);; descriptionString(o, 'foo'); is 'value:3 keys:1 [EW][Sealed]'
+PASS (function(){o = {foo:1, foo:3}; Object.seal(o);; descriptionString(o, 'foo');})() did not throw exception.
+PASS o = {foo:1, foo:3}; Object.seal(o); o.foo = 5; descriptionString(o, 'foo'); is 'value:5 keys:1 [EW][Sealed]'
+PASS 'use strict';o = {foo:1, foo:3}; Object.seal(o); o.foo = 5; descriptionString(o, 'foo'); is 'value:5 keys:1 [EW][Sealed]'
+PASS (function(){o = {foo:1, foo:3}; Object.seal(o); o.foo = 5; descriptionString(o, 'foo');})() did not throw exception.
+PASS o = {foo:1, foo:3}; Object.seal(o); Object.defineProperty(o, 'foo', {value:5}); descriptionString(o, 'foo'); is 'value:5 keys:1 [EW][Sealed]'
+PASS 'use strict';o = {foo:1, foo:3}; Object.seal(o); Object.defineProperty(o, 'foo', {value:5}); descriptionString(o, 'foo'); is 'value:5 keys:1 [EW][Sealed]'
+PASS (function(){o = {foo:1, foo:3}; Object.seal(o); Object.defineProperty(o, 'foo', {value:5}); descriptionString(o, 'foo');})() did not throw exception.
+PASS o = {foo:1, foo:3}; Object.seal(o); Object.defineProperty(o, 'foo', {get(){return 5}}); descriptionString(o, 'foo'); threw exception TypeError: Attempting to change access mechanism for an unconfigurable property..
+PASS 'use strict';o = {foo:1, foo:3}; Object.seal(o); Object.defineProperty(o, 'foo', {get(){return 5}}); descriptionString(o, 'foo'); threw exception TypeError: Attempting to change access mechanism for an unconfigurable property..
+PASS (function(){o = {foo:1, foo:3}; Object.seal(o); Object.defineProperty(o, 'foo', {get(){return 5}}); descriptionString(o, 'foo');})() threw exception TypeError: Attempting to change access mechanism for an unconfigurable property..
+PASS o = {foo:1, foo:3}; Object.seal(o); o.__defineGetter__('foo', function(){return 5}); descriptionString(o, 'foo'); is 'value:3 keys:1 [EW][Sealed]'
+PASS 'use strict';o = {foo:1, foo:3}; Object.seal(o); o.__defineGetter__('foo', function(){return 5}); descriptionString(o, 'foo'); is 'value:3 keys:1 [EW][Sealed]'
+PASS (function(){o = {foo:1, foo:3}; Object.seal(o); o.__defineGetter__('foo', function(){return 5}); descriptionString(o, 'foo');})() did not throw exception.
+
+Basic + Computed
+PASS o = {['foo']:1}; descriptionString(o, 'foo'); is 'value:1 keys:1 [ECW][Extensible]'
+PASS 'use strict';o = {['foo']:1}; descriptionString(o, 'foo'); is 'value:1 keys:1 [ECW][Extensible]'
+PASS (function(){o = {['foo']:1}; descriptionString(o, 'foo');})() did not throw exception.
+PASS o = {foo:1, ['foo']:2}; descriptionString(o, 'foo'); is 'value:2 keys:1 [ECW][Extensible]'
+PASS 'use strict';o = {foo:1, ['foo']:2}; descriptionString(o, 'foo'); is 'value:2 keys:1 [ECW][Extensible]'
+PASS (function(){o = {foo:1, ['foo']:2}; descriptionString(o, 'foo');})() did not throw exception.
+PASS o = {['foo']:1, foo:2}; descriptionString(o, 'foo'); is 'value:2 keys:1 [ECW][Extensible]'
+PASS 'use strict';o = {['foo']:1, foo:2}; descriptionString(o, 'foo'); is 'value:2 keys:1 [ECW][Extensible]'
+PASS (function(){o = {['foo']:1, foo:2}; descriptionString(o, 'foo');})() did not throw exception.
+PASS o = {foo:1, ['foo']:2, foo:3}; descriptionString(o, 'foo'); is 'value:3 keys:1 [ECW][Extensible]'
+PASS 'use strict';o = {foo:1, ['foo']:2, foo:3}; descriptionString(o, 'foo'); is 'value:3 keys:1 [ECW][Extensible]'
+PASS (function(){o = {foo:1, ['foo']:2, foo:3}; descriptionString(o, 'foo');})() did not throw exception.
+PASS o = {['foo']:1, foo:2, ['foo']:3}; descriptionString(o, 'foo'); is 'value:3 keys:1 [ECW][Extensible]'
+PASS 'use strict';o = {['foo']:1, foo:2, ['foo']:3}; descriptionString(o, 'foo'); is 'value:3 keys:1 [ECW][Extensible]'
+PASS (function(){o = {['foo']:1, foo:2, ['foo']:3}; descriptionString(o, 'foo');})() did not throw exception.
+PASS o = {['foo']:1, ['foo']:2}; descriptionString(o, 'foo'); is 'value:2 keys:1 [ECW][Extensible]'
+PASS 'use strict';o = {['foo']:1, ['foo']:2}; descriptionString(o, 'foo'); is 'value:2 keys:1 [ECW][Extensible]'
+PASS (function(){o = {['foo']:1, ['foo']:2}; descriptionString(o, 'foo');})() did not throw exception.
+PASS o = {foo:1, ['foo']:2}; o.foo = 3; descriptionString(o, 'foo'); is 'value:3 keys:1 [ECW][Extensible]'
+PASS 'use strict';o = {foo:1, ['foo']:2}; o.foo = 3; descriptionString(o, 'foo'); is 'value:3 keys:1 [ECW][Extensible]'
+PASS (function(){o = {foo:1, ['foo']:2}; o.foo = 3; descriptionString(o, 'foo');})() did not throw exception.
+PASS o = {['foo']:1, ['bar']:2}; descriptionString(o, 'foo'); is 'value:1 keys:2 [ECW][Extensible]'
+PASS 'use strict';o = {['foo']:1, ['bar']:2}; descriptionString(o, 'foo'); is 'value:1 keys:2 [ECW][Extensible]'
+PASS (function(){o = {['foo']:1, ['bar']:2}; descriptionString(o, 'foo');})() did not throw exception.
+PASS o = {['foo']:1, ['bar']:2, foo:3}; descriptionString(o, 'foo'); is 'value:3 keys:2 [ECW][Extensible]'
+PASS 'use strict';o = {['foo']:1, ['bar']:2, foo:3}; descriptionString(o, 'foo'); is 'value:3 keys:2 [ECW][Extensible]'
+PASS (function(){o = {['foo']:1, ['bar']:2, foo:3}; descriptionString(o, 'foo');})() did not throw exception.
+
+Basic + Accessor
+PASS o = {get foo(){return 2}}; descriptionString(o, 'foo'); is 'getter:function value:(2) keys:1 [EC][Extensible]'
+PASS 'use strict';o = {get foo(){return 2}}; descriptionString(o, 'foo'); is 'getter:function value:(2) keys:1 [EC][Extensible]'
+PASS (function(){o = {get foo(){return 2}}; descriptionString(o, 'foo');})() did not throw exception.
+PASS o = {set foo(x){}}; descriptionString(o, 'foo'); is 'setter:function keys:1 [EC][Extensible]'
+PASS 'use strict';o = {set foo(x){}}; descriptionString(o, 'foo'); is 'setter:function keys:1 [EC][Extensible]'
+PASS (function(){o = {set foo(x){}}; descriptionString(o, 'foo');})() did not throw exception.
+PASS o = {get foo(){return 1}, get foo(){return 2}}; descriptionString(o, 'foo'); is 'getter:function value:(2) keys:1 [EC][Extensible]'
+PASS 'use strict';o = {get foo(){return 1}, get foo(){return 2}}; descriptionString(o, 'foo'); is 'getter:function value:(2) keys:1 [EC][Extensible]'
+PASS (function(){o = {get foo(){return 1}, get foo(){return 2}}; descriptionString(o, 'foo');})() did not throw exception.
+PASS o = {get foo(){return 2}, set foo(x){}}; descriptionString(o, 'foo'); is 'getter:function value:(2) setter:function keys:1 [EC][Extensible]'
+PASS 'use strict';o = {get foo(){return 2}, set foo(x){}}; descriptionString(o, 'foo'); is 'getter:function value:(2) setter:function keys:1 [EC][Extensible]'
+PASS (function(){o = {get foo(){return 2}, set foo(x){}}; descriptionString(o, 'foo');})() did not throw exception.
+PASS o = {get foo(){return 2}, set foo(x){}, get foo(){return 3}}; descriptionString(o, 'foo'); is 'getter:function value:(3) setter:function keys:1 [EC][Extensible]'
+PASS 'use strict';o = {get foo(){return 2}, set foo(x){}, get foo(){return 3}}; descriptionString(o, 'foo'); is 'getter:function value:(3) setter:function keys:1 [EC][Extensible]'
+PASS (function(){o = {get foo(){return 2}, set foo(x){}, get foo(){return 3}}; descriptionString(o, 'foo');})() did not throw exception.
+PASS o = {bar:1, get foo(){return 2}, set foo(x){}, baz:1}; descriptionString(o, 'foo'); is 'getter:function value:(2) setter:function keys:3 [EC][Extensible]'
+PASS 'use strict';o = {bar:1, get foo(){return 2}, set foo(x){}, baz:1}; descriptionString(o, 'foo'); is 'getter:function value:(2) setter:function keys:3 [EC][Extensible]'
+PASS (function(){o = {bar:1, get foo(){return 2}, set foo(x){}, baz:1}; descriptionString(o, 'foo');})() did not throw exception.
+PASS o = {foo:1, get foo(){return 2}}; descriptionString(o, 'foo'); is 'getter:function value:(2) keys:1 [EC][Extensible]'
+PASS 'use strict';o = {foo:1, get foo(){return 2}}; descriptionString(o, 'foo'); is 'getter:function value:(2) keys:1 [EC][Extensible]'
+PASS (function(){o = {foo:1, get foo(){return 2}}; descriptionString(o, 'foo');})() did not throw exception.
+PASS o = {foo:1, get foo(){return 2}, set foo(x){}}; descriptionString(o, 'foo'); is 'getter:function value:(2) setter:function keys:1 [EC][Extensible]'
+PASS 'use strict';o = {foo:1, get foo(){return 2}, set foo(x){}}; descriptionString(o, 'foo'); is 'getter:function value:(2) setter:function keys:1 [EC][Extensible]'
+PASS (function(){o = {foo:1, get foo(){return 2}, set foo(x){}}; descriptionString(o, 'foo');})() did not throw exception.
+PASS o = {get foo(){return 2}, foo:1}; descriptionString(o, 'foo'); is 'value:1 keys:1 [ECW][Extensible]'
+PASS 'use strict';o = {get foo(){return 2}, foo:1}; descriptionString(o, 'foo'); is 'value:1 keys:1 [ECW][Extensible]'
+PASS (function(){o = {get foo(){return 2}, foo:1}; descriptionString(o, 'foo');})() did not throw exception.
+PASS o = {get foo(){return 2}, set foo(x){}, foo:1}; descriptionString(o, 'foo'); is 'value:1 keys:1 [ECW][Extensible]'
+PASS 'use strict';o = {get foo(){return 2}, set foo(x){}, foo:1}; descriptionString(o, 'foo'); is 'value:1 keys:1 [ECW][Extensible]'
+PASS (function(){o = {get foo(){return 2}, set foo(x){}, foo:1}; descriptionString(o, 'foo');})() did not throw exception.
+PASS o = {foo:1, get foo(){return -1}, foo:2}; descriptionString(o, 'foo'); is 'value:2 keys:1 [ECW][Extensible]'
+PASS 'use strict';o = {foo:1, get foo(){return -1}, foo:2}; descriptionString(o, 'foo'); is 'value:2 keys:1 [ECW][Extensible]'
+PASS (function(){o = {foo:1, get foo(){return -1}, foo:2}; descriptionString(o, 'foo');})() did not throw exception.
+PASS o = {foo:1, get foo(){return 2}, bar:3}; descriptionString(o, 'foo'); is 'getter:function value:(2) keys:2 [EC][Extensible]'
+PASS 'use strict';o = {foo:1, get foo(){return 2}, bar:3}; descriptionString(o, 'foo'); is 'getter:function value:(2) keys:2 [EC][Extensible]'
+PASS (function(){o = {foo:1, get foo(){return 2}, bar:3}; descriptionString(o, 'foo');})() did not throw exception.
+PASS o = {foo:1, get foo(){return -1}, foo:-1, get foo() {return 2}}; descriptionString(o, 'foo'); is 'getter:function value:(2) keys:1 [EC][Extensible]'
+PASS 'use strict';o = {foo:1, get foo(){return -1}, foo:-1, get foo() {return 2}}; descriptionString(o, 'foo'); is 'getter:function value:(2) keys:1 [EC][Extensible]'
+PASS (function(){o = {foo:1, get foo(){return -1}, foo:-1, get foo() {return 2}}; descriptionString(o, 'foo');})() did not throw exception.
+PASS o = {foo:1, get foo(){return 3}}; Object.defineProperty(o, 'foo', {value:5}); descriptionString(o, 'foo'); is 'value:5 keys:1 [EC][Extensible]'
+PASS 'use strict';o = {foo:1, get foo(){return 3}}; Object.defineProperty(o, 'foo', {value:5}); descriptionString(o, 'foo'); is 'value:5 keys:1 [EC][Extensible]'
+PASS (function(){o = {foo:1, get foo(){return 3}}; Object.defineProperty(o, 'foo', {value:5}); descriptionString(o, 'foo');})() did not throw exception.
+PASS o = {foo:1, get foo(){return 3}}; Object.defineProperty(o, 'foo', {get(){return 5}}); descriptionString(o, 'foo'); is 'getter:function value:(5) keys:1 [EC][Extensible]'
+PASS 'use strict';o = {foo:1, get foo(){return 3}}; Object.defineProperty(o, 'foo', {get(){return 5}}); descriptionString(o, 'foo'); is 'getter:function value:(5) keys:1 [EC][Extensible]'
+PASS (function(){o = {foo:1, get foo(){return 3}}; Object.defineProperty(o, 'foo', {get(){return 5}}); descriptionString(o, 'foo');})() did not throw exception.
+PASS o = {foo:1, get foo(){return 3}}; o.__defineGetter__('foo', function(){return 5}); descriptionString(o, 'foo'); is 'getter:function value:(5) keys:1 [EC][Extensible]'
+PASS 'use strict';o = {foo:1, get foo(){return 3}}; o.__defineGetter__('foo', function(){return 5}); descriptionString(o, 'foo'); is 'getter:function value:(5) keys:1 [EC][Extensible]'
+PASS (function(){o = {foo:1, get foo(){return 3}}; o.__defineGetter__('foo', function(){return 5}); descriptionString(o, 'foo');})() did not throw exception.
+PASS o = {foo:1, get foo(){return 3}, set foo(x){}}; Object.seal(o); o.foo = 5; descriptionString(o, 'foo'); is 'getter:function value:(3) setter:function keys:1 [E][Sealed][Frozen]'
+PASS 'use strict';o = {foo:1, get foo(){return 3}, set foo(x){}}; Object.seal(o); o.foo = 5; descriptionString(o, 'foo'); is 'getter:function value:(3) setter:function keys:1 [E][Sealed][Frozen]'
+PASS (function(){o = {foo:1, get foo(){return 3}, set foo(x){}}; Object.seal(o); o.foo = 5; descriptionString(o, 'foo');})() did not throw exception.
+PASS o = {foo:1, get foo(){return 3}}; Object.seal(o);; descriptionString(o, 'foo'); is 'getter:function value:(3) keys:1 [E][Sealed][Frozen]'
+PASS 'use strict';o = {foo:1, get foo(){return 3}}; Object.seal(o);; descriptionString(o, 'foo'); is 'getter:function value:(3) keys:1 [E][Sealed][Frozen]'
+PASS (function(){o = {foo:1, get foo(){return 3}}; Object.seal(o);; descriptionString(o, 'foo');})() did not throw exception.
+PASS o = {foo:1, get foo(){return 3}}; Object.seal(o); Object.defineProperty(o, 'foo', {get(){return 5}}); descriptionString(o, 'foo'); threw exception TypeError: Attempting to change the getter of an unconfigurable property..
+PASS 'use strict';o = {foo:1, get foo(){return 3}}; Object.seal(o); Object.defineProperty(o, 'foo', {get(){return 5}}); descriptionString(o, 'foo'); threw exception TypeError: Attempting to change the getter of an unconfigurable property..
+PASS (function(){o = {foo:1, get foo(){return 3}}; Object.seal(o); Object.defineProperty(o, 'foo', {get(){return 5}}); descriptionString(o, 'foo');})() threw exception TypeError: Attempting to change the getter of an unconfigurable property..
+PASS o = {foo:1, get foo(){return 3}}; Object.seal(o); Object.defineProperty(o, 'foo', {value:5}); descriptionString(o, 'foo'); threw exception TypeError: Attempting to change access mechanism for an unconfigurable property..
+PASS 'use strict';o = {foo:1, get foo(){return 3}}; Object.seal(o); Object.defineProperty(o, 'foo', {value:5}); descriptionString(o, 'foo'); threw exception TypeError: Attempting to change access mechanism for an unconfigurable property..
+PASS (function(){o = {foo:1, get foo(){return 3}}; Object.seal(o); Object.defineProperty(o, 'foo', {value:5}); descriptionString(o, 'foo');})() threw exception TypeError: Attempting to change access mechanism for an unconfigurable property..
+PASS o = {foo:1, get foo(){return 3}}; Object.seal(o); o.__defineGetter__('foo', function(){return 5}); descriptionString(o, 'foo'); is 'getter:function value:(3) keys:1 [E][Sealed][Frozen]'
+PASS 'use strict';o = {foo:1, get foo(){return 3}}; Object.seal(o); o.__defineGetter__('foo', function(){return 5}); descriptionString(o, 'foo'); is 'getter:function value:(3) keys:1 [E][Sealed][Frozen]'
+PASS (function(){o = {foo:1, get foo(){return 3}}; Object.seal(o); o.__defineGetter__('foo', function(){return 5}); descriptionString(o, 'foo');})() did not throw exception.
+
+Computed + Accessor
+PASS o = {['foo']:1, get foo(){return 2}}; descriptionString(o, 'foo'); is 'getter:function value:(2) keys:1 [EC][Extensible]'
+PASS 'use strict';o = {['foo']:1, get foo(){return 2}}; descriptionString(o, 'foo'); is 'getter:function value:(2) keys:1 [EC][Extensible]'
+PASS (function(){o = {['foo']:1, get foo(){return 2}}; descriptionString(o, 'foo');})() did not throw exception.
+PASS o = {['foo']:1, get foo(){return 2}, set foo(x){}}; descriptionString(o, 'foo'); is 'getter:function value:(2) setter:function keys:1 [EC][Extensible]'
+PASS 'use strict';o = {['foo']:1, get foo(){return 2}, set foo(x){}}; descriptionString(o, 'foo'); is 'getter:function value:(2) setter:function keys:1 [EC][Extensible]'
+PASS (function(){o = {['foo']:1, get foo(){return 2}, set foo(x){}}; descriptionString(o, 'foo');})() did not throw exception.
+PASS o = {get foo(){return 2}, ['foo']:1}; descriptionString(o, 'foo'); is 'value:1 keys:1 [ECW][Extensible]'
+PASS 'use strict';o = {get foo(){return 2}, ['foo']:1}; descriptionString(o, 'foo'); is 'value:1 keys:1 [ECW][Extensible]'
+PASS (function(){o = {get foo(){return 2}, ['foo']:1}; descriptionString(o, 'foo');})() did not throw exception.
+PASS o = {get foo(){return 2}, set foo(x){}, ['foo']:1}; descriptionString(o, 'foo'); is 'value:1 keys:1 [ECW][Extensible]'
+PASS 'use strict';o = {get foo(){return 2}, set foo(x){}, ['foo']:1}; descriptionString(o, 'foo'); is 'value:1 keys:1 [ECW][Extensible]'
+PASS (function(){o = {get foo(){return 2}, set foo(x){}, ['foo']:1}; descriptionString(o, 'foo');})() did not throw exception.
+PASS o = {['foo']:1, get foo(){return -1}, ['foo']:2}; descriptionString(o, 'foo'); is 'value:2 keys:1 [ECW][Extensible]'
+PASS 'use strict';o = {['foo']:1, get foo(){return -1}, ['foo']:2}; descriptionString(o, 'foo'); is 'value:2 keys:1 [ECW][Extensible]'
+PASS (function(){o = {['foo']:1, get foo(){return -1}, ['foo']:2}; descriptionString(o, 'foo');})() did not throw exception.
+PASS o = {['foo']:1, get foo(){return 2}, ['bar']:3}; descriptionString(o, 'foo'); is 'getter:function value:(2) keys:2 [EC][Extensible]'
+PASS 'use strict';o = {['foo']:1, get foo(){return 2}, ['bar']:3}; descriptionString(o, 'foo'); is 'getter:function value:(2) keys:2 [EC][Extensible]'
+PASS (function(){o = {['foo']:1, get foo(){return 2}, ['bar']:3}; descriptionString(o, 'foo');})() did not throw exception.
+PASS o = {['foo']:1, get foo(){return -1}, ['foo']:-1, get foo() {return 2}}; descriptionString(o, 'foo'); is 'getter:function value:(2) keys:1 [EC][Extensible]'
+PASS 'use strict';o = {['foo']:1, get foo(){return -1}, ['foo']:-1, get foo() {return 2}}; descriptionString(o, 'foo'); is 'getter:function value:(2) keys:1 [EC][Extensible]'
+PASS (function(){o = {['foo']:1, get foo(){return -1}, ['foo']:-1, get foo() {return 2}}; descriptionString(o, 'foo');})() did not throw exception.
+
+Basic + Computed + Accessor
+PASS o = {foo:1, get foo(){return 2}, ['foo']:3}; descriptionString(o, 'foo'); is 'value:3 keys:1 [ECW][Extensible]'
+PASS 'use strict';o = {foo:1, get foo(){return 2}, ['foo']:3}; descriptionString(o, 'foo'); is 'value:3 keys:1 [ECW][Extensible]'
+PASS (function(){o = {foo:1, get foo(){return 2}, ['foo']:3}; descriptionString(o, 'foo');})() did not throw exception.
+PASS o = {foo:1, ['foo']:3, get foo(){return 2}}; descriptionString(o, 'foo'); is 'getter:function value:(2) keys:1 [EC][Extensible]'
+PASS 'use strict';o = {foo:1, ['foo']:3, get foo(){return 2}}; descriptionString(o, 'foo'); is 'getter:function value:(2) keys:1 [EC][Extensible]'
+PASS (function(){o = {foo:1, ['foo']:3, get foo(){return 2}}; descriptionString(o, 'foo');})() did not throw exception.
+PASS o = {foo:1, ['foo']:3, get foo(){return 2}, set foo(x){}}; descriptionString(o, 'foo'); is 'getter:function value:(2) setter:function keys:1 [EC][Extensible]'
+PASS 'use strict';o = {foo:1, ['foo']:3, get foo(){return 2}, set foo(x){}}; descriptionString(o, 'foo'); is 'getter:function value:(2) setter:function keys:1 [EC][Extensible]'
+PASS (function(){o = {foo:1, ['foo']:3, get foo(){return 2}, set foo(x){}}; descriptionString(o, 'foo');})() did not throw exception.
+PASS o = {['foo']:3, get foo(){return 2}, foo:1}; descriptionString(o, 'foo'); is 'value:1 keys:1 [ECW][Extensible]'
+PASS 'use strict';o = {['foo']:3, get foo(){return 2}, foo:1}; descriptionString(o, 'foo'); is 'value:1 keys:1 [ECW][Extensible]'
+PASS (function(){o = {['foo']:3, get foo(){return 2}, foo:1}; descriptionString(o, 'foo');})() did not throw exception.
+PASS o = {get foo(){return 2}, ['foo']:3, foo:1}; descriptionString(o, 'foo'); is 'value:1 keys:1 [ECW][Extensible]'
+PASS 'use strict';o = {get foo(){return 2}, ['foo']:3, foo:1}; descriptionString(o, 'foo'); is 'value:1 keys:1 [ECW][Extensible]'
+PASS (function(){o = {get foo(){return 2}, ['foo']:3, foo:1}; descriptionString(o, 'foo');})() did not throw exception.
+PASS o = {get foo(){return 2}, ['foo']:3, get foo(){return 2}, foo:1}; descriptionString(o, 'foo'); is 'value:1 keys:1 [ECW][Extensible]'
+PASS 'use strict';o = {get foo(){return 2}, ['foo']:3, get foo(){return 2}, foo:1}; descriptionString(o, 'foo'); is 'value:1 keys:1 [ECW][Extensible]'
+PASS (function(){o = {get foo(){return 2}, ['foo']:3, get foo(){return 2}, foo:1}; descriptionString(o, 'foo');})() did not throw exception.
+PASS o = {set foo(x){}, ['foo']:3, set foo(x){}, foo:1}; descriptionString(o, 'foo'); is 'value:1 keys:1 [ECW][Extensible]'
+PASS 'use strict';o = {set foo(x){}, ['foo']:3, set foo(x){}, foo:1}; descriptionString(o, 'foo'); is 'value:1 keys:1 [ECW][Extensible]'
+PASS (function(){o = {set foo(x){}, ['foo']:3, set foo(x){}, foo:1}; descriptionString(o, 'foo');})() did not throw exception.
+PASS o = {set foo(x){}, foo:1, get foo(){return 2}, ['foo']:3}; descriptionString(o, 'foo'); is 'value:3 keys:1 [ECW][Extensible]'
+PASS 'use strict';o = {set foo(x){}, foo:1, get foo(){return 2}, ['foo']:3}; descriptionString(o, 'foo'); is 'value:3 keys:1 [ECW][Extensible]'
+PASS (function(){o = {set foo(x){}, foo:1, get foo(){return 2}, ['foo']:3}; descriptionString(o, 'foo');})() did not throw exception.
+PASS o = {get foo(){return 2}, get foo(){return 2}, ['foo']:3, foo:1}; descriptionString(o, 'foo'); is 'value:1 keys:1 [ECW][Extensible]'
+PASS 'use strict';o = {get foo(){return 2}, get foo(){return 2}, ['foo']:3, foo:1}; descriptionString(o, 'foo'); is 'value:1 keys:1 [ECW][Extensible]'
+PASS (function(){o = {get foo(){return 2}, get foo(){return 2}, ['foo']:3, foo:1}; descriptionString(o, 'foo');})() did not throw exception.
+PASS successfullyParsed is true
+
+TEST COMPLETE
+
diff --git a/LayoutTests/js/object-literal-duplicate-properties.html b/LayoutTests/js/object-literal-duplicate-properties.html
new file mode 100644 (file)
index 0000000..85909a8
--- /dev/null
@@ -0,0 +1,10 @@
+<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN">
+<html>
+<head>
+<script src="../resources/js-test-pre.js"></script>
+</head>
+<body>
+<script src="script-tests/object-literal-duplicate-properties.js"></script>
+<script src="../resources/js-test-post.js"></script>
+</body>
+</html>
index bd7478c..d4bc652 100644 (file)
@@ -3,20 +3,20 @@ Make sure that we correctly identify parse errors in object literals
 On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
 
 
-PASS ({a:1, get a(){}}) threw exception SyntaxError: Attempted to redefine property 'a'..
-PASS ({a:1, set a(x){}}) threw exception SyntaxError: Attempted to redefine property 'a'..
-PASS ({get a(){}, a:1}) threw exception SyntaxError: Attempted to redefine property 'a'..
-PASS ({set a(x){}, a:1}) threw exception SyntaxError: Attempted to redefine property 'a'..
-PASS ({get a(){}, get a(){}}) threw exception SyntaxError: Attempted to redefine property 'a'..
-PASS ({set a(x){}, set a(x){}}) threw exception SyntaxError: Attempted to redefine property 'a'..
-PASS ({set a(x){}, get a(){}, set a(x){}}) threw exception SyntaxError: Attempted to redefine property 'a'..
-PASS (function(){({a:1, get a(){}})}) threw exception SyntaxError: Attempted to redefine property 'a'..
-PASS (function(){({a:1, set a(x){}})}) threw exception SyntaxError: Attempted to redefine property 'a'..
-PASS (function(){({get a(){}, a:1})}) threw exception SyntaxError: Attempted to redefine property 'a'..
-PASS (function(){({set a(x){}, a:1})}) threw exception SyntaxError: Attempted to redefine property 'a'..
-PASS (function(){({get a(){}, get a(){}})}) threw exception SyntaxError: Attempted to redefine property 'a'..
-PASS (function(){({set a(x){}, set a(x){}})}) threw exception SyntaxError: Attempted to redefine property 'a'..
-PASS (function(){({set a(x){}, get a(){}, set a(x){}})}) threw exception SyntaxError: Attempted to redefine property 'a'..
+PASS ({a:1, get a(){}}) did not throw exception.
+PASS ({a:1, set a(x){}}) did not throw exception.
+PASS ({get a(){}, a:1}) did not throw exception.
+PASS ({set a(x){}, a:1}) did not throw exception.
+PASS ({get a(){}, get a(){}}) did not throw exception.
+PASS ({set a(x){}, set a(x){}}) did not throw exception.
+PASS ({set a(x){}, get a(){}, set a(x){}}) did not throw exception.
+PASS (function(){({a:1, get a(){}})}) did not throw exception.
+PASS (function(){({a:1, set a(x){}})}) did not throw exception.
+PASS (function(){({get a(){}, a:1})}) did not throw exception.
+PASS (function(){({set a(x){}, a:1})}) did not throw exception.
+PASS (function(){({get a(){}, get a(){}})}) did not throw exception.
+PASS (function(){({set a(x){}, set a(x){}})}) did not throw exception.
+PASS (function(){({set a(x){}, get a(){}, set a(x){}})}) did not throw exception.
 PASS ({a:1, a:1, a:1}), true is true
 PASS ({get a(){}, set a(x){}}), true is true
 PASS ({set a(x){}, get a(){}}), true is true
diff --git a/LayoutTests/js/script-tests/object-literal-duplicate-properties.js b/LayoutTests/js/script-tests/object-literal-duplicate-properties.js
new file mode 100644 (file)
index 0000000..9aabc19
--- /dev/null
@@ -0,0 +1,184 @@
+description("basic tests for object literal duplicate properties");
+
+// Ensure basic properties get eliminated by getters.
+x = 0;
+o = {
+    foo: x++,
+    get foo() {return "getter"},
+};
+shouldBe("x", "1");
+shouldBe("o.foo", "'getter'");
+
+// Ensure getters/setters get eliminated by computed properties.
+x = 0;
+o = {
+    get foo() {return "getter"},
+    foo: x++,
+};
+shouldBe("x", "1");
+shouldBe("o.foo", "0");
+
+// Ensure computed properties are eliminated by getters/setters.
+x = 0;
+o = {
+    ['foo']: x++,
+    get foo() {return "getter"},
+};
+shouldBe("x", "1");
+shouldBe("o.foo", "'getter'");
+
+// Ensure getters/setters properties are eliminated by computed properties.
+x = 0;
+o = {
+    get foo() {return "getter"},
+    set foo(x) {},
+    ['foo']: x++,
+};
+shouldBe("x", "1");
+shouldBe("o.foo", "0");
+
+// Multiple types and multiple properties.
+x = 0;
+o = {
+    get foo() { return "NO"; },
+    foo: x++,
+    set test1(x) {},
+    bar: x++,
+    get test2() {},
+    ['test3']: x++,
+    get foo() {return "getter"},
+    ['foo']: x++,
+    set bar(x) {},
+    nest: {foo:1, get foo(){}, bar:1, set foo(x){}},
+};
+shouldBe("x", "4");
+shouldBe("o.foo", "3");
+shouldBe("o.bar", "undefined");
+// FIXME: <https://webkit.org/b/142933> Redefining a property should not change its insertion index (Object.keys order)
+shouldBe("Object.keys(o).join()", "'foo,test1,bar,test2,test3,nest'");
+
+
+
+function descriptionString(o, property) {
+    var descriptor = Object.getOwnPropertyDescriptor(o, property);
+    if (!descriptor)
+        return "PROPERTY NOT FOUND";
+
+    var string = "";
+    if (descriptor.value)
+        string += "value:" + String(descriptor.value) + " ";
+    if (descriptor.get)
+        string += "getter:" + typeof descriptor.get + " value:(" + o[property] + ") ";
+    if (descriptor.set)
+        string += "setter:" + typeof descriptor.set + " ";
+
+    string += "keys:" + Object.keys(o).length + " ";
+
+    string += "[";
+    if (descriptor.enumerable)
+        string += "E";
+    if (descriptor.configurable)
+        string += "C";
+    if (descriptor.writable)
+        string += "W";
+    string += "]";
+
+    if (Object.isSealed(o))
+        string += "[Sealed]";
+    if (Object.isFrozen(o))
+        string += "[Frozen]";
+    if (Object.isExtensible(o))
+        string += "[Extensible]";
+
+    return string;
+}
+
+function runTest(test, expected) {
+    test = test + "; descriptionString(o, 'foo');";
+    if (expected) {
+        shouldBe(test, expected);
+        shouldBe("'use strict';" + test, expected);
+        shouldNotThrow("(function(){" + test + "})()");
+    } else {
+        shouldThrow(test);
+        shouldThrow("'use strict';" + test);
+        shouldThrow("(function(){" + test + "})()");
+    }
+}
+
+// Basic properties.
+debug(""); debug("Basic");
+runTest("o = {foo:1}", "'value:1 keys:1 [ECW][Extensible]'");
+runTest("o = {foo:1}; o.foo = 2", "'value:2 keys:1 [ECW][Extensible]'");
+runTest("o = {foo:1, foo:3}", "'value:3 keys:1 [ECW][Extensible]'");
+runTest("o = {foo:1, fooo:2, foo:3}", "'value:3 keys:2 [ECW][Extensible]'");
+runTest("o = {foo:1, fooo:2, foo:3}; o.foo = 4", "'value:4 keys:2 [ECW][Extensible]'");
+runTest("o = {foo:1, fooo:2, foo:3, bar: 9}", "'value:3 keys:3 [ECW][Extensible]'");
+runTest("o = {foo:1, foo:3}; Object.defineProperty(o, 'foo', {value:5})", "'value:5 keys:1 [ECW][Extensible]'");
+runTest("o = {foo:1, foo:3}; Object.defineProperty(o, 'foo', {get(){return 5}})", "'getter:function value:(5) keys:1 [EC][Extensible]'");
+runTest("o = {foo:1, foo:3}; o.__defineGetter__('foo', function(){return 5})", "'getter:function value:(5) keys:1 [EC][Extensible]'");
+runTest("o = {foo:1, foo:3}; Object.seal(o);", "'value:3 keys:1 [EW][Sealed]'");
+runTest("o = {foo:1, foo:3}; Object.seal(o); o.foo = 5", "'value:5 keys:1 [EW][Sealed]'");
+runTest("o = {foo:1, foo:3}; Object.seal(o); Object.defineProperty(o, 'foo', {value:5})", "'value:5 keys:1 [EW][Sealed]'");
+runTest("o = {foo:1, foo:3}; Object.seal(o); Object.defineProperty(o, 'foo', {get(){return 5}})", null);
+// FIXME: <https://webkit.org/b/142934> __defineGetter__/__defineSetter__ should throw exceptions
+runTest("o = {foo:1, foo:3}; Object.seal(o); o.__defineGetter__('foo', function(){return 5})", "'value:3 keys:1 [EW][Sealed]'");
+
+// Basic properties with Computed properties.
+debug(""); debug("Basic + Computed");
+runTest("o = {['foo']:1}", "'value:1 keys:1 [ECW][Extensible]'");
+runTest("o = {foo:1, ['foo']:2}", "'value:2 keys:1 [ECW][Extensible]'");
+runTest("o = {['foo']:1, foo:2}", "'value:2 keys:1 [ECW][Extensible]'");
+runTest("o = {foo:1, ['foo']:2, foo:3}", "'value:3 keys:1 [ECW][Extensible]'");
+runTest("o = {['foo']:1, foo:2, ['foo']:3}", "'value:3 keys:1 [ECW][Extensible]'");
+runTest("o = {['foo']:1, ['foo']:2}", "'value:2 keys:1 [ECW][Extensible]'");
+runTest("o = {foo:1, ['foo']:2}; o.foo = 3", "'value:3 keys:1 [ECW][Extensible]'");
+runTest("o = {['foo']:1, ['bar']:2}", "'value:1 keys:2 [ECW][Extensible]'");
+runTest("o = {['foo']:1, ['bar']:2, foo:3}", "'value:3 keys:2 [ECW][Extensible]'");
+
+// Basic properties with Accessor properties.
+debug(""); debug("Basic + Accessor");
+runTest("o = {get foo(){return 2}}", "'getter:function value:(2) keys:1 [EC][Extensible]'");
+runTest("o = {set foo(x){}}", "'setter:function keys:1 [EC][Extensible]'");
+runTest("o = {get foo(){return 1}, get foo(){return 2}}", "'getter:function value:(2) keys:1 [EC][Extensible]'");
+runTest("o = {get foo(){return 2}, set foo(x){}}", "'getter:function value:(2) setter:function keys:1 [EC][Extensible]'");
+runTest("o = {get foo(){return 2}, set foo(x){}, get foo(){return 3}}", "'getter:function value:(3) setter:function keys:1 [EC][Extensible]'");
+runTest("o = {bar:1, get foo(){return 2}, set foo(x){}, baz:1}", "'getter:function value:(2) setter:function keys:3 [EC][Extensible]'");
+runTest("o = {foo:1, get foo(){return 2}}", "'getter:function value:(2) keys:1 [EC][Extensible]'");
+runTest("o = {foo:1, get foo(){return 2}, set foo(x){}}", "'getter:function value:(2) setter:function keys:1 [EC][Extensible]'");
+runTest("o = {get foo(){return 2}, foo:1}", "'value:1 keys:1 [ECW][Extensible]'");
+runTest("o = {get foo(){return 2}, set foo(x){}, foo:1}", "'value:1 keys:1 [ECW][Extensible]'");
+runTest("o = {foo:1, get foo(){return -1}, foo:2}", "'value:2 keys:1 [ECW][Extensible]'");
+runTest("o = {foo:1, get foo(){return 2}, bar:3}", "'getter:function value:(2) keys:2 [EC][Extensible]'");
+runTest("o = {foo:1, get foo(){return -1}, foo:-1, get foo() {return 2}}", "'getter:function value:(2) keys:1 [EC][Extensible]'");
+runTest("o = {foo:1, get foo(){return 3}}; Object.defineProperty(o, 'foo', {value:5})", "'value:5 keys:1 [EC][Extensible]'");
+runTest("o = {foo:1, get foo(){return 3}}; Object.defineProperty(o, 'foo', {get(){return 5}})", "'getter:function value:(5) keys:1 [EC][Extensible]'");
+runTest("o = {foo:1, get foo(){return 3}}; o.__defineGetter__('foo', function(){return 5})", "'getter:function value:(5) keys:1 [EC][Extensible]'");
+runTest("o = {foo:1, get foo(){return 3}, set foo(x){}}; Object.seal(o); o.foo = 5", "'getter:function value:(3) setter:function keys:1 [E][Sealed][Frozen]'");
+runTest("o = {foo:1, get foo(){return 3}}; Object.seal(o);", "'getter:function value:(3) keys:1 [E][Sealed][Frozen]'");
+runTest("o = {foo:1, get foo(){return 3}}; Object.seal(o); Object.defineProperty(o, 'foo', {get(){return 5}})", null);
+runTest("o = {foo:1, get foo(){return 3}}; Object.seal(o); Object.defineProperty(o, 'foo', {value:5})", null);
+// FIXME: <https://webkit.org/b/142934> __defineGetter__/__defineSetter__ should throw exceptions
+runTest("o = {foo:1, get foo(){return 3}}; Object.seal(o); o.__defineGetter__('foo', function(){return 5})", "'getter:function value:(3) keys:1 [E][Sealed][Frozen]'");
+
+// Computed properties with Accessor properties.
+debug(""); debug("Computed + Accessor");
+runTest("o = {['foo']:1, get foo(){return 2}}", "'getter:function value:(2) keys:1 [EC][Extensible]'");
+runTest("o = {['foo']:1, get foo(){return 2}, set foo(x){}}", "'getter:function value:(2) setter:function keys:1 [EC][Extensible]'");
+runTest("o = {get foo(){return 2}, ['foo']:1}", "'value:1 keys:1 [ECW][Extensible]'");
+runTest("o = {get foo(){return 2}, set foo(x){}, ['foo']:1}", "'value:1 keys:1 [ECW][Extensible]'");
+runTest("o = {['foo']:1, get foo(){return -1}, ['foo']:2}", "'value:2 keys:1 [ECW][Extensible]'");
+runTest("o = {['foo']:1, get foo(){return 2}, ['bar']:3}", "'getter:function value:(2) keys:2 [EC][Extensible]'");
+runTest("o = {['foo']:1, get foo(){return -1}, ['foo']:-1, get foo() {return 2}}", "'getter:function value:(2) keys:1 [EC][Extensible]'");
+
+// Basic properties, Computed properties, and Accessor properties.
+debug(""); debug("Basic + Computed + Accessor");
+runTest("o = {foo:1, get foo(){return 2}, ['foo']:3}", "'value:3 keys:1 [ECW][Extensible]'");
+runTest("o = {foo:1, ['foo']:3, get foo(){return 2}}", "'getter:function value:(2) keys:1 [EC][Extensible]'");
+runTest("o = {foo:1, ['foo']:3, get foo(){return 2}, set foo(x){}}", "'getter:function value:(2) setter:function keys:1 [EC][Extensible]'");
+runTest("o = {['foo']:3, get foo(){return 2}, foo:1}", "'value:1 keys:1 [ECW][Extensible]'");
+runTest("o = {get foo(){return 2}, ['foo']:3, foo:1}", "'value:1 keys:1 [ECW][Extensible]'");
+runTest("o = {get foo(){return 2}, ['foo']:3, get foo(){return 2}, foo:1}", "'value:1 keys:1 [ECW][Extensible]'");
+runTest("o = {set foo(x){}, ['foo']:3, set foo(x){}, foo:1}", "'value:1 keys:1 [ECW][Extensible]'");
+runTest("o = {set foo(x){}, foo:1, get foo(){return 2}, ['foo']:3}", "'value:3 keys:1 [ECW][Extensible]'");
+runTest("o = {get foo(){return 2}, get foo(){return 2}, ['foo']:3, foo:1}", "'value:1 keys:1 [ECW][Extensible]'");
index 3e4041f..c4a4586 100644 (file)
@@ -1,19 +1,20 @@
 description("Make sure that we correctly identify parse errors in object literals");
 
-shouldThrow("({a:1, get a(){}})");
-shouldThrow("({a:1, set a(x){}})");
-shouldThrow("({get a(){}, a:1})");
-shouldThrow("({set a(x){}, a:1})");
-shouldThrow("({get a(){}, get a(){}})");
-shouldThrow("({set a(x){}, set a(x){}})");
-shouldThrow("({set a(x){}, get a(){}, set a(x){}})");
-shouldThrow("(function(){({a:1, get a(){}})})");
-shouldThrow("(function(){({a:1, set a(x){}})})");
-shouldThrow("(function(){({get a(){}, a:1})})");
-shouldThrow("(function(){({set a(x){}, a:1})})");
-shouldThrow("(function(){({get a(){}, get a(){}})})");
-shouldThrow("(function(){({set a(x){}, set a(x){}})})");
-shouldThrow("(function(){({set a(x){}, get a(){}, set a(x){}})})");
+shouldNotThrow("({a:1, get a(){}})");
+shouldNotThrow("({a:1, set a(x){}})");
+shouldNotThrow("({get a(){}, a:1})");
+shouldNotThrow("({set a(x){}, a:1})");
+shouldNotThrow("({get a(){}, get a(){}})");
+shouldNotThrow("({set a(x){}, set a(x){}})");
+shouldNotThrow("({set a(x){}, get a(){}, set a(x){}})");
+shouldNotThrow("(function(){({a:1, get a(){}})})");
+shouldNotThrow("(function(){({a:1, set a(x){}})})");
+shouldNotThrow("(function(){({get a(){}, a:1})})");
+shouldNotThrow("(function(){({set a(x){}, a:1})})");
+shouldNotThrow("(function(){({get a(){}, get a(){}})})");
+shouldNotThrow("(function(){({set a(x){}, set a(x){}})})");
+shouldNotThrow("(function(){({set a(x){}, get a(){}, set a(x){}})})");
+
 shouldBeTrue("({a:1, a:1, a:1}), true");
 shouldBeTrue("({get a(){}, set a(x){}}), true");
 shouldBeTrue("({set a(x){}, get a(){}}), true");
@@ -25,6 +26,7 @@ shouldNotThrow("({get a(){}})");
 shouldNotThrow("({set a(x){}})");
 shouldNotThrow("({set a([x, y]){}})");
 shouldNotThrow("({set a({x, y}){}})");
+
 shouldThrow("({get a(x){}})");
 shouldThrow("({b:1, get a(x){}})");
 shouldThrow("({get a([x]){}})");
index 61e5e61..d3ec7af 100644 (file)
@@ -1,3 +1,95 @@
+2015-05-13  Joseph Pecoraro  <pecoraro@apple.com>
+
+        ES6: Allow duplicate property names
+        https://bugs.webkit.org/show_bug.cgi?id=142895
+
+        Reviewed by Geoffrey Garen.
+
+        Introduce new `op_put_getter_by_id` and `op_put_setter_by_id` opcodes
+        that will define a single getter or setter property on an object.
+
+        The existing `op_put_getter_setter` opcode is still preferred for
+        putting both a getter and setter at the same time but cannot be used
+        for putting an individual getter or setter which is needed in
+        some cases.
+
+        Add a new slow path when generating bytecodes for a property list
+        with computed properties, as computed properties are the only time
+        the list of properties cannot be determined statically.
+
+        * bytecompiler/NodesCodegen.cpp:
+        (JSC::PropertyListNode::emitBytecode):
+        - fast path for all constant properties
+        - slow but paired getter/setter path if there are no computed properties
+        - slow path, individual put operation for every property, if there are computed properties
+
+        * parser/Nodes.h:
+        Distinguish a Computed property from a Constant property.
+
+        * parser/Parser.cpp:
+        (JSC::Parser<LexerType>::parseProperty):
+        (JSC::Parser<LexerType>::parsePropertyMethod):
+        Distingish Computed and Constant properties.
+
+        (JSC::Parser<LexerType>::parseObjectLiteral):
+        When we drop into strict mode it is because we saw a getter
+        or setter, so be more explicit.
+
+        (JSC::Parser<LexerType>::parseStrictObjectLiteral):
+        Eliminate duplicate property syntax error exception.
+
+        * parser/SyntaxChecker.h:
+        (JSC::SyntaxChecker::getName):
+        * parser/ASTBuilder.h:
+        (JSC::ASTBuilder::getName): Deleted.
+        No longer used.
+
+        * runtime/JSObject.h:
+        (JSC::JSObject::putDirectInternal):
+        When updating a property. If the Accessor attribute changed
+        update the Structure.
+
+        * runtime/JSObject.cpp:
+        (JSC::JSObject::putGetter):
+        (JSC::JSObject::putSetter):
+        Called by the opcodes, just perform the same operation that
+        __defineGetter__ or __defineSetter__ would do.
+
+        (JSC::JSObject::putDirectNonIndexAccessor):
+        This transition is now handled in putDirectInternal.
+
+        * runtime/Structure.h:
+        Add needed export.
+
+        * bytecode/BytecodeList.json:
+        * bytecode/BytecodeUseDef.h:
+        (JSC::computeUsesForBytecodeOffset):
+        (JSC::computeDefsForBytecodeOffset):
+        * bytecode/CodeBlock.cpp:
+        (JSC::CodeBlock::dumpBytecode):
+        * bytecompiler/BytecodeGenerator.cpp:
+        (JSC::BytecodeGenerator::emitPutGetterById):
+        (JSC::BytecodeGenerator::emitPutSetterById):
+        * bytecompiler/BytecodeGenerator.h:
+        * jit/JIT.cpp:
+        (JSC::JIT::privateCompileMainPass):
+        * jit/JIT.h:
+        * jit/JITInlines.h:
+        (JSC::JIT::callOperation):
+        * jit/JITOperations.cpp:
+        * jit/JITOperations.h:
+        * jit/JITPropertyAccess.cpp:
+        (JSC::JIT::emit_op_put_getter_by_id):
+        (JSC::JIT::emit_op_put_setter_by_id):
+        * jit/JITPropertyAccess32_64.cpp:
+        (JSC::JIT::emit_op_put_getter_by_id):
+        (JSC::JIT::emit_op_put_setter_by_id):
+        * llint/LLIntSlowPaths.cpp:
+        (JSC::LLInt::LLINT_SLOW_PATH_DECL):
+        * llint/LLIntSlowPaths.h:
+        * llint/LowLevelInterpreter.asm:
+        New bytecodes. Modelled after existing op_put_getter_setter.
+
 2015-05-13  Filip Pizlo  <fpizlo@apple.com>
 
         Creating a new blank document in icloud pages causes an AI error: Abstract value (CellBytecodedoubleBoolOther, TOP, TOP) for double node has type outside SpecFullDouble.
index e3ed1dc..be7236b 100644 (file)
@@ -74,6 +74,8 @@
             { "name" : "op_put_by_val_direct", "length" : 5 },
             { "name" : "op_del_by_val", "length" : 4 },
             { "name" : "op_put_by_index", "length" : 4 },
+            { "name" : "op_put_getter_by_id", "length" : 4 },
+            { "name" : "op_put_setter_by_id", "length" : 4 },
             { "name" : "op_put_getter_setter", "length" : 5 },
             { "name" : "op_jmp", "length" : 2 },
             { "name" : "op_jtrue", "length" : 3 },
index 7667e68..507f483 100644 (file)
@@ -99,6 +99,8 @@ void computeUsesForBytecodeOffset(
     case op_put_by_id_transition_normal_out_of_line:
     case op_put_by_id_out_of_line:
     case op_put_by_id:
+    case op_put_getter_by_id:
+    case op_put_setter_by_id:
     case op_put_to_scope:
     case op_put_to_arguments: {
         functor(codeBlock, instruction, opcodeID, instruction[1].u.operand);
@@ -272,6 +274,8 @@ void computeDefsForBytecodeOffset(CodeBlock* codeBlock, unsigned bytecodeOffset,
     case op_put_by_id_transition_direct_out_of_line:
     case op_put_by_id_transition_normal:
     case op_put_by_id_transition_normal_out_of_line:
+    case op_put_getter_by_id:
+    case op_put_setter_by_id:
     case op_put_getter_setter:
     case op_put_by_val:
     case op_put_by_val_direct:
index 7801a56..bf6956f 100644 (file)
@@ -1110,6 +1110,22 @@ void CodeBlock::dumpBytecode(
             printPutByIdCacheStatus(out, exec, location, stubInfos);
             break;
         }
+        case op_put_getter_by_id: {
+            int r0 = (++it)->u.operand;
+            int id0 = (++it)->u.operand;
+            int r1 = (++it)->u.operand;
+            printLocationAndOp(out, exec, location, it, "put_getter_by_id");
+            out.printf("%s, %s, %s", registerName(r0).data(), idName(id0, identifier(id0)).data(), registerName(r1).data());
+            break;
+        }
+        case op_put_setter_by_id: {
+            int r0 = (++it)->u.operand;
+            int id0 = (++it)->u.operand;
+            int r1 = (++it)->u.operand;
+            printLocationAndOp(out, exec, location, it, "put_setter_by_id");
+            out.printf("%s, %s, %s", registerName(r0).data(), idName(id0, identifier(id0)).data(), registerName(r1).data());
+            break;
+        }
         case op_put_getter_setter: {
             int r0 = (++it)->u.operand;
             int id0 = (++it)->u.operand;
index c3ce875..5ff461e 100644 (file)
@@ -1548,6 +1548,28 @@ RegisterID* BytecodeGenerator::emitDirectPutById(RegisterID* base, const Identif
     return value;
 }
 
+void BytecodeGenerator::emitPutGetterById(RegisterID* base, const Identifier& property, RegisterID* getter)
+{
+    unsigned propertyIndex = addConstant(property);
+    m_staticPropertyAnalyzer.putById(base->index(), propertyIndex);
+
+    emitOpcode(op_put_getter_by_id);
+    instructions().append(base->index());
+    instructions().append(propertyIndex);
+    instructions().append(getter->index());
+}
+
+void BytecodeGenerator::emitPutSetterById(RegisterID* base, const Identifier& property, RegisterID* setter)
+{
+    unsigned propertyIndex = addConstant(property);
+    m_staticPropertyAnalyzer.putById(base->index(), propertyIndex);
+
+    emitOpcode(op_put_setter_by_id);
+    instructions().append(base->index());
+    instructions().append(propertyIndex);
+    instructions().append(setter->index());
+}
+
 void BytecodeGenerator::emitPutGetterSetter(RegisterID* base, const Identifier& property, RegisterID* getter, RegisterID* setter)
 {
     unsigned propertyIndex = addConstant(property);
index b815632..85dfb9e 100644 (file)
@@ -482,6 +482,9 @@ namespace JSC {
         RegisterID* emitDirectPutByVal(RegisterID* base, RegisterID* property, RegisterID* value);
         RegisterID* emitDeleteByVal(RegisterID* dst, RegisterID* base, RegisterID* property);
         RegisterID* emitPutByIndex(RegisterID* base, unsigned index, RegisterID* value);
+
+        void emitPutGetterById(RegisterID* base, const Identifier& property, RegisterID* getter);
+        void emitPutSetterById(RegisterID* base, const Identifier& property, RegisterID* setter);
         void emitPutGetterSetter(RegisterID* base, const Identifier& property, RegisterID* getter, RegisterID* setter);
         
         ExpectedFunction expectedFunctionForIdentifier(const Identifier&);
index 0db374d..ff210c3 100644 (file)
@@ -377,11 +377,15 @@ RegisterID* PropertyListNode::emitBytecode(BytecodeGenerator& generator, Registe
 {
     // Fast case: this loop just handles regular value properties.
     PropertyListNode* p = this;
-    for (; p && p->m_node->m_type == PropertyNode::Constant; p = p->m_next)
+    for (; p && (p->m_node->m_type & PropertyNode::Constant); p = p->m_next)
         emitPutConstantProperty(generator, dst, *p->m_node);
 
     // Were there any get/set properties?
     if (p) {
+        // Build a list of getter/setter pairs to try to put them at the same time. If we encounter
+        // a computed property, just emit everything as that may override previous values.
+        bool hasComputedProperty = false;
+
         typedef std::pair<PropertyNode*, PropertyNode*> GetterSetterPair;
         typedef HashMap<StringImpl*, GetterSetterPair> GetterSetterMap;
         GetterSetterMap map;
@@ -389,13 +393,22 @@ RegisterID* PropertyListNode::emitBytecode(BytecodeGenerator& generator, Registe
         // Build a map, pairing get/set values together.
         for (PropertyListNode* q = p; q; q = q->m_next) {
             PropertyNode* node = q->m_node;
-            if (node->m_type == PropertyNode::Constant)
+            if (node->m_type & PropertyNode::Computed) {
+                hasComputedProperty = true;
+                break;
+            }
+            if (node->m_type & PropertyNode::Constant)
                 continue;
 
-            GetterSetterPair pair(node, static_cast<PropertyNode*>(0));
+            // Duplicates are possible.
+            GetterSetterPair pair(node, static_cast<PropertyNode*>(nullptr));
             GetterSetterMap::AddResult result = map.add(node->name()->impl(), pair);
-            if (!result.isNewEntry)
-                result.iterator->value.second = node;
+            if (!result.isNewEntry) {
+                if (result.iterator->value.first->m_type == node->m_type)
+                    result.iterator->value.first = node;
+                else
+                    result.iterator->value.second = node;
+            }
         }
 
         // Iterate over the remaining properties in the list.
@@ -403,7 +416,7 @@ RegisterID* PropertyListNode::emitBytecode(BytecodeGenerator& generator, Registe
             PropertyNode* node = p->m_node;
 
             // Handle regular values.
-            if (node->m_type == PropertyNode::Constant) {
+            if (node->m_type & PropertyNode::Constant) {
                 emitPutConstantProperty(generator, dst, *node);
                 continue;
             }
@@ -413,8 +426,18 @@ RegisterID* PropertyListNode::emitBytecode(BytecodeGenerator& generator, Registe
             if (isClassProperty)
                 emitPutHomeObject(generator, value, dst);
 
-            // This is a get/set property, find its entry in the map.
-            ASSERT(node->m_type == PropertyNode::Getter || node->m_type == PropertyNode::Setter);
+            ASSERT(node->m_type & (PropertyNode::Getter | PropertyNode::Setter));
+
+            // This is a get/set property which may be overridden by a computed property later.
+            if (hasComputedProperty) {
+                if (node->m_type & PropertyNode::Getter)
+                    generator.emitPutGetterById(dst, *node->name(), value);
+                else
+                    generator.emitPutSetterById(dst, *node->name(), value);
+                continue;
+            }
+
+            // This is a get/set property pair.
             GetterSetterMap::iterator it = map.find(node->name()->impl());
             ASSERT(it != map.end());
             GetterSetterPair& pair = it->value;
@@ -422,16 +445,16 @@ RegisterID* PropertyListNode::emitBytecode(BytecodeGenerator& generator, Registe
             // Was this already generated as a part of its partner?
             if (pair.second == node)
                 continue;
-    
+
             // Generate the paired node now.
             RefPtr<RegisterID> getterReg;
             RefPtr<RegisterID> setterReg;
             RegisterID* secondReg = nullptr;
 
-            if (node->m_type == PropertyNode::Getter) {
+            if (node->m_type & PropertyNode::Getter) {
                 getterReg = value;
                 if (pair.second) {
-                    ASSERT(pair.second->m_type == PropertyNode::Setter);
+                    ASSERT(pair.second->m_type & PropertyNode::Setter);
                     setterReg = generator.emitNode(pair.second->m_assign);
                     secondReg = setterReg.get();
                 } else {
@@ -439,10 +462,10 @@ RegisterID* PropertyListNode::emitBytecode(BytecodeGenerator& generator, Registe
                     generator.emitLoad(setterReg.get(), jsUndefined());
                 }
             } else {
-                ASSERT(node->m_type == PropertyNode::Setter);
+                ASSERT(node->m_type & PropertyNode::Setter);
                 setterReg = value;
                 if (pair.second) {
-                    ASSERT(pair.second->m_type == PropertyNode::Getter);
+                    ASSERT(pair.second->m_type & PropertyNode::Getter);
                     getterReg = generator.emitNode(pair.second->m_assign);
                     secondReg = getterReg.get();
                 } else {
index fd344ad..255e38a 100644 (file)
@@ -302,6 +302,15 @@ public:
         addCallArgument(arg3);
     }
 
+    ALWAYS_INLINE void setupArgumentsWithExecState(GPRReg arg1, TrustedImmPtr arg2, GPRReg arg3)
+    {
+        resetCallArguments();
+        addCallArgument(GPRInfo::callFrameRegister);
+        addCallArgument(arg1);
+        addCallArgument(arg2);
+        addCallArgument(arg3);
+    }
+
     ALWAYS_INLINE void setupArgumentsWithExecState(GPRReg arg1, TrustedImm32 arg2, TrustedImmPtr arg3)
     {
         resetCallArguments();
index b4113c4..e51c75a 100644 (file)
@@ -282,6 +282,8 @@ void JIT::privateCompileMainPass()
         DEFINE_OP(op_put_by_index)
         case op_put_by_val_direct:
         DEFINE_OP(op_put_by_val)
+        DEFINE_OP(op_put_getter_by_id)
+        DEFINE_OP(op_put_setter_by_id)
         DEFINE_OP(op_put_getter_setter)
         case op_init_global_const_nop:
             NEXT_OPCODE(op_init_global_const_nop);
index d7225ea..97c807e 100644 (file)
@@ -537,6 +537,8 @@ namespace JSC {
         void emit_op_put_by_id(Instruction*);
         void emit_op_put_by_index(Instruction*);
         void emit_op_put_by_val(Instruction*);
+        void emit_op_put_getter_by_id(Instruction*);
+        void emit_op_put_setter_by_id(Instruction*);
         void emit_op_put_getter_setter(Instruction*);
         void emit_op_init_global_const(Instruction*);
         void emit_op_ret(Instruction*);
@@ -722,6 +724,7 @@ namespace JSC {
         MacroAssembler::Call callOperation(V_JITOperation_E);
         MacroAssembler::Call callOperation(V_JITOperation_EC, RegisterID);
         MacroAssembler::Call callOperation(V_JITOperation_ECC, RegisterID, RegisterID);
+        MacroAssembler::Call callOperation(V_JITOperation_ECIC, RegisterID, const Identifier*, RegisterID);
         MacroAssembler::Call callOperation(V_JITOperation_ECICC, RegisterID, const Identifier*, RegisterID, RegisterID);
         MacroAssembler::Call callOperation(J_JITOperation_EE, RegisterID);
         MacroAssembler::Call callOperation(V_JITOperation_EZSymtabJ, int, SymbolTable*, RegisterID);
@@ -731,6 +734,7 @@ namespace JSC {
 #else
         MacroAssembler::Call callOperationNoExceptionCheck(V_JITOperation_EJ, RegisterID, RegisterID);
 #endif
+        MacroAssembler::Call callOperation(V_JITOperation_EJIdJ, RegisterID, const Identifier*, RegisterID);
         MacroAssembler::Call callOperation(V_JITOperation_EJIdJJ, RegisterID, const Identifier*, RegisterID, RegisterID);
 #if USE(JSVALUE64)
         MacroAssembler::Call callOperation(F_JITOperation_EFJZZ, RegisterID, RegisterID, int32_t, RegisterID);
index 9ee24d0..54603b6 100644 (file)
@@ -478,6 +478,12 @@ ALWAYS_INLINE MacroAssembler::Call JIT::callOperation(V_JITOperation_EJ operatio
     return appendCallWithExceptionCheck(operation);
 }
 
+ALWAYS_INLINE MacroAssembler::Call JIT::callOperation(V_JITOperation_EJIdJ operation, RegisterID regOp1, const Identifier* identOp2, RegisterID regOp3)
+{
+    setupArgumentsWithExecState(regOp1, TrustedImmPtr(identOp2), regOp3);
+    return appendCallWithExceptionCheck(operation);
+}
+
 ALWAYS_INLINE MacroAssembler::Call JIT::callOperation(V_JITOperation_EJIdJJ operation, RegisterID regOp1, const Identifier* identOp2, RegisterID regOp3, RegisterID regOp4)
 {
     setupArgumentsWithExecState(regOp1, TrustedImmPtr(identOp2), regOp3, regOp4);
@@ -588,6 +594,12 @@ ALWAYS_INLINE MacroAssembler::Call JIT::callOperation(S_JITOperation_EJJ operati
     return appendCallWithExceptionCheck(operation);
 }
 
+ALWAYS_INLINE MacroAssembler::Call JIT::callOperation(V_JITOperation_ECIC operation, RegisterID regOp1, const Identifier* identOp2, RegisterID regOp3)
+{
+    setupArgumentsWithExecState(regOp1, TrustedImmPtr(identOp2), regOp3);
+    return appendCallWithExceptionCheck(operation);
+}
+
 ALWAYS_INLINE MacroAssembler::Call JIT::callOperation(V_JITOperation_ECICC operation, RegisterID regOp1, const Identifier* identOp2, RegisterID regOp3, RegisterID regOp4)
 {
     setupArgumentsWithExecState(regOp1, TrustedImmPtr(identOp2), regOp3, regOp4);
index 98d8773..e0aa30c 100644 (file)
@@ -1279,6 +1279,32 @@ void JIT_OPERATION operationPutByIndex(ExecState* exec, EncodedJSValue encodedAr
 }
 
 #if USE(JSVALUE64)
+void JIT_OPERATION operationPutGetterById(ExecState* exec, EncodedJSValue encodedObjectValue, Identifier* identifier, EncodedJSValue encodedGetterValue)
+{
+    VM& vm = exec->vm();
+    NativeCallFrameTracer tracer(&vm, exec);
+
+    ASSERT(JSValue::decode(encodedObjectValue).isObject());
+    JSObject* baseObj = asObject(JSValue::decode(encodedObjectValue));
+
+    JSValue getter = JSValue::decode(encodedGetterValue);
+    ASSERT(getter.isObject());
+    baseObj->putGetter(exec, *identifier, asObject(getter));
+}
+
+void JIT_OPERATION operationPutSetterById(ExecState* exec, EncodedJSValue encodedObjectValue, Identifier* identifier, EncodedJSValue encodedSetterValue)
+{
+    VM& vm = exec->vm();
+    NativeCallFrameTracer tracer(&vm, exec);
+
+    ASSERT(JSValue::decode(encodedObjectValue).isObject());
+    JSObject* baseObj = asObject(JSValue::decode(encodedObjectValue));
+
+    JSValue setter = JSValue::decode(encodedSetterValue);
+    ASSERT(setter.isObject());
+    baseObj->putSetter(exec, *identifier, asObject(setter));
+}
+
 void JIT_OPERATION operationPutGetterSetter(ExecState* exec, EncodedJSValue encodedObjectValue, Identifier* identifier, EncodedJSValue encodedGetterValue, EncodedJSValue encodedSetterValue)
 {
     VM& vm = exec->vm();
@@ -1302,6 +1328,30 @@ void JIT_OPERATION operationPutGetterSetter(ExecState* exec, EncodedJSValue enco
     baseObj->putDirectAccessor(exec, *identifier, accessor, Accessor);
 }
 #else
+void JIT_OPERATION operationPutGetterById(ExecState* exec, JSCell* object, Identifier* identifier, JSCell* getter)
+{
+    VM& vm = exec->vm();
+    NativeCallFrameTracer tracer(&vm, exec);
+
+    ASSERT(object && object->isObject());
+    JSObject* baseObj = object->getObject();
+
+    ASSERT(getter->isObject());
+    baseObj->putGetter(exec, *identifier, getter);
+}
+
+void JIT_OPERATION operationPutSetterById(ExecState* exec, JSCell* object, Identifier* identifier, JSCell* setter)
+{
+    VM& vm = exec->vm();
+    NativeCallFrameTracer tracer(&vm, exec);
+
+    ASSERT(object && object->isObject());
+    JSObject* baseObj = object->getObject();
+
+    ASSERT(setter->isObject());
+    baseObj->putSetter(exec, *identifier, setter);
+}
+
 void JIT_OPERATION operationPutGetterSetter(ExecState* exec, JSCell* object, Identifier* identifier, JSCell* getter, JSCell* setter)
 {
     VM& vm = exec->vm();
index 4c81d85..161e8df 100644 (file)
@@ -176,6 +176,7 @@ typedef void JIT_OPERATION (*V_JITOperation_EC)(ExecState*, JSCell*);
 typedef void JIT_OPERATION (*V_JITOperation_ECb)(ExecState*, CodeBlock*);
 typedef void JIT_OPERATION (*V_JITOperation_ECC)(ExecState*, JSCell*, JSCell*);
 typedef void JIT_OPERATION (*V_JITOperation_ECIcf)(ExecState*, JSCell*, InlineCallFrame*);
+typedef void JIT_OPERATION (*V_JITOperation_ECIC)(ExecState*, JSCell*, Identifier*, JSCell*);
 typedef void JIT_OPERATION (*V_JITOperation_ECICC)(ExecState*, JSCell*, Identifier*, JSCell*, JSCell*);
 typedef void JIT_OPERATION (*V_JITOperation_ECCIcf)(ExecState*, JSCell*, JSCell*, InlineCallFrame*);
 typedef void JIT_OPERATION (*V_JITOperation_ECJJ)(ExecState*, JSCell*, EncodedJSValue, EncodedJSValue);
@@ -185,6 +186,7 @@ typedef void JIT_OPERATION (*V_JITOperation_ECC)(ExecState*, JSCell*, JSCell*);
 typedef void JIT_OPERATION (*V_JITOperation_EZSymtabJ)(ExecState*, int32_t, SymbolTable*, EncodedJSValue);
 typedef void JIT_OPERATION (*V_JITOperation_EJ)(ExecState*, EncodedJSValue);
 typedef void JIT_OPERATION (*V_JITOperation_EJCI)(ExecState*, EncodedJSValue, JSCell*, StringImpl*);
+typedef void JIT_OPERATION (*V_JITOperation_EJIdJ)(ExecState*, EncodedJSValue, Identifier*, EncodedJSValue);
 typedef void JIT_OPERATION (*V_JITOperation_EJIdJJ)(ExecState*, EncodedJSValue, Identifier*, EncodedJSValue, EncodedJSValue);
 typedef void JIT_OPERATION (*V_JITOperation_EJJJ)(ExecState*, EncodedJSValue, EncodedJSValue, EncodedJSValue);
 typedef void JIT_OPERATION (*V_JITOperation_EJPP)(ExecState*, EncodedJSValue, void*, void*);
@@ -294,8 +296,12 @@ SlowPathReturnType JIT_OPERATION operationOptimize(ExecState*, int32_t) WTF_INTE
 #endif
 void JIT_OPERATION operationPutByIndex(ExecState*, EncodedJSValue, int32_t, EncodedJSValue);
 #if USE(JSVALUE64)
+void JIT_OPERATION operationPutGetterById(ExecState*, EncodedJSValue, Identifier*, EncodedJSValue) WTF_INTERNAL;
+void JIT_OPERATION operationPutSetterById(ExecState*, EncodedJSValue, Identifier*, EncodedJSValue) WTF_INTERNAL;
 void JIT_OPERATION operationPutGetterSetter(ExecState*, EncodedJSValue, Identifier*, EncodedJSValue, EncodedJSValue) WTF_INTERNAL;
 #else
+void JIT_OPERATION operationPutGetterById(ExecState*, JSCell*, Identifier*, JSCell*) WTF_INTERNAL;
+void JIT_OPERATION operationPutSetterById(ExecState*, JSCell*, Identifier*, JSCell*) WTF_INTERNAL;
 void JIT_OPERATION operationPutGetterSetter(ExecState*, JSCell*, Identifier*, JSCell*, JSCell*) WTF_INTERNAL;
 #endif
 void JIT_OPERATION operationPushCatchScope(ExecState*, int32_t, SymbolTable*, EncodedJSValue) WTF_INTERNAL;
index 16abc59..3823c17 100644 (file)
@@ -444,6 +444,20 @@ void JIT::emit_op_put_by_index(Instruction* currentInstruction)
     callOperation(operationPutByIndex, regT0, currentInstruction[2].u.operand, regT1);
 }
 
+void JIT::emit_op_put_getter_by_id(Instruction* currentInstruction)
+{
+    emitGetVirtualRegister(currentInstruction[1].u.operand, regT0);
+    emitGetVirtualRegister(currentInstruction[3].u.operand, regT1);
+    callOperation(operationPutGetterById, regT0, &m_codeBlock->identifier(currentInstruction[2].u.operand), regT1);
+}
+
+void JIT::emit_op_put_setter_by_id(Instruction* currentInstruction)
+{
+    emitGetVirtualRegister(currentInstruction[1].u.operand, regT0);
+    emitGetVirtualRegister(currentInstruction[3].u.operand, regT1);
+    callOperation(operationPutSetterById, regT0, &m_codeBlock->identifier(currentInstruction[2].u.operand), regT1);
+}
+
 void JIT::emit_op_put_getter_setter(Instruction* currentInstruction)
 {
     emitGetVirtualRegister(currentInstruction[1].u.operand, regT0);
index 251a689..0a11bba 100644 (file)
@@ -57,6 +57,28 @@ void JIT::emit_op_put_by_index(Instruction* currentInstruction)
     callOperation(operationPutByIndex, regT1, regT0, property, regT3, regT2);
 }
 
+void JIT::emit_op_put_getter_by_id(Instruction* currentInstruction)
+{
+    int base = currentInstruction[1].u.operand;
+    int property = currentInstruction[2].u.operand;
+    int getter = currentInstruction[3].u.operand;
+
+    emitLoadPayload(base, regT1);
+    emitLoadPayload(getter, regT3);
+    callOperation(operationPutGetterById, regT1, &m_codeBlock->identifier(property), regT3);
+}
+
+void JIT::emit_op_put_setter_by_id(Instruction* currentInstruction)
+{
+    int base = currentInstruction[1].u.operand;
+    int property = currentInstruction[2].u.operand;
+    int setter = currentInstruction[3].u.operand;
+
+    emitLoadPayload(base, regT1);
+    emitLoadPayload(setter, regT3);
+    callOperation(operationPutSetterById, regT1, &m_codeBlock->identifier(property), regT3);
+}
+
 void JIT::emit_op_put_getter_setter(Instruction* currentInstruction)
 {
     int base = currentInstruction[1].u.operand;
index 20bebf3..f5865c0 100644 (file)
@@ -862,6 +862,32 @@ LLINT_SLOW_PATH_DECL(slow_path_put_by_index)
     LLINT_END();
 }
 
+LLINT_SLOW_PATH_DECL(slow_path_put_getter_by_id)
+{
+    LLINT_BEGIN();
+    ASSERT(LLINT_OP(1).jsValue().isObject());
+    JSObject* baseObj = asObject(LLINT_OP(1).jsValue());
+    
+    JSValue getter = LLINT_OP(3).jsValue();
+    ASSERT(getter.isObject());
+    
+    baseObj->putGetter(exec, exec->codeBlock()->identifier(pc[2].u.operand), asObject(getter));
+    LLINT_END();
+}
+
+LLINT_SLOW_PATH_DECL(slow_path_put_setter_by_id)
+{
+    LLINT_BEGIN();
+    ASSERT(LLINT_OP(1).jsValue().isObject());
+    JSObject* baseObj = asObject(LLINT_OP(1).jsValue());
+    
+    JSValue setter = LLINT_OP(3).jsValue();
+    ASSERT(setter.isObject());
+    
+    baseObj->putSetter(exec, exec->codeBlock()->identifier(pc[2].u.operand), asObject(setter));
+    LLINT_END();
+}
+
 LLINT_SLOW_PATH_DECL(slow_path_put_getter_setter)
 {
     LLINT_BEGIN();
index 4bf7deb..f8f3864 100644 (file)
@@ -80,6 +80,8 @@ LLINT_SLOW_PATH_HIDDEN_DECL(slow_path_put_by_val);
 LLINT_SLOW_PATH_HIDDEN_DECL(slow_path_put_by_val_direct);
 LLINT_SLOW_PATH_HIDDEN_DECL(slow_path_del_by_val);
 LLINT_SLOW_PATH_HIDDEN_DECL(slow_path_put_by_index);
+LLINT_SLOW_PATH_HIDDEN_DECL(slow_path_put_getter_by_id);
+LLINT_SLOW_PATH_HIDDEN_DECL(slow_path_put_setter_by_id);
 LLINT_SLOW_PATH_HIDDEN_DECL(slow_path_put_getter_setter);
 LLINT_SLOW_PATH_HIDDEN_DECL(slow_path_jtrue);
 LLINT_SLOW_PATH_HIDDEN_DECL(slow_path_jfalse);
index 543d9e9..c3764ba 100644 (file)
@@ -1057,6 +1057,18 @@ _llint_op_put_by_index:
     dispatch(4)
 
 
+_llint_op_put_getter_by_id:
+    traceExecution()
+    callSlowPath(_llint_slow_path_put_getter_by_id)
+    dispatch(4)
+
+
+_llint_op_put_setter_by_id:
+    traceExecution()
+    callSlowPath(_llint_slow_path_put_setter_by_id)
+    dispatch(4)
+
+
 _llint_op_put_getter_setter:
     traceExecution()
     callSlowPath(_llint_slow_path_put_getter_setter)
index ef319a1..c494536 100644 (file)
@@ -713,7 +713,6 @@ public:
         return result;
     }
     
-    const Identifier* getName(Property property) const { return property->name(); }
     PropertyNode::Type getType(Property property) const { return property->type(); }
 
     bool isResolve(ExpressionNode* expr) const { return expr->isResolveNode(); }
index d2cf2ee..3a19005 100644 (file)
@@ -564,7 +564,7 @@ namespace JSC {
 
     class PropertyNode : public ParserArenaFreeable {
     public:
-        enum Type { Constant = 1, Getter = 2, Setter = 4 };
+        enum Type { Constant = 1, Getter = 2, Setter = 4, Computed = 8 };
         enum PutType { Unknown, KnownDirect };
 
         PropertyNode(const Identifier&, ExpressionNode*, Type, PutType, SuperBinding);
@@ -582,7 +582,7 @@ namespace JSC {
         const Identifier* m_name;
         ExpressionNode* m_expression;
         ExpressionNode* m_assign;
-        unsigned m_type : 3;
+        unsigned m_type : 4;
         unsigned m_needsSuperBinding : 1;
         unsigned m_putType : 1;
     };
index fd42445..95e988d 100644 (file)
@@ -2066,14 +2066,14 @@ template <class TreeBuilder> TreeProperty Parser<LexerType>::parseProperty(TreeB
         if (match(OPENPAREN)) {
             auto method = parsePropertyMethod(context, &m_vm->propertyNames->nullIdentifier);
             propagateError();
-            return context.createProperty(propertyName, method, PropertyNode::Constant, PropertyNode::KnownDirect, complete);
+            return context.createProperty(propertyName, method, static_cast<PropertyNode::Type>(PropertyNode::Constant | PropertyNode::Computed), PropertyNode::KnownDirect, complete);
         }
 
         consumeOrFail(COLON, "Expected ':' after property name");
         TreeExpression node = parseAssignmentExpression(context);
         failIfFalse(node, "Cannot parse expression for property declaration");
         context.setEndOffset(node, m_lexer->currentOffset());
-        return context.createProperty(propertyName, node, PropertyNode::Constant, PropertyNode::Unknown, complete);
+        return context.createProperty(propertyName, node, static_cast<PropertyNode::Type>(PropertyNode::Constant | PropertyNode::Computed), PropertyNode::Unknown, complete);
     }
     default:
         failIfFalse(m_token.m_type & KeywordTokenFlag, "Expected a property name");
@@ -2112,7 +2112,7 @@ template <class TreeBuilder> TreeProperty Parser<LexerType>::parseGetterSetter(T
     JSTokenLocation location(tokenLocation());
     next();
     ParserFunctionInfo<TreeBuilder> info;
-    if (type == PropertyNode::Getter) {
+    if (type & PropertyNode::Getter) {
         failIfFalse(match(OPENPAREN), "Expected a parameter list for getter definition");
         failIfFalse((parseFunctionInfo(context, FunctionNoRequirements, GetterMode, false, constructorKind, superBinding,
             getterOrSetterStartOffset, info)), "Cannot parse getter definition");
@@ -2142,7 +2142,7 @@ template <class TreeBuilder> TreeExpression Parser<LexerType>::parseObjectLitera
     
     TreeProperty property = parseProperty(context, false);
     failIfFalse(property, "Cannot parse object literal property");
-    if (!m_syntaxAlreadyValidated && context.getType(property) != PropertyNode::Constant) {
+    if (!m_syntaxAlreadyValidated && context.getType(property) & (PropertyNode::Getter | PropertyNode::Setter)) {
         restoreSavePoint(savePoint);
         return parseStrictObjectLiteral(context);
     }
@@ -2150,13 +2150,12 @@ template <class TreeBuilder> TreeExpression Parser<LexerType>::parseObjectLitera
     TreePropertyList tail = propertyList;
     while (match(COMMA)) {
         next(TreeBuilder::DontBuildStrings);
-        // allow extra comma, see http://bugs.webkit.org/show_bug.cgi?id=5939
         if (match(CLOSEBRACE))
             break;
         JSTokenLocation propertyLocation(tokenLocation());
         property = parseProperty(context, false);
         failIfFalse(property, "Cannot parse object literal property");
-        if (!m_syntaxAlreadyValidated && context.getType(property) != PropertyNode::Constant) {
+        if (!m_syntaxAlreadyValidated && context.getType(property) & (PropertyNode::Getter | PropertyNode::Setter)) {
             restoreSavePoint(savePoint);
             return parseStrictObjectLiteral(context);
         }
@@ -2187,31 +2186,15 @@ template <class TreeBuilder> TreeExpression Parser<LexerType>::parseStrictObject
     TreeProperty property = parseProperty(context, true);
     failIfFalse(property, "Cannot parse object literal property");
     
-    typedef HashMap<RefPtr<StringImpl>, unsigned, IdentifierRepHash> ObjectValidationMap;
-    ObjectValidationMap objectValidator;
-    // Add the first property
-    if (!m_syntaxAlreadyValidated && context.getName(property))
-        objectValidator.add(context.getName(property)->impl(), context.getType(property));
-    
     TreePropertyList propertyList = context.createPropertyList(location, property);
     TreePropertyList tail = propertyList;
     while (match(COMMA)) {
         next();
-        // allow extra comma, see http://bugs.webkit.org/show_bug.cgi?id=5939
         if (match(CLOSEBRACE))
             break;
         JSTokenLocation propertyLocation(tokenLocation());
         property = parseProperty(context, true);
         failIfFalse(property, "Cannot parse object literal property");
-        if (!m_syntaxAlreadyValidated && context.getName(property)) {
-            ObjectValidationMap::AddResult propertyEntry = objectValidator.add(context.getName(property)->impl(), context.getType(property));
-            if (!propertyEntry.isNewEntry) {
-                semanticFailIfTrue(propertyEntry.iterator->value == PropertyNode::Constant, "Attempted to redefine property '", propertyEntry.iterator->key.get(), "'");
-                semanticFailIfTrue(context.getType(property) == PropertyNode::Constant, "Attempted to redefine property '", propertyEntry.iterator->key.get(), "'");
-                semanticFailIfTrue(context.getType(property) & propertyEntry.iterator->value, "Attempted to redefine property '", propertyEntry.iterator->key.get(), "'");
-                propertyEntry.iterator->value |= context.getType(property);
-            }
-        }
         tail = context.createPropertyList(propertyLocation, property, tail);
     }
 
index 554e5df..f045397 100644 (file)
@@ -290,7 +290,6 @@ public:
     
     void assignmentStackAppend(int, int, int, int, int, Operator) { }
     int createAssignment(const JSTokenLocation&, int, int, int, int, int) { RELEASE_ASSERT_NOT_REACHED(); return AssignmentExpr; }
-    const Identifier* getName(const Property& property) const { return property.name; }
     PropertyNode::Type getType(const Property& property) const { return property.type; }
     bool isResolve(ExpressionType expr) const { return expr == ResolveExpr || expr == ResolveEvalExpr; }
     ExpressionType createDeconstructingAssignment(const JSTokenLocation&, int, ExpressionType)
index 537011d..d677024 100644 (file)
@@ -1197,6 +1197,24 @@ bool JSObject::allowsAccessFrom(ExecState* exec)
     return globalObject->globalObjectMethodTable()->allowsAccessFrom(globalObject, exec);
 }
 
+void JSObject::putGetter(ExecState* exec, PropertyName propertyName, JSValue getter)
+{
+    PropertyDescriptor descriptor;
+    descriptor.setGetter(getter);
+    descriptor.setEnumerable(true);
+    descriptor.setConfigurable(true);
+    defineOwnProperty(this, exec, propertyName, descriptor, false);
+}
+
+void JSObject::putSetter(ExecState* exec, PropertyName propertyName, JSValue setter)
+{
+    PropertyDescriptor descriptor;
+    descriptor.setSetter(setter);
+    descriptor.setEnumerable(true);
+    descriptor.setConfigurable(true);
+    defineOwnProperty(this, exec, propertyName, descriptor, false);
+}
+
 void JSObject::putDirectAccessor(ExecState* exec, PropertyName propertyName, JSValue value, unsigned attributes)
 {
     ASSERT(value.isGetterSetter() && (attributes & Accessor));
@@ -1229,12 +1247,6 @@ void JSObject::putDirectNonIndexAccessor(VM& vm, PropertyName propertyName, JSVa
     PutPropertySlot slot(this);
     putDirectInternal<PutModeDefineOwnProperty>(vm, propertyName, value, attributes, slot);
 
-    // putDirect will change our Structure if we add a new property. For
-    // getters and setters, though, we also need to change our Structure
-    // if we override an existing non-getter or non-setter.
-    if (slot.type() != PutPropertySlot::NewProperty)
-        setStructure(vm, Structure::attributeChangeTransition(vm, structure(vm), propertyName, attributes));
-
     Structure* structure = this->structure(vm);
     if (attributes & ReadOnly)
         structure->setContainsReadOnlyProperties();
index 445bb52..ef15367 100644 (file)
@@ -467,6 +467,9 @@ public:
     void putDirectAccessor(ExecState*, PropertyName, JSValue, unsigned attributes);
     JS_EXPORT_PRIVATE void putDirectCustomAccessor(VM&, PropertyName, JSValue, unsigned attributes);
 
+    void putGetter(ExecState*, PropertyName, JSValue);
+    void putSetter(ExecState*, PropertyName, JSValue);
+
     JS_EXPORT_PRIVATE bool hasProperty(ExecState*, PropertyName) const;
     JS_EXPORT_PRIVATE bool hasProperty(ExecState*, unsigned propertyName) const;
     bool hasOwnProperty(ExecState*, PropertyName) const;
@@ -1318,8 +1321,12 @@ inline bool JSObject::putDirectInternal(VM& vm, PropertyName propertyName, JSVal
 
             putDirect(vm, offset, value);
             structure->didReplaceProperty(offset);
-            
             slot.setExistingProperty(this, offset);
+
+            if ((attributes & Accessor) != (currentAttributes & Accessor)) {
+                ASSERT(!(attributes & ReadOnly));
+                setStructure(vm, Structure::attributeChangeTransition(vm, structure, propertyName, attributes));
+            }
             return true;
         }
 
@@ -1369,6 +1376,11 @@ inline bool JSObject::putDirectInternal(VM& vm, PropertyName propertyName, JSVal
         structure->didReplaceProperty(offset);
         slot.setExistingProperty(this, offset);
         putDirect(vm, offset, value);
+
+        if ((attributes & Accessor) != (currentAttributes & Accessor)) {
+            ASSERT(!(attributes & ReadOnly));
+            setStructure(vm, Structure::attributeChangeTransition(vm, structure, propertyName, attributes));
+        }
         return true;
     }
 
index 1b44e2a..165f6ba 100644 (file)
@@ -137,7 +137,7 @@ public:
     JS_EXPORT_PRIVATE static Structure* addPropertyTransitionToExistingStructure(Structure*, PropertyName, unsigned attributes, PropertyOffset&);
     static Structure* removePropertyTransition(VM&, Structure*, PropertyName, PropertyOffset&);
     JS_EXPORT_PRIVATE static Structure* changePrototypeTransition(VM&, Structure*, JSValue prototype);
-    static Structure* attributeChangeTransition(VM&, Structure*, PropertyName, unsigned attributes);
+    JS_EXPORT_PRIVATE static Structure* attributeChangeTransition(VM&, Structure*, PropertyName, unsigned attributes);
     JS_EXPORT_PRIVATE static Structure* toCacheableDictionaryTransition(VM&, Structure*);
     static Structure* toUncacheableDictionaryTransition(VM&, Structure*);
     JS_EXPORT_PRIVATE static Structure* sealTransition(VM&, Structure*);